diff options
1253 files changed, 19218 insertions, 7260 deletions
@@ -32,3 +32,6 @@ /.vs/ # Visual Studio build directory /out/ + +# clang-tidy output +/clang-tidy-fixes.patch diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b8bb4ab..b2c90eb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -112,6 +112,7 @@ l:tidy-fedora39: extends: - .fedora39_tidy - .cmake_build_linux + - .cmake_tidy_artifacts - .linux_x86_64_tags - .cmake_cdash_artifacts - .run_automatically @@ -754,6 +755,13 @@ t:intel2021.9.0-makefiles: CMAKE_CI_BUILD_NAME: intel2021.9.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8 +t:intel2021.10.0-makefiles: + extends: + - .cmake_test_linux_intelclassic_makefiles + variables: + CMAKE_CI_BUILD_NAME: intel2021.10.0_makefiles + CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8 + t:oneapi2021.1.1-makefiles: extends: - .cmake_test_linux_inteloneapi_makefiles @@ -824,6 +832,20 @@ t:oneapi2023.1.0-makefiles: CMAKE_CI_BUILD_NAME: oneapi2023.1.0_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8 +t:oneapi2023.2.0-makefiles: + extends: + - .cmake_test_linux_inteloneapi_makefiles + variables: + CMAKE_CI_BUILD_NAME: oneapi2023.2.1_makefiles + CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8 + +t:oneapi2024.0.0-makefiles: + extends: + - .cmake_test_linux_inteloneapi_makefiles + variables: + CMAKE_CI_BUILD_NAME: oneapi2024.0.0_makefiles + CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2024.0.0-el8 + b:linux-x86_64-package: extends: - .linux_package diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore index 19796ef..b28fe32 100644 --- a/.gitlab/.gitignore +++ b/.gitlab/.gitignore @@ -19,3 +19,5 @@ /unstable-jom* /watcom /wix* +/clang-tidy-fixes +/num_warnings.txt diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 2326bcf..b2315be 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -119,6 +119,8 @@ # Take the sphinx logs. - ${CMAKE_CI_BUILD_DIR}/build-*.log - ${CMAKE_CI_BUILD_DIR}/linkcheck/output.* + # Take the HTML output. + - ${CMAKE_CI_BUILD_DIR}/html/ .cmake_test_artifacts: artifacts: @@ -151,3 +153,10 @@ - ${CMAKE_CI_BUILD_DIR}/html exclude: - ${CMAKE_CI_BUILD_DIR}/html/.buildinfo + +.cmake_tidy_artifacts: + artifacts: + expire_in: 1d + when: always + paths: + - clang-tidy-fixes.patch diff --git a/.gitlab/ci/configure_fedora39_tidy.cmake b/.gitlab/ci/configure_fedora39_tidy.cmake index 4ed1eb3..7a3eaa6 100644 --- a/.gitlab/ci/configure_fedora39_tidy.cmake +++ b/.gitlab/ci/configure_fedora39_tidy.cmake @@ -1,5 +1,6 @@ set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "") set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "") set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "") +set(CMake_CLANG_TIDY_EXPORT_FIXES_DIR "$ENV{CI_PROJECT_DIR}/.gitlab/clang-tidy-fixes" CACHE PATH "") include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake") diff --git a/.gitlab/ci/ctest_build.cmake b/.gitlab/ci/ctest_build.cmake index b1b9830..4a18cf9 100644 --- a/.gitlab/ci/ctest_build.cmake +++ b/.gitlab/ci/ctest_build.cmake @@ -65,6 +65,7 @@ if ("$ENV{CTEST_NO_WARNINGS_ALLOWED}" AND num_warnings GREATER 0) message(FATAL_ERROR "Found ${num_warnings} warnings (treating as fatal).") endif () +file(WRITE "$ENV{CI_PROJECT_DIR}/.gitlab/num_warnings.txt" "${num_warnings}\n") if (ctest_build_args) message(FATAL_ERROR diff --git a/.gitlab/ci/post_build_fedora39_tidy.sh b/.gitlab/ci/post_build_fedora39_tidy.sh new file mode 100644 index 0000000..a36663a --- /dev/null +++ b/.gitlab/ci/post_build_fedora39_tidy.sh @@ -0,0 +1,21 @@ +git config user.name "Kitware Robot" +git config user.email "kwrobot@kitware.com" + +clang-apply-replacements --style=file .gitlab/clang-tidy-fixes +git add . + +if [ -n "$(git status --porcelain)" ]; then + quietly git commit --file=- <<EOF +WIP: clang-tidy: <SHORT DESCRIPTION OF CHANGE HERE> + +<LONGER DESCRIPTION OF CHANGE HERE.> +EOF + git format-patch --output=clang-tidy-fixes.patch -1 -N + echo "Patch from clang-tidy available, check artifacts of this CI job." >&2 +fi + +readonly num_warnings="$(cat .gitlab/num_warnings.txt)" +if [ "$num_warnings" -ne 0 ]; then + echo "Found $num_warnings warnings (treating as fatal)." >&2 + exit 1 +fi diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index f529ab2..e0364a5 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -82,7 +82,6 @@ variables: CMAKE_CONFIGURATION: fedora39_tidy - CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_NO_INSTALL: 1 .fedora39_clang_analyzer: diff --git a/Auxiliary/vim/indent/cmake.vim b/Auxiliary/vim/indent/cmake.vim index 0c662fa..28ecf84 100644 --- a/Auxiliary/vim/indent/cmake.vim +++ b/Auxiliary/vim/indent/cmake.vim @@ -17,6 +17,8 @@ let b:did_indent = 1 setlocal indentexpr=CMakeGetIndent(v:lnum) setlocal indentkeys+==ENDIF(,ENDFOREACH(,ENDMACRO(,ELSE(,ELSEIF(,ENDWHILE( +let b:undo_indent = "setl inde< indk<" + " Only define the function once. if exists("*CMakeGetIndent") finish diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 4bbdc65..d2cbdc9 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -19,10 +19,10 @@ endif let s:keepcpo= &cpo set cpo&vim -syn region cmakeBracketArgument start="\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell +syn region cmakeBracketArgument start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell -syn region cmakeComment start="#" end="$" contains=cmakeTodo,@Spell -syn region cmakeBracketComment start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell +syn region cmakeComment start="#\(\[=*\[\)\@!" end="$" contains=cmakeTodo,@Spell +syn region cmakeBracketComment start="#\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell syn match cmakeEscaped /\(\\\\\|\\"\|\\n\|\\t\)/ contained syn region cmakeRegistry start="\[" end="]" contained oneline contains=cmakeTodo,cmakeEscaped @@ -70,6 +70,7 @@ syn keyword cmakeProperty contained \ ATTACHED_FILES \ ATTACHED_FILES_ON_FAIL \ AUTOGEN_BUILD_DIR + \ AUTOGEN_COMMAND_LINE_LENGTH_MAX \ AUTOGEN_ORIGIN_DEPENDS \ AUTOGEN_PARALLEL \ AUTOGEN_SOURCE_GROUP @@ -374,6 +375,7 @@ syn keyword cmakeProperty contained \ Swift_LANGUAGE_VERSION \ Swift_MODULE_DIRECTORY \ Swift_MODULE_NAME + \ Swift_COMPILATION_MODE \ TARGET_ARCHIVES_MAY_BE_SHARED_LIBS \ TARGET_MESSAGES \ TARGET_SUPPORTS_SHARED_LIBS @@ -764,6 +766,7 @@ syn keyword cmakeVariable contained \ CMAKE_ASM_STANDARD_REQUIRED \ CMAKE_ASM_SUPPORTED \ CMAKE_ASM_VISIBILITY_PRESET + \ CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX \ CMAKE_AUTOGEN_ORIGIN_DEPENDS \ CMAKE_AUTOGEN_PARALLEL \ CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE @@ -1656,6 +1659,7 @@ syn keyword cmakeVariable contained \ CMAKE_SKIP_INSTALL_RPATH \ CMAKE_SKIP_INSTALL_RULES \ CMAKE_SKIP_RPATH + \ CMAKE_SKIP_TEST_ALL_DEPENDENCY \ CMAKE_SOURCE_DIR \ CMAKE_STAGING_PREFIX \ CMAKE_STATIC_LIBRARY_PREFIX @@ -2760,7 +2764,6 @@ syn keyword cmakeKWcmake_language contained \ DIRECTORY \ EVAL \ FALSE - \ FETCHCONTENT_MAKEAVAILABE_SERIAL \ FETCHCONTENT_MAKEAVAILABLE_SERIAL \ FETCHCONTENT_SOURCE_DIR_ \ FETCHCONTENT_TRY_FIND_PACKAGE_MODE diff --git a/CMakeLists.txt b/CMakeLists.txt index 2823ca4..a99cf50 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.26 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.27 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/Copyright.txt b/Copyright.txt index 515e403..2074109 100644 --- a/Copyright.txt +++ b/Copyright.txt @@ -1,5 +1,5 @@ CMake - Cross Platform Makefile Generator -Copyright 2000-2023 Kitware, Inc. and Contributors +Copyright 2000-2024 Kitware, Inc. and Contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 5fe4326..2bb1390 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -373,6 +373,11 @@ The options are: :manual:`generator expressions <cmake-generator-expressions(7)>` was also added. + .. versionadded:: 3.29 + The :ref:`Ninja Generators` will now incorporate the dependencies into its + "deps log" database if the file is not listed in ``OUTPUTS`` or + ``BYPRODUCTS``. + Using ``DEPFILE`` with generators other than those listed above is an error. If the ``DEPFILE`` argument is relative, it should be relative to diff --git a/Help/command/add_executable.rst b/Help/command/add_executable.rst index d9ea0da..b6833b4 100644 --- a/Help/command/add_executable.rst +++ b/Help/command/add_executable.rst @@ -10,15 +10,28 @@ Add an executable to the project using the specified source files. Normal Executables ^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake +.. signature:: + add_executable(<name> <options>... <sources>...) + :target: normal - add_executable(<name> [WIN32] [MACOSX_BUNDLE] - [EXCLUDE_FROM_ALL] - [source1] [source2 ...]) + Add an executable target called ``<name>`` to be built from the source + files listed in the command invocation. -Adds an executable target called ``<name>`` to be built from the source -files listed in the command invocation. The -``<name>`` corresponds to the logical target name and must be globally + The options are: + + ``WIN32`` + Set the :prop_tgt:`WIN32_EXECUTABLE` target property automatically. + See documentation of that target property for details. + + ``MACOSX_BUNDLE`` + Set the :prop_tgt:`MACOSX_BUNDLE` target property automatically. + See documentation of that target property for details. + + ``EXCLUDE_FROM_ALL`` + Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically. + See documentation of that target property for details. + +The ``<name>`` corresponds to the logical target name and must be globally unique within a project. The actual file name of the executable built is constructed based on conventions of the native platform (such as ``<name>.exe`` or just ``<name>``). @@ -39,18 +52,6 @@ command was invoked. See documentation of the location. See documentation of the :prop_tgt:`OUTPUT_NAME` target property to change the ``<name>`` part of the final file name. -If ``WIN32`` is given the property :prop_tgt:`WIN32_EXECUTABLE` will be -set on the target created. See documentation of that target property for -details. - -If ``MACOSX_BUNDLE`` is given the corresponding property will be set on -the created target. See documentation of the :prop_tgt:`MACOSX_BUNDLE` -target property for details. - -If ``EXCLUDE_FROM_ALL`` is given the corresponding property will be set on -the created target. See documentation of the :prop_tgt:`EXCLUDE_FROM_ALL` -target property for details. - See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. @@ -61,17 +62,25 @@ within IDE. Imported Executables ^^^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - +.. signature:: add_executable(<name> IMPORTED [GLOBAL]) + :target: IMPORTED + + Add an :ref:`IMPORTED executable target <Imported Targets>` to reference + an executable file located outside the project. The target name may be + referenced like any target built within the project, except that by + default it is visible only in the directory in which it is created, + and below. + + The options are: + + ``GLOBAL`` + Make the target name globally visible. + +No rules are generated to build imported targets, and the :prop_tgt:`IMPORTED` +target property is ``True``. Imported executables are useful for convenient +reference from commands like :command:`add_custom_command`. -An :ref:`IMPORTED executable target <Imported Targets>` references an -executable file located outside the project. No rules are generated to -build it, and the :prop_tgt:`IMPORTED` target property is ``True``. The -target name has scope in the directory in which it is created and below, but -the ``GLOBAL`` option extends visibility. It may be referenced like any -target built within the project. ``IMPORTED`` executables are useful -for convenient reference from commands like :command:`add_custom_command`. Details about the imported executable are specified by setting properties whose names begin in ``IMPORTED_``. The most important such property is :prop_tgt:`IMPORTED_LOCATION` (and its per-configuration version @@ -82,14 +91,14 @@ properties for more information. Alias Executables ^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - +.. signature:: add_executable(<name> ALIAS <target>) + :target: ALIAS -Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can -be used to refer to ``<target>`` in subsequent commands. The ``<name>`` -does not appear in the generated buildsystem as a make target. The -``<target>`` may not be an ``ALIAS``. + Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can + be used to refer to ``<target>`` in subsequent commands. The ``<name>`` + does not appear in the generated buildsystem as a make target. The + ``<target>`` may not be an ``ALIAS``. .. versionadded:: 3.11 An ``ALIAS`` can target a ``GLOBAL`` :ref:`Imported Target <Imported Targets>` diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index 07c8bab..5b22cb1 100644 --- a/Help/command/add_library.rst +++ b/Help/command/add_library.rst @@ -10,18 +10,39 @@ Add a library to the project using the specified source files. Normal Libraries ^^^^^^^^^^^^^^^^ -.. code-block:: cmake +.. signature:: + add_library(<name> [<type>] [EXCLUDE_FROM_ALL] <sources>...) + :target: normal + + Add a library target called ``<name>`` to be built from the source files + listed in the command invocation. + + The optional ``<type>`` specifies the type of library to be created: + + ``STATIC`` + An archive of object files for use when linking other targets. + + ``SHARED`` + A dynamic library that may be linked by other targets and loaded + at runtime. + + ``MODULE`` + A plugin that may not be linked by other targets, but may be + dynamically loaded at runtime using dlopen-like functionality. + + If no ``<type>`` is given the default is ``STATIC`` or ``SHARED`` + based on the value of the :variable:`BUILD_SHARED_LIBS` variable. - add_library(<name> [STATIC | SHARED | MODULE] - [EXCLUDE_FROM_ALL] - [<source>...]) + The options are: -Adds a library target called ``<name>`` to be built from the source files -listed in the command invocation. The ``<name>`` -corresponds to the logical target name and must be globally unique within -a project. The actual file name of the library built is constructed based -on conventions of the native platform (such as ``lib<name>.a`` or -``<name>.lib``). + ``EXCLUDE_FROM_ALL`` + Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically. + See documentation of that target property for details. + +The ``<name>`` corresponds to the logical target name and must be globally +unique within a project. The actual file name of the library built is +constructed based on conventions of the native platform (such as +``lib<name>.a`` or ``<name>.lib``). .. versionadded:: 3.1 Source arguments to ``add_library`` may use "generator expressions" with @@ -32,15 +53,8 @@ on conventions of the native platform (such as ``lib<name>.a`` or The source files can be omitted if they are added later using :command:`target_sources`. -``STATIC``, ``SHARED``, or ``MODULE`` may be given to specify the type of -library to be created. ``STATIC`` libraries are archives of object files -for use when linking other targets. ``SHARED`` libraries are linked -dynamically and loaded at runtime. ``MODULE`` libraries are plugins that -are not linked into other targets but may be loaded dynamically at runtime -using dlopen-like functionality. If no type is given explicitly the -type is ``STATIC`` or ``SHARED`` based on whether the current value of the -variable :variable:`BUILD_SHARED_LIBS` is ``ON``. For ``SHARED`` and -``MODULE`` libraries the :prop_tgt:`POSITION_INDEPENDENT_CODE` target +For ``SHARED`` and ``MODULE`` libraries the +:prop_tgt:`POSITION_INDEPENDENT_CODE` target property is set to ``ON`` automatically. A ``SHARED`` library may be marked with the :prop_tgt:`FRAMEWORK` target property to create an macOS Framework. @@ -63,10 +77,6 @@ invoked. See documentation of the :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY`, location. See documentation of the :prop_tgt:`OUTPUT_NAME` target property to change the ``<name>`` part of the final file name. -If ``EXCLUDE_FROM_ALL`` is given the corresponding property will be set on -the created target. See documentation of the :prop_tgt:`EXCLUDE_FROM_ALL` -target property for details. - See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. @@ -77,14 +87,15 @@ within IDE. Object Libraries ^^^^^^^^^^^^^^^^ -.. code-block:: cmake +.. signature:: + add_library(<name> OBJECT <sources>...) + :target: OBJECT - add_library(<name> OBJECT [<source>...]) + Add an :ref:`Object Library <Object Libraries>` to compile source files + without archiving or linking their object files into a library. -Creates an :ref:`Object Library <Object Libraries>`. An object library -compiles source files but does not archive or link their object files into a -library. Instead other targets created by ``add_library`` or -:command:`add_executable` may reference the objects using an expression of the +Other targets created by ``add_library`` or :command:`add_executable` +may reference the objects using an expression of the form :genex:`$\<TARGET_OBJECTS:objlib\> <TARGET_OBJECTS>` as a source, where ``objlib`` is the object library name. For example: @@ -109,46 +120,48 @@ consider adding at least one real source file to any target that references Interface Libraries ^^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - +.. signature:: add_library(<name> INTERFACE) - -Creates an :ref:`Interface Library <Interface Libraries>`. -An ``INTERFACE`` library target does not compile sources and does -not produce a library artifact on disk. However, it may have -properties set on it and it may be installed and exported. -Typically, ``INTERFACE_*`` properties are populated on an interface -target using the commands: - -* :command:`set_property`, -* :command:`target_link_libraries(INTERFACE)`, -* :command:`target_link_options(INTERFACE)`, -* :command:`target_include_directories(INTERFACE)`, -* :command:`target_compile_options(INTERFACE)`, -* :command:`target_compile_definitions(INTERFACE)`, and -* :command:`target_sources(INTERFACE)`, - -and then it is used as an argument to :command:`target_link_libraries` -like any other target. - -An interface library created with the above signature has no source files -itself and is not included as a target in the generated buildsystem. - -.. versionadded:: 3.15 - An interface library can have :prop_tgt:`PUBLIC_HEADER` and - :prop_tgt:`PRIVATE_HEADER` properties. The headers specified by those - properties can be installed using the :command:`install(TARGETS)` command. - -.. versionadded:: 3.19 - An interface library target may be created with source files: - - .. code-block:: cmake - - add_library(<name> INTERFACE [<source>...] [EXCLUDE_FROM_ALL]) - - Source files may be listed directly in the ``add_library`` call or added - later by calls to :command:`target_sources` with the ``PRIVATE`` or - ``PUBLIC`` keywords. + :target: INTERFACE + + Add an :ref:`Interface Library <Interface Libraries>` target that may + specify usage requirements for dependents but does not compile sources + and does not produce a library artifact on disk. + + An interface library with no source files is not included as a target + in the generated buildsystem. However, it may have + properties set on it and it may be installed and exported. + Typically, ``INTERFACE_*`` properties are populated on an interface + target using the commands: + + * :command:`set_property`, + * :command:`target_link_libraries(INTERFACE)`, + * :command:`target_link_options(INTERFACE)`, + * :command:`target_include_directories(INTERFACE)`, + * :command:`target_compile_options(INTERFACE)`, + * :command:`target_compile_definitions(INTERFACE)`, and + * :command:`target_sources(INTERFACE)`, + + and then it is used as an argument to :command:`target_link_libraries` + like any other target. + + .. versionadded:: 3.15 + An interface library can have :prop_tgt:`PUBLIC_HEADER` and + :prop_tgt:`PRIVATE_HEADER` properties. The headers specified by those + properties can be installed using the :command:`install(TARGETS)` command. + +.. signature:: + add_library(<name> INTERFACE [EXCLUDE_FROM_ALL] <sources>...) + :target: INTERFACE-with-sources + + .. versionadded:: 3.19 + + Add an :ref:`Interface Library <Interface Libraries>` target with + source files (in addition to usage requirements and properties as + documented by the :command:`above signature <add_library(INTERFACE)>`). + Source files may be listed directly in the ``add_library`` call + or added later by calls to :command:`target_sources` with the + ``PRIVATE`` or ``PUBLIC`` keywords. If an interface library has source files (i.e. the :prop_tgt:`SOURCES` target property is set), or header sets (i.e. the :prop_tgt:`HEADER_SETS` @@ -158,92 +171,106 @@ itself and is not included as a target in the generated buildsystem. but does contain build rules for custom commands created by the :command:`add_custom_command` command. -.. note:: - In most command signatures where the ``INTERFACE`` keyword appears, - the items listed after it only become part of that target's usage - requirements and are not part of the target's own settings. However, - in this signature of ``add_library``, the ``INTERFACE`` keyword refers - to the library type only. Sources listed after it in the ``add_library`` - call are ``PRIVATE`` to the interface library and do not appear in its - :prop_tgt:`INTERFACE_SOURCES` target property. + The options are: + + ``EXCLUDE_FROM_ALL`` + Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically. + See documentation of that target property for details. + + .. note:: + In most command signatures where the ``INTERFACE`` keyword appears, + the items listed after it only become part of that target's usage + requirements and are not part of the target's own settings. However, + in this signature of ``add_library``, the ``INTERFACE`` keyword refers + to the library type only. Sources listed after it in the ``add_library`` + call are ``PRIVATE`` to the interface library and do not appear in its + :prop_tgt:`INTERFACE_SOURCES` target property. .. _`add_library imported libraries`: Imported Libraries ^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - +.. signature:: add_library(<name> <type> IMPORTED [GLOBAL]) - -Creates an :ref:`IMPORTED library target <Imported Targets>` called ``<name>``. -No rules are generated to build it, and the :prop_tgt:`IMPORTED` target -property is ``True``. The target name has scope in the directory in which -it is created and below, but the ``GLOBAL`` option extends visibility. -It may be referenced like any target built within the project. -``IMPORTED`` libraries are useful for convenient reference from commands -like :command:`target_link_libraries`. Details about the imported library -are specified by setting properties whose names begin in ``IMPORTED_`` and -``INTERFACE_``. - -The ``<type>`` must be one of: - -``STATIC``, ``SHARED``, ``MODULE``, ``UNKNOWN`` - References a library file located outside the project. The - :prop_tgt:`IMPORTED_LOCATION` target property (or its per-configuration - variant :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`) specifies the - location of the main library file on disk: - - * For a ``SHARED`` library on most non-Windows platforms, the main library - file is the ``.so`` or ``.dylib`` file used by both linkers and dynamic - loaders. If the referenced library file has a ``SONAME`` (or on macOS, - has a ``LC_ID_DYLIB`` starting in ``@rpath/``), the value of that field - should be set in the :prop_tgt:`IMPORTED_SONAME` target property. - If the referenced library file does not have a ``SONAME``, but the - platform supports it, then the :prop_tgt:`IMPORTED_NO_SONAME` target - property should be set. - - * For a ``SHARED`` library on Windows, the :prop_tgt:`IMPORTED_IMPLIB` - target property (or its per-configuration variant - :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>`) specifies the location of the - DLL import library file (``.lib`` or ``.dll.a``) on disk, and the - ``IMPORTED_LOCATION`` is the location of the ``.dll`` runtime - library (and is optional, but needed by the :genex:`TARGET_RUNTIME_DLLS` - generator expression). - - Additional usage requirements may be specified in ``INTERFACE_*`` properties. - - An ``UNKNOWN`` library type is typically only used in the implementation of - :ref:`Find Modules`. It allows the path to an imported library (often found - using the :command:`find_library` command) to be used without having to know - what type of library it is. This is especially useful on Windows where a - static library and a DLL's import library both have the same file extension. - -``OBJECT`` - References a set of object files located outside the project. - The :prop_tgt:`IMPORTED_OBJECTS` target property (or its per-configuration - variant :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>`) specifies the locations of - object files on disk. - Additional usage requirements may be specified in ``INTERFACE_*`` properties. - -``INTERFACE`` - Does not reference any library or object files on disk, but may - specify usage requirements in ``INTERFACE_*`` properties. - -See documentation of the ``IMPORTED_*`` and ``INTERFACE_*`` properties -for more information. + :target: IMPORTED + + Add an :ref:`IMPORTED library target <Imported Targets>` called ``<name>``. + The target name may be referenced like any target built within the project, + except that by default it is visible only in the directory in which it is + created, and below. + + The ``<type>`` must be one of: + + ``STATIC``, ``SHARED``, ``MODULE``, ``UNKNOWN`` + References a library file located outside the project. The + :prop_tgt:`IMPORTED_LOCATION` target property (or its per-configuration + variant :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`) specifies the + location of the main library file on disk: + + * For a ``SHARED`` library on most non-Windows platforms, the main library + file is the ``.so`` or ``.dylib`` file used by both linkers and dynamic + loaders. If the referenced library file has a ``SONAME`` (or on macOS, + has a ``LC_ID_DYLIB`` starting in ``@rpath/``), the value of that field + should be set in the :prop_tgt:`IMPORTED_SONAME` target property. + If the referenced library file does not have a ``SONAME``, but the + platform supports it, then the :prop_tgt:`IMPORTED_NO_SONAME` target + property should be set. + + * For a ``SHARED`` library on Windows, the :prop_tgt:`IMPORTED_IMPLIB` + target property (or its per-configuration variant + :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>`) specifies the location of the + DLL import library file (``.lib`` or ``.dll.a``) on disk, and the + ``IMPORTED_LOCATION`` is the location of the ``.dll`` runtime + library (and is optional, but needed by the :genex:`TARGET_RUNTIME_DLLS` + generator expression). + + Additional usage requirements may be specified in ``INTERFACE_*`` + properties. + + An ``UNKNOWN`` library type is typically only used in the implementation + of :ref:`Find Modules`. It allows the path to an imported library + (often found using the :command:`find_library` command) to be used + without having to know what type of library it is. This is especially + useful on Windows where a static library and a DLL's import library + both have the same file extension. + + ``OBJECT`` + References a set of object files located outside the project. + The :prop_tgt:`IMPORTED_OBJECTS` target property (or its per-configuration + variant :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>`) specifies the locations of + object files on disk. + Additional usage requirements may be specified in ``INTERFACE_*`` + properties. + + ``INTERFACE`` + Does not reference any library or object files on disk, but may + specify usage requirements in ``INTERFACE_*`` properties. + + The options are: + + ``GLOBAL`` + Make the target name globally visible. + +No rules are generated to build imported targets, and the :prop_tgt:`IMPORTED` +target property is ``True``. Imported libraries are useful for convenient +reference from commands like :command:`target_link_libraries`. + +Details about the imported library are specified by setting properties whose +names begin in ``IMPORTED_`` and ``INTERFACE_``. See documentation of +such properties for more information. Alias Libraries ^^^^^^^^^^^^^^^ -.. code-block:: cmake - +.. signature:: add_library(<name> ALIAS <target>) + :target: ALIAS -Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can be -used to refer to ``<target>`` in subsequent commands. The ``<name>`` does -not appear in the generated buildsystem as a make target. The ``<target>`` -may not be an ``ALIAS``. + Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can be + used to refer to ``<target>`` in subsequent commands. The ``<name>`` does + not appear in the generated buildsystem as a make target. The ``<target>`` + may not be an ``ALIAS``. .. versionadded:: 3.11 An ``ALIAS`` can target a ``GLOBAL`` :ref:`Imported Target <Imported Targets>` diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index 02dd3986..557c858 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst @@ -27,9 +27,37 @@ 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. + + * .. versionadded:: 3.3 + + The target's :prop_tgt:`CROSSCOMPILING_EMULATOR`, if set, will be + used to run the command on the host:: + + <emulator> <command> + + .. versionchanged:: 3.29 + + The emulator is used only when + :variable:`cross-compiling <CMAKE_CROSSCOMPILING>`. + See policy :policy:`CMP0158`. + + * .. versionadded:: 3.29 + + The target's :prop_tgt:`TEST_LAUNCHER`, if set, will be + used to launch the command:: + + <launcher> <command> + + If the :prop_tgt:`CROSSCOMPILING_EMULATOR` is also set, both are used:: + + <launcher> <emulator> <command> The command may be specified using :manual:`generator expressions <cmake-generator-expressions(7)>`. diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst index dad0833..0d2f75e 100644 --- a/Help/command/cmake_host_system_information.rst +++ b/Help/command/cmake_host_system_information.rst @@ -265,7 +265,7 @@ Example: .. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes. -.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html +.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/latest/os-release.html .. _various distribution-specific files: http://linuxmafia.com/faq/Admin/release-files.html .. _Query Windows registry: diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index 3af6b9c..4b64eb6 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst @@ -317,7 +317,7 @@ be one of the ``<methods>`` that was specified when setting the provider. implementation as part of its processing, it can do so by including the ``BYPASS_PROVIDER`` keyword as one of the arguments. -``FETCHCONTENT_MAKEAVAILABE_SERIAL`` +``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` The ``<method-specific-args>`` will be everything passed to the :command:`FetchContent_Declare` call that corresponds to the requested dependency, with the following exceptions: diff --git a/Help/command/export.rst b/Help/command/export.rst index cc927bc..f190df7 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -15,12 +15,13 @@ Synopsis export(`TARGETS`_ <target>... [...]) export(`EXPORT`_ <export-name> [...]) export(`PACKAGE`_ <PackageName>) + export(`SETUP`_ <export-name> [...]) Exporting Targets ^^^^^^^^^^^^^^^^^ -.. _`export(TARGETS)`: -.. _TARGETS: +.. signature:: + export(TARGETS <target>... [...]) .. code-block:: cmake @@ -62,7 +63,7 @@ The options are: This signature requires all targets to be listed explicitly. If a library target is included in the export, but a target to which it links is not -included, the behavior is unspecified. See the `export(EXPORT)`_ signature +included, the behavior is unspecified. See the :command:`export(EXPORT)` signature to automatically export the same targets from the build tree as :command:`install(EXPORT)` would from an install tree. @@ -102,27 +103,33 @@ that policy is set to OLD for one of the targets. Exporting Targets matching install(EXPORT) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. _`export(EXPORT)`: -.. _EXPORT: +.. signature:: + export(EXPORT <export-name> [...]) .. code-block:: cmake export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>] - [CXX_MODULES_DIRECTORY <directory>]) + [CXX_MODULES_DIRECTORY <directory>] [EXPORT_PACKAGE_DEPENDENCIES]) Creates a file ``<filename>`` that may be included by outside projects to import targets from the current project's build tree. This is the same -as the `export(TARGETS)`_ signature, except that the targets are not +as the :command:`export(TARGETS)` signature, except that the targets are not explicitly listed. Instead, it exports the targets associated with the installation export ``<export-name>``. Target installations may be associated with the export ``<export-name>`` using the ``EXPORT`` option of the :command:`install(TARGETS)` command. +``EXPORT_PACKAGE_DEPENDENCIES`` + .. versionadded:: 3.29 + + Specify that :command:`find_dependency` calls should be exported. See + :command:`install(EXPORT)` for details on how this works. + Exporting Packages ^^^^^^^^^^^^^^^^^^ -.. _`export(PACKAGE)`: -.. _PACKAGE: +.. signature:: + export(PACKAGE <PackageName>) .. code-block:: cmake @@ -149,3 +156,62 @@ registry. outside the source and build trees. Set the :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable to add build directories to the CMake user package registry. + +Configuring Exports +^^^^^^^^^^^^^^^^^^^ + +.. signature:: + export(SETUP <export-name> [...]) + +.. code-block:: cmake + + export(SETUP <export-name> + [PACKAGE_DEPENDENCY <dep> + [ENABLED (<bool-true>|<bool-false>|AUTO)] + [EXTRA_ARGS <args>...] + ] [...] + [TARGET <target> + [XCFRAMEWORK_LOCATION <location>] + ] [...] + ) + +.. versionadded:: 3.29 + +Configure the parameters of an export. The arguments are as follows: + +``PACKAGE_DEPENDENCY <dep>`` + Specify a package dependency to configure. This changes how + :command:`find_dependency` calls are written during + :command:`export(EXPORT)` and :command:`install(EXPORT)`. ``<dep>`` is the + name of a package to export. This argument accepts the following additional + arguments: + + ``ENABLED`` + Manually control whether or not the dependency is exported. This accepts + the following values: + + ``<bool-true>`` + Any value that CMake recognizes as "true". Always export the dependency, + even if no exported targets depend on it. This can be used to manually + add :command:`find_dependency` calls to the export. + + ``<bool-false>`` + Any value that CMake recognizes as "false". Never export the dependency, + even if an exported target depends on it. + + ``AUTO`` + Only export the dependency if an exported target depends on it. + + ``EXTRA_ARGS <args>`` + Specify additional arguments to pass to :command:`find_dependency` after + the ``REQUIRED`` argument. + +``TARGET <target>`` + Specify a target to configure in this export. This argument accepts the + following additional arguments: + + ``XCFRAMEWORK_LOCATION`` + Specify the location of an ``.xcframework`` which contains the library from + this target. If specified, the generated code will check to see if the + ``.xcframework`` exists, and if it does, it will use the ``.xcframework`` + as its imported location instead of the installed library. diff --git a/Help/command/file.rst b/Help/command/file.rst index f9d1a79..957b8b5 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -32,14 +32,14 @@ Synopsis `Writing`_ file({`WRITE`_ | `APPEND`_} <filename> <content>...) - file({`TOUCH`_ | `TOUCH_NOCREATE`_} [<file>...]) + file({`TOUCH`_ | `TOUCH_NOCREATE`_} <file>...) file(`GENERATE`_ OUTPUT <output-file> [...]) file(`CONFIGURE`_ OUTPUT <output-file> CONTENT <content> [...]) `Filesystem`_ - file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] [<globbing-expr>...]) - file(`MAKE_DIRECTORY`_ [<dir>...]) - file({`REMOVE`_ | `REMOVE_RECURSE`_ } [<files>...]) + file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] <globbing-expr>...) + file(`MAKE_DIRECTORY`_ <directories>...) + file({`REMOVE`_ | `REMOVE_RECURSE`_ } <files>...) file(`RENAME`_ <oldname> <newname> [...]) file(`COPY_FILE`_ <oldname> <newname> [...]) file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...]) @@ -80,7 +80,7 @@ Reading (``a`` through ``f``) are in lowercase. .. signature:: - file(STRINGS <filename> <variable> [<options>...]) + file(STRINGS <filename> <variable> <options>...) Parse a list of ASCII strings from ``<filename>`` and store it in ``<variable>``. Binary data in the file are ignored. Carriage return @@ -165,17 +165,17 @@ Reading [RESOLVED_DEPENDENCIES_VAR <deps_var>] [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>] - [EXECUTABLES [<executable_files>...]] - [LIBRARIES [<library_files>...]] - [MODULES [<module_files>...]] - [DIRECTORIES [<directories>...]] + [EXECUTABLES <executable_files>...] + [LIBRARIES <library_files>...] + [MODULES <module_files>...] + [DIRECTORIES <directories>...] [BUNDLE_EXECUTABLE <bundle_executable_file>] - [PRE_INCLUDE_REGEXES [<regexes>...]] - [PRE_EXCLUDE_REGEXES [<regexes>...]] - [POST_INCLUDE_REGEXES [<regexes>...]] - [POST_EXCLUDE_REGEXES [<regexes>...]] - [POST_INCLUDE_FILES [<files>...]] - [POST_EXCLUDE_FILES [<files>...]] + [PRE_INCLUDE_REGEXES <regexes>...] + [PRE_EXCLUDE_REGEXES <regexes>...] + [POST_INCLUDE_REGEXES <regexes>...] + [POST_EXCLUDE_REGEXES <regexes>...] + [POST_INCLUDE_FILES <files>...] + [POST_EXCLUDE_FILES <files>...] ) Please note that this sub-command is not intended to be used in project mode. @@ -210,7 +210,7 @@ Reading of paths that were found for that filename are stored in ``<conflicting_deps_prefix>_<filename>``. - ``EXECUTABLES <executable_files>`` + ``EXECUTABLES <executable_files>...`` List of executable files to read for dependencies. These are executables that are typically created with :command:`add_executable`, but they do not have to be created by CMake. On Apple platforms, the paths to these @@ -218,14 +218,14 @@ Reading resolving the libraries. Specifying any kind of library (``STATIC``, ``MODULE``, or ``SHARED``) here will result in undefined behavior. - ``LIBRARIES <library_files>`` + ``LIBRARIES <library_files>...`` List of library files to read for dependencies. These are libraries that are typically created with :command:`add_library(SHARED)`, but they do not have to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE`` libraries, or executables here will result in undefined behavior. - ``MODULES <module_files>`` + ``MODULES <module_files>...`` List of loadable module files to read for dependencies. These are modules that are typically created with :command:`add_library(MODULE)`, but they do not have to be created by CMake. They are typically used by calling @@ -233,7 +233,7 @@ Reading Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here will result in undefined behavior. - ``DIRECTORIES <directories>`` + ``DIRECTORIES <directories>...`` List of additional directories to search for dependencies. On Linux platforms, these directories are searched if the dependency is not found in any of the other usual paths. If it is found in such a directory, a @@ -256,30 +256,30 @@ Reading The following arguments specify filters for including or excluding libraries to be resolved. See below for a full description of how they work. - ``PRE_INCLUDE_REGEXES <regexes>`` + ``PRE_INCLUDE_REGEXES <regexes>...`` List of pre-include regexes through which to filter the names of not-yet-resolved dependencies. - ``PRE_EXCLUDE_REGEXES <regexes>`` + ``PRE_EXCLUDE_REGEXES <regexes>...`` List of pre-exclude regexes through which to filter the names of not-yet-resolved dependencies. - ``POST_INCLUDE_REGEXES <regexes>`` + ``POST_INCLUDE_REGEXES <regexes>...`` List of post-include regexes through which to filter the names of resolved dependencies. - ``POST_EXCLUDE_REGEXES <regexes>`` + ``POST_EXCLUDE_REGEXES <regexes>...`` List of post-exclude regexes through which to filter the names of resolved dependencies. - ``POST_INCLUDE_FILES <files>`` + ``POST_INCLUDE_FILES <files>...`` .. versionadded:: 3.21 List of post-include filenames through which to filter the names of resolved dependencies. Symlinks are resolved when attempting to match these filenames. - ``POST_EXCLUDE_FILES <files>`` + ``POST_EXCLUDE_FILES <files>...`` .. versionadded:: 3.21 List of post-exclude filenames through which to filter the names of @@ -486,8 +486,8 @@ Writing to update the file only when its content changes. .. signature:: - file(TOUCH [<files>...]) - file(TOUCH_NOCREATE [<files>...]) + file(TOUCH <files>...) + file(TOUCH_NOCREATE <files>...) .. versionadded:: 3.12 @@ -638,10 +638,10 @@ Filesystem .. signature:: file(GLOB <variable> [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] - [<globbing-expressions>...]) + <globbing-expressions>...) file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS] [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] - [<globbing-expressions>...]) + <globbing-expressions>...) Generate a list of files that match the ``<globbing-expressions>`` and store it into the ``<variable>``. Globbing expressions are similar to @@ -703,13 +703,13 @@ Filesystem ============== ====================================================== .. signature:: - file(MAKE_DIRECTORY [<directories>...]) + file(MAKE_DIRECTORY <directories>...) Create the given directories and their parents as needed. .. signature:: - file(REMOVE [<files>...]) - file(REMOVE_RECURSE [<files>...]) + file(REMOVE <files>...) + file(REMOVE_RECURSE <files>...) Remove the given files. The ``REMOVE_RECURSE`` mode will remove the given files and directories, including non-empty directories. No error is emitted @@ -1012,8 +1012,8 @@ Transfer ^^^^^^^^ .. signature:: - file(DOWNLOAD <url> [<file>] [<options>...]) - file(UPLOAD <file> <url> [<options>...]) + file(DOWNLOAD <url> [<file>] <options>...) + file(UPLOAD <file> <url> <options>...) The ``DOWNLOAD`` subcommand downloads the given ``<url>`` to a local ``<file>``. The ``UPLOAD`` mode uploads a local ``<file>`` to a given diff --git a/Help/command/if.rst b/Help/command/if.rst index 5d85a1f..1afbe04 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst @@ -178,6 +178,47 @@ File Operations False if the given path is an empty string. + .. warning:: + To check the readability of a file, use preferably ``if(IS_READABLE)`` + because this test will evolve to check file existence only in a future + release. + +.. signature:: if(IS_READABLE <path-to-file-or-directory>) + + .. versionadded:: 3.29 + + True if the named file or directory is readable. Behavior + is well-defined only for explicit full paths (a leading ``~/`` is not + expanded as a home directory and is considered a relative path). + Resolves symbolic links, i.e. if the named file or directory is a + symbolic link, returns true if the target of the symbolic link is readable. + + False if the given path is an empty string. + +.. signature:: if(IS_WRITABLE <path-to-file-or-directory>) + + .. versionadded:: 3.29 + + True if the named file or directory is writable. Behavior + is well-defined only for explicit full paths (a leading ``~/`` is not + expanded as a home directory and is considered a relative path). + Resolves symbolic links, i.e. if the named file or directory is a + symbolic link, returns true if the target of the symbolic link is writable. + + False if the given path is an empty string. + +.. signature:: if(IS_EXECUTABLE <path-to-file-or-directory>) + + .. versionadded:: 3.29 + + True if the named file or directory is executable. Behavior + is well-defined only for explicit full paths (a leading ``~/`` is not + expanded as a home directory and is considered a relative path). + Resolves symbolic links, i.e. if the named file or directory is a + symbolic link, returns true if the target of the symbolic link is executable. + + False if the given path is an empty string. + .. signature:: if(<file1> IS_NEWER_THAN <file2>) :target: IS_NEWER_THAN diff --git a/Help/command/install.rst b/Help/command/install.rst index b0698dd..0c2a32a 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -784,7 +784,8 @@ Signatures [CXX_MODULES_DIRECTORY <directory>] [EXPORT_LINK_INTERFACE_LIBRARIES] [COMPONENT <component>] - [EXCLUDE_FROM_ALL]) + [EXCLUDE_FROM_ALL] + [EXPORT_PACKAGE_DEPENDENCIES]) install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...]) The ``EXPORT`` form generates and installs a CMake file containing code to @@ -848,6 +849,34 @@ Signatures without this information, none of the C++ modules which are part of the targets in the export set will support being imported in consuming targets. + ``EXPORT_PACKAGE_DEPENDENCIES`` + .. versionadded:: 3.29 + + Specify that :command:`find_dependency` calls should be exported. If this + argument is specified, CMake examines all targets in the export set and + gathers their ``INTERFACE`` link targets. If any such targets either were + found with :command:`find_package` or have the + :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` property set, and such package + dependency was not disabled by passing ``ENABLED OFF`` to + :command:`export(SETUP)`, then a :command:`find_dependency` call is + written with the target's corresponding package name, a ``REQUIRED`` + argument, and any additional arguments specified by the ``EXTRA_ARGS`` + argument of :command:`export(SETUP)`. Any package dependencies that were + manually specified by passing ``ENABLED ON`` to :command:`export(SETUP)` + are also added, even if the exported targets don't depend on any targets + from them. + + The :command:`find_dependency` calls are written in the following order: + + 1. Any package dependencies that were listed in :command:`export(SETUP)` + are written in the order they were first specified, regardless of + whether or not they contain ``INTERFACE`` dependencies of the + exported targets. + 2. Any package dependencies that contain ``INTERFACE`` link dependencies + of the exported targets and that were never specified in + :command:`export(SETUP)` are written in the order they were first + found. + The ``EXPORT`` form is useful to help outside projects use targets built and installed by the current project. For example, the code diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index 0255b4d..f44a4e8 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst @@ -77,6 +77,7 @@ Try Compiling Source Files [COMPILE_DEFINITIONS <defs>...] [LINK_OPTIONS <options>...] [LINK_LIBRARIES <libs>...] + [LINKER_LANGUAGE <lang>] [OUTPUT_VARIABLE <var>] [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]] [<LANG>_STANDARD <std>] @@ -177,6 +178,9 @@ The options for the above signatures are: If this option is specified, any ``-DLINK_LIBRARIES=...`` value given to the ``CMAKE_FLAGS`` option will be ignored. + .. versionadded:: 3.29 + Alias targets to imported libraries are also supported. + ``LINK_OPTIONS <options>...`` .. versionadded:: 3.14 @@ -184,6 +188,14 @@ The options for the above signatures are: set the :prop_tgt:`STATIC_LIBRARY_OPTIONS` target property in the generated project, depending on the :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` variable. +``LINKER_LANGUAGE <lang>``` + .. versionadded:: 3.29 + + Specify the :prop_tgt:`LINKER_LANGUAGE` target property of the generated + project. When using multiple source files with different languages, set + this to the language of the source file containing the program entry point, + e.g., ``main``. + ``LOG_DESCRIPTION <text>`` .. versionadded:: 3.26 diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst index 1b5087d..c466a81 100644 --- a/Help/command/try_run.rst +++ b/Help/command/try_run.rst @@ -67,6 +67,7 @@ The signature above is recommended for clarity. [COMPILE_DEFINITIONS <defs>...] [LINK_OPTIONS <options>...] [LINK_LIBRARIES <libs>...] + [LINKER_LANGUAGE <lang>] [COMPILE_OUTPUT_VARIABLE <var>] [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]] [<LANG>_STANDARD <std>] diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index 705ec9c..23436de 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst @@ -69,28 +69,32 @@ List of CPack DEB generator specific variables: :Mandatory: Yes :Default: ``<CPACK_PACKAGE_FILE_NAME>[-<component>].deb`` - This may be set to ``DEB-DEFAULT`` to allow the CPack DEB generator to generate - package file name by itself in deb format:: + This may be set to: - <PackageName>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb + ``DEB-DEFAULT`` + Tell CPack to automatically generate the package file name in deb format:: - Alternatively provided package file name must end - with either ``.deb`` or ``.ipk`` suffix. + <PackageName>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb - .. versionadded:: 3.10 - ``.ipk`` suffix used by OPKG packaging system. + This setting recommended as the preferred behavior, but for backward + compatibility with the CPack DEB generator in CMake prior to version 3.6, + this is not the default. Without this, duplicate names may occur. + Duplicate files get overwritten and it is up to the packager to set + the variables in a manner that will prevent such errors. - .. note:: + ``<file-name>[.deb]`` + Use the given file name. - Preferred setting of this variable is ``DEB-DEFAULT`` but for backward - compatibility with the CPack DEB generator in CMake prior to version 3.6 this - feature is disabled by default. + .. versionchanged:: 3.29 - .. note:: + The ``.deb`` suffix will be automatically added if the file name does + not end in ``.deb`` or ``.ipk``. Previously the suffix was required. + + ``<file-name>.ipk`` + .. versionadded:: 3.10 - By using non default filenames duplicate names may occur. Duplicate files - get overwritten and it is up to the packager to set the variables in a - manner that will prevent such errors. + Use the given file name. + The ``.ipk`` suffix is used by the OPKG packaging system. .. variable:: CPACK_DEBIAN_PACKAGE_EPOCH diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst index 7b91261..4a2ce5f 100644 --- a/Help/cpack_gen/rpm.rst +++ b/Help/cpack_gen/rpm.rst @@ -84,9 +84,18 @@ List of CPack RPM generator specific variables: :Default: ``<CPACK_PACKAGE_FILE_NAME>[-<component>].rpm`` with spaces replaced by '-' - This may be set to ``RPM-DEFAULT`` to allow ``rpmbuild`` tool to generate package - file name by itself. - Alternatively provided package file name must end with ``.rpm`` suffix. + This may be set to: + + ``RPM-DEFAULT`` + Tell ``rpmbuild`` to automatically generate the package file name. + + ``<file-name>[.rpm]`` + Use the given file name. + + .. versionchanged:: 3.29 + + The ``.rpm`` suffix will be automatically added if missing. + Previously the suffix was required. .. note:: diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst index af01252..cb56c9d 100644 --- a/Help/cpack_gen/wix.rst +++ b/Help/cpack_gen/wix.rst @@ -119,7 +119,8 @@ Windows using WiX. If this variable is set, the specified template will be used to generate the WiX wxs file. This should be used if further customization of the - output is required. + output is required. The template contents will override the effect of most + ``CPACK_WIX_`` variables. If this variable is not set, the default MSI template included with CMake will be used. @@ -337,3 +338,31 @@ Windows using WiX. of the installer. May for example be set to ``x64`` or ``arm64``. When unspecified, CPack will default to ``x64`` or ``x86``. + +.. variable:: CPACK_WIX_INSTALL_SCOPE + + .. versionadded:: 3.29 + + This variable can be optionally set to specify the ``InstallScope`` + of the installer: + + ``perMachine`` (default) + Create an installer that installs for all users and requires + administrative privileges. Start menu entries created by the + installer are visible to all users. + + ``perUser`` + Not yet supported. This is reserved for future use. + + ``NONE`` + Create an installer without any ``InstallScope`` attribute. + + .. deprecated:: 3.29 + + This value is only for compatibility with the inconsistent behavior used + by CPack 3.28 and older. The resulting installer requires administrative + privileges and installs into the system-wide ``ProgramFiles`` directory, + but the start menu entry and uninstaller registration are created only + for the current user. + + See https://wixtoolset.org/docs/v3/xsd/wix/package/ diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst index 81e0e6f..c904673 100644 --- a/Help/dev/maint.rst +++ b/Help/dev/maint.rst @@ -8,6 +8,13 @@ See documentation on `CMake Development`_ for more information. .. contents:: Maintainer Processes: +Governance +========== + +CMake has no formal governance body. Maintainers expect one another to +cooperate constructively and make decisions in good faith. In cases of +disagreement, the chief maintainer retains final authority. + Review a Merge Request ====================== @@ -332,15 +339,16 @@ Commit with a message such as:: away from setting policies to OLD. Update the ``cmake_policy`` version range generated by ``install(EXPORT)`` -in ``cmExportFileGenerator::GeneratePolicyHeaderCode`` to end at the +in ``cmExportFileGenerator::GeneratePolicyHeaderCode`` and +``install_jar_exports`` in ``javaTargets.cmake.in`` to end at the previous release. We use one release back since we now know all the policies added for that version. Commit with a message such as:: export: Increase maximum policy version in exported files to $prev - The files generated by `install(EXPORT)` and `export()` commands - are known to work with policies as of CMake $prev, so enable them - in sufficiently new CMake versions. + The files generated by `install(EXPORT)`, `export()`, and + `install_jar_exports()` commands are known to work with policies + as of CMake $prev, so enable them in sufficiently new CMake versions. Update the ``cmake_minimum_required`` version range in CMake itself: diff --git a/Help/dev/try_compile-linker-language.rst b/Help/dev/try_compile-linker-language.rst new file mode 100644 index 0000000..8482dee --- /dev/null +++ b/Help/dev/try_compile-linker-language.rst @@ -0,0 +1,6 @@ +try_compile-linker-language +--------------------------- + +* The :command:`try_compile` and :command:`try_run` commands gained a + ``LINKER_LANGUAGE`` option to specify the :prop_tgt:`LINKER_LANGUAGE` + target property in the generated test project. diff --git a/Help/envvar/CMAKE_TEST_LAUNCHER.rst b/Help/envvar/CMAKE_TEST_LAUNCHER.rst new file mode 100644 index 0000000..d620ce5 --- /dev/null +++ b/Help/envvar/CMAKE_TEST_LAUNCHER.rst @@ -0,0 +1,11 @@ +CMAKE_TEST_LAUNCHER +------------------- + +.. versionadded:: 3.29 + +.. include:: ENV_VAR.txt + +The default value for the :variable:`CMAKE_TEST_LAUNCHER` variable when there +is no explicit configuration given on the first run while creating a new +build tree. On later runs in an existing build tree the value persists in +the cache as :variable:`CMAKE_TEST_LAUNCHER`. diff --git a/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make b/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make new file mode 100644 index 0000000..a17673a --- /dev/null +++ b/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make @@ -0,0 +1,2 @@ +test: + +ctest -j 8 diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index 55f07b7..5272ab9 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst @@ -56,6 +56,7 @@ Environment Variables that Control the Build /envvar/CMAKE_MSVCIDE_RUN_PATH /envvar/CMAKE_NO_VERBOSE /envvar/CMAKE_OSX_ARCHITECTURES + /envvar/CMAKE_TEST_LAUNCHER /envvar/CMAKE_TOOLCHAIN_FILE /envvar/DESTDIR /envvar/LDFLAGS diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index 88a7bab..c249ee2 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -431,7 +431,7 @@ Version 1 does not exist to avoid confusion with that from { "kind": "codemodel", - "version": { "major": 2, "minor": 6 }, + "version": { "major": 2, "minor": 7 }, "paths": { "source": "/path/to/top-level-source-dir", "build": "/path/to/top-level-build-dir" @@ -998,6 +998,36 @@ with members: destination is available. The value is an unsigned integer 0-based index into the ``backtraceGraph`` member's ``nodes`` array. +``launchers`` + Optional member that is present on executable targets that have + at least one launcher specified by the project. The value is a + JSON array of entries corresponding to the specified launchers. + Each entry is a JSON object with members: + + ``command`` + A string specifying the path to the launcher on disk, represented + with forward slashes. If the file is inside the top-level source + directory then the path is specified relative to that directory. + + ``arguments`` + Optional member that is present when the launcher command has + arguments preceding the executable to be launched. The value + is a JSON array of strings representing the arguments. + + ``type`` + A string specifying the type of launcher. The value is one of + the following: + + ``emulator`` + An emulator for the target platform when cross-compiling. + See the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property. + + ``test`` + A start program for the execution of tests. + See the :prop_tgt:`TEST_LAUNCHER` target property. + + This field was added in codemodel version 2.7. + ``link`` Optional member that is present for executables and shared library targets that link into a runtime binary. The value is a JSON object diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index d4a43de..3e58b31 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -1053,6 +1053,10 @@ related to most of the expressions in this sub-section. ``1`` if CMake's compiler id of the C compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. + .. versionchanged:: 3.15 + Multiple ``compiler_ids`` can be specified. + CMake 3.14 and earlier only accepted a single compiler ID. + .. genex:: $<CXX_COMPILER_ID> CMake's compiler id of the CXX compiler used. @@ -1063,6 +1067,10 @@ related to most of the expressions in this sub-section. ``1`` if CMake's compiler id of the CXX compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. + .. versionchanged:: 3.15 + Multiple ``compiler_ids`` can be specified. + CMake 3.14 and earlier only accepted a single compiler ID. + .. genex:: $<CUDA_COMPILER_ID> .. versionadded:: 3.15 @@ -1115,6 +1123,10 @@ related to most of the expressions in this sub-section. ``1`` if CMake's compiler id of the Fortran compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. + .. versionchanged:: 3.15 + Multiple ``compiler_ids`` can be specified. + CMake 3.14 and earlier only accepted a single compiler ID. + .. genex:: $<HIP_COMPILER_ID> .. versionadded:: 3.21 diff --git a/Help/manual/cmake-language.7.rst b/Help/manual/cmake-language.7.rst index d0774cb..dba953d 100644 --- a/Help/manual/cmake-language.7.rst +++ b/Help/manual/cmake-language.7.rst @@ -224,7 +224,7 @@ For example:: They interpret the opening bracket as the start of an `Unquoted Argument`_. -.. _`Lua`: http://www.lua.org/ +.. _`Lua`: https://www.lua.org/ .. _`Quoted Argument`: diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 46707ff..35b4497 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -51,6 +51,16 @@ 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.29 +================================= + +.. toctree:: + :maxdepth: 1 + + CMP0158: add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when cross-compiling. </policy/CMP0158> + CMP0157: Swift compilation mode is selected by an abstraction. </policy/CMP0157> + CMP0156: De-duplicate libraries on link lines based on linker capabilities. </policy/CMP0156> + Policies Introduced by CMake 3.28 ================================= diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 0ff2f2a..458cb73 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -130,6 +130,7 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG /prop_tgt/AUTOGEN_BUILD_DIR + /prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX /prop_tgt/AUTOGEN_ORIGIN_DEPENDS /prop_tgt/AUTOGEN_PARALLEL /prop_tgt/AUTOGEN_TARGET_DEPENDS @@ -211,6 +212,7 @@ Properties on Targets /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG /prop_tgt/EXPORT_COMPILE_COMMANDS + /prop_tgt/EXPORT_FIND_PACKAGE_NAME /prop_tgt/EXPORT_NAME /prop_tgt/EXPORT_NO_SYSTEM /prop_tgt/EXPORT_PROPERTIES @@ -334,6 +336,7 @@ Properties on Targets /prop_tgt/LINK_SEARCH_START_STATIC /prop_tgt/LINK_WHAT_YOU_USE /prop_tgt/LINKER_LANGUAGE + /prop_tgt/LINKER_TYPE /prop_tgt/LOCATION /prop_tgt/LOCATION_CONFIG /prop_tgt/MACHO_COMPATIBILITY_VERSION @@ -389,11 +392,13 @@ Properties on Targets /prop_tgt/STATIC_LIBRARY_FLAGS_CONFIG /prop_tgt/STATIC_LIBRARY_OPTIONS /prop_tgt/SUFFIX + /prop_tgt/Swift_COMPILATION_MODE /prop_tgt/Swift_DEPENDENCIES_FILE /prop_tgt/Swift_LANGUAGE_VERSION /prop_tgt/Swift_MODULE_DIRECTORY /prop_tgt/Swift_MODULE_NAME /prop_tgt/SYSTEM + /prop_tgt/TEST_LAUNCHER /prop_tgt/TYPE /prop_tgt/UNITY_BUILD /prop_tgt/UNITY_BUILD_BATCH_SIZE diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst index e32bd29..1ebdf85 100644 --- a/Help/manual/cmake-toolchains.7.rst +++ b/Help/manual/cmake-toolchains.7.rst @@ -583,7 +583,7 @@ generator is recommended. The :generator:`Unix Makefiles` or :generator:`Ninja` generators can also be used, but they require the project to handle more areas like target CPU selection and code signing. -Any of the three systems can be targeted by setting the +Any of the Apple device platforms can be targeted by setting the :variable:`CMAKE_SYSTEM_NAME` variable to a value from the table below. By default, the latest Device SDK is chosen. As for all Apple platforms, a different SDK (e.g. a simulator) can be selected by setting the @@ -611,9 +611,11 @@ Variable :variable:`CMAKE_OSX_ARCHITECTURES` can be used to set architectures for both device and simulator. Variable :variable:`CMAKE_OSX_DEPLOYMENT_TARGET` can be used to set an iOS/tvOS/visionOS/watchOS deployment target. -Next configuration will install fat 5 architectures iOS library -and add the ``-miphoneos-version-min=9.3``/``-mios-simulator-version-min=9.3`` -flags to the compiler: +The next example installs five architectures in a universal binary for an iOS +library. It adds the relevant ``-miphoneos-version-min=9.3`` or +``-mios-simulator-version-min=9.3`` compiler flag where appropriate. +Note that the :variable:`CMAKE_IOS_INSTALL_COMBINED` variable used in the +example is now deprecated, so this approach is no longer recommended. .. code-block:: console diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index d9df773..0e778f8 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -55,7 +55,6 @@ Variables that Provide Information /variable/CMAKE_EDIT_COMMAND /variable/CMAKE_EXECUTABLE_SUFFIX /variable/CMAKE_EXECUTABLE_SUFFIX_LANG - /variable/CMAKE_EXTRA_GENERATOR /variable/CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES /variable/CMAKE_FIND_DEBUG_MODE /variable/CMAKE_FIND_PACKAGE_NAME @@ -74,6 +73,10 @@ Variables that Provide Information /variable/CMAKE_JOB_POOLS /variable/CMAKE_LANG_COMPILER_AR /variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT + /variable/CMAKE_LANG_COMPILER_LINKER + /variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT + /variable/CMAKE_LANG_COMPILER_LINKER_ID + /variable/CMAKE_LANG_COMPILER_LINKER_VERSION /variable/CMAKE_LANG_COMPILER_RANLIB /variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX /variable/CMAKE_LINK_LIBRARY_SUFFIX @@ -111,8 +114,10 @@ Variables that Provide Information /variable/CMAKE_SOURCE_DIR /variable/CMAKE_STATIC_LIBRARY_PREFIX /variable/CMAKE_STATIC_LIBRARY_SUFFIX + /variable/CMAKE_Swift_COMPILATION_MODE /variable/CMAKE_Swift_MODULE_DIRECTORY /variable/CMAKE_Swift_NUM_THREADS + /variable/CMAKE_TEST_LAUNCHER /variable/CMAKE_TOOLCHAIN_FILE /variable/CMAKE_TWEAK_VERSION /variable/CMAKE_VERBOSE_MAKEFILE @@ -126,6 +131,7 @@ Variables that Provide Information /variable/CMAKE_VS_PLATFORM_TOOLSET /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR + /variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN /variable/CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION /variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER @@ -169,8 +175,6 @@ Variables that Change Behavior /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 /variable/CMAKE_BUILD_TYPE /variable/CMAKE_CLANG_VFS_OVERLAY /variable/CMAKE_CODEBLOCKS_COMPILER_ID @@ -198,8 +202,6 @@ Variables that Change Behavior /variable/CMAKE_FIND_LIBRARY_PREFIXES /variable/CMAKE_FIND_LIBRARY_SUFFIXES /variable/CMAKE_FIND_NO_INSTALL_PREFIX - /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY - /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY /variable/CMAKE_FIND_PACKAGE_PREFER_CONFIG /variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS /variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL @@ -251,6 +253,7 @@ Variables that Change Behavior /variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES /variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName /variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY + /variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY /variable/CMAKE_STAGING_PREFIX /variable/CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS /variable/CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE @@ -336,15 +339,6 @@ Variables that Describe the System /variable/LINUX /variable/MINGW /variable/MSVC - /variable/MSVC10 - /variable/MSVC11 - /variable/MSVC12 - /variable/MSVC14 - /variable/MSVC60 - /variable/MSVC70 - /variable/MSVC71 - /variable/MSVC80 - /variable/MSVC90 /variable/MSVC_IDE /variable/MSVC_TOOLSET_VERSION /variable/MSVC_VERSION @@ -395,6 +389,7 @@ Variables that Control the Build /variable/CMAKE_APPLE_SILICON_PROCESSOR /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX /variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS /variable/CMAKE_AUTOGEN_PARALLEL /variable/CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE @@ -439,6 +434,7 @@ Variables that Control the Build /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT /variable/CMAKE_EXE_LINKER_FLAGS_INIT + /variable/CMAKE_EXPORT_FIND_PACKAGE_NAME /variable/CMAKE_FOLDER /variable/CMAKE_Fortran_FORMAT /variable/CMAKE_Fortran_MODULE_DIRECTORY @@ -459,7 +455,6 @@ Variables that Control the Build /variable/CMAKE_INSTALL_RPATH_USE_LINK_PATH /variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION /variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION_CONFIG - /variable/CMAKE_IOS_INSTALL_COMBINED /variable/CMAKE_LANG_CLANG_TIDY /variable/CMAKE_LANG_CLANG_TIDY_EXPORT_FIXES_DIR /variable/CMAKE_LANG_COMPILER_LAUNCHER @@ -474,6 +469,8 @@ Variables that Control the Build /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG /variable/CMAKE_LANG_LINKER_LAUNCHER + /variable/CMAKE_LANG_USING_LINKER_MODE + /variable/CMAKE_LANG_USING_LINKER_TYPE /variable/CMAKE_LANG_VISIBILITY_PRESET /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG @@ -490,6 +487,7 @@ Variables that Control the Build /variable/CMAKE_LINK_LIBRARY_USING_FEATURE_SUPPORTED /variable/CMAKE_LINK_WHAT_YOU_USE /variable/CMAKE_LINK_WHAT_YOU_USE_CHECK + /variable/CMAKE_LINKER_TYPE /variable/CMAKE_MACOSX_BUNDLE /variable/CMAKE_MACOSX_RPATH /variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG @@ -534,7 +532,6 @@ Variables that Control the Build /variable/CMAKE_UNITY_BUILD /variable/CMAKE_UNITY_BUILD_BATCH_SIZE /variable/CMAKE_UNITY_BUILD_UNIQUE_ID - /variable/CMAKE_USE_RELATIVE_PATHS /variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS /variable/CMAKE_VISIBILITY_INLINES_HIDDEN /variable/CMAKE_VS_DEBUGGER_COMMAND @@ -571,9 +568,6 @@ Variables for Languages /variable/CMAKE_C_EXTENSIONS /variable/CMAKE_C_STANDARD /variable/CMAKE_C_STANDARD_REQUIRED - /variable/CMAKE_COMPILER_IS_GNUCC - /variable/CMAKE_COMPILER_IS_GNUCXX - /variable/CMAKE_COMPILER_IS_GNUG77 /variable/CMAKE_CUDA_ARCHITECTURES /variable/CMAKE_CUDA_COMPILE_FEATURES /variable/CMAKE_CUDA_EXTENSIONS @@ -693,7 +687,6 @@ Variables for CTest /variable/CTEST_CUSTOM_TESTS_IGNORE /variable/CTEST_CUSTOM_WARNING_EXCEPTION /variable/CTEST_CUSTOM_WARNING_MATCH - /variable/CTEST_CVS_CHECKOUT /variable/CTEST_CVS_COMMAND /variable/CTEST_CVS_UPDATE_OPTIONS /variable/CTEST_DROP_LOCATION @@ -722,7 +715,6 @@ Variables for CTest /variable/CTEST_P4_UPDATE_OPTIONS /variable/CTEST_RESOURCE_SPEC_FILE /variable/CTEST_RUN_CURRENT_SCRIPT - /variable/CTEST_SCP_COMMAND /variable/CTEST_SCRIPT_DIRECTORY /variable/CTEST_SITE /variable/CTEST_SOURCE_DIRECTORY @@ -733,7 +725,6 @@ Variables for CTest /variable/CTEST_SVN_UPDATE_OPTIONS /variable/CTEST_TEST_LOAD /variable/CTEST_TEST_TIMEOUT - /variable/CTEST_TRIGGER_SITE /variable/CTEST_UPDATE_COMMAND /variable/CTEST_UPDATE_OPTIONS /variable/CTEST_UPDATE_VERSION_ONLY @@ -786,3 +777,67 @@ are subject to change, and not recommended for use in project code. /variable/CMAKE_LANG_PLATFORM_ID /variable/CMAKE_NOT_USING_CONFIG_FLAGS /variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION + +Deprecated Variables that Provide Information +============================================= + +.. toctree:: + :maxdepth: 1 + + /variable/CMAKE_EXTRA_GENERATOR + +Deprecated Variables that Change Behavior +========================================= + +.. toctree:: + :maxdepth: 1 + + /variable/CMAKE_AUTOMOC_RELAXED_MODE + /variable/CMAKE_BACKWARDS_COMPATIBILITY + /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY + /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY + +Deprecated Variables that Describe the System +============================================= + +.. toctree:: + :maxdepth: 1 + + /variable/MSVC10 + /variable/MSVC11 + /variable/MSVC12 + /variable/MSVC14 + /variable/MSVC60 + /variable/MSVC70 + /variable/MSVC71 + /variable/MSVC80 + /variable/MSVC90 + +Deprecated Variables that Control the Build +=========================================== + +.. toctree:: + :maxdepth: 1 + + /variable/CMAKE_IOS_INSTALL_COMBINED + /variable/CMAKE_USE_RELATIVE_PATHS + +Deprecated Variables for Languages +================================== + +.. toctree:: + :maxdepth: 1 + + /variable/CMAKE_COMPILER_IS_GNUCC + /variable/CMAKE_COMPILER_IS_GNUCXX + /variable/CMAKE_COMPILER_IS_GNUG77 + +Deprecated Variables for CTest +============================== + +.. toctree:: + :maxdepth: 1 + + /variable/CTEST_CVS_CHECKOUT + /variable/CTEST_SCP_COMMAND + /variable/CTEST_TRIGGER_SITE diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 5223acb..621c005 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -891,6 +891,10 @@ Available commands are: ``-`` will result in an error. Use ``--`` to indicate the end of options, in case a file starts with ``-``. + .. versionadded:: 3.29 + + ``cat`` can now print the standard input by passing the ``-`` argument. + .. program:: cmake-E .. option:: chdir <dir> <cmd> [<arg>...] @@ -1311,6 +1315,7 @@ The following ``cmake -E`` commands are available only on Windows: Write Windows registry value. +.. _`Find-Package Tool Mode`: Run the Find-Package Tool ========================= diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index a5b27b6..0917191 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -354,6 +354,8 @@ Run Tests .. option:: --test-dir <dir> + .. versionadded:: 3.20 + Specify the directory in which to look for tests, typically a CMake project build directory. If not specified, the current directory is used. @@ -1592,17 +1594,20 @@ that running several of these tests at once does not exhaust the GPU's memory pool. Please note that CTest has no concept of what a GPU is or how much memory it -has, nor does it have any way of communicating with a GPU to retrieve this -information or perform any memory management. CTest simply keeps track of a -list of abstract resource types, each of which has a certain number of slots -available for tests to use. Each test specifies the number of slots that it -requires from a certain resource, and CTest then schedules them in a way that -prevents the total number of slots in use from exceeding the listed capacity. -When a test is executed, and slots from a resource are allocated to that test, -tests may assume that they have exclusive use of those slots for the duration -of the test's process. - -The CTest resource allocation feature consists of two inputs: +has. It does not have any way of communicating with a GPU to retrieve this +information or perform any memory management, although the project can define +a test that provides details about the test machine (see +:ref:`ctest-resource-dynamically-generated-spec-file`). + +CTest keeps track of a list of abstract resource types, each of which has a +certain number of slots available for tests to use. Each test specifies the +number of slots that it requires from a certain resource, and CTest then +schedules them in a way that prevents the total number of slots in use from +exceeding the listed capacity. When a test is executed, and slots from a +resource are allocated to that test, tests may assume that they have exclusive +use of those slots for the duration of the test's process. + +The CTest resource allocation feature consists of at least two inputs: * The :ref:`resource specification file <ctest-resource-specification-file>`, described below, which describes the resources available on the system. @@ -1643,15 +1648,20 @@ properties to indicate a skipped test. Resource Specification File --------------------------- -The resource specification file is a JSON file which is passed to CTest, either -on the command line as :option:`ctest --resource-spec-file`, or as the -``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`. If a dashboard script -is used and ``RESOURCE_SPEC_FILE`` is not specified, the value of -:variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used instead. -If :option:`--resource-spec-file <ctest --resource-spec-file>`, ``RESOURCE_SPEC_FILE``, -and :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script are not specified, -the value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used -instead. If none of these are specified, no resource spec file is used. +The resource specification file is a JSON file which is passed to CTest in one +of a number of ways. It can be specified on the command line with the +:option:`ctest --resource-spec-file` option, it can be given using the +``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`, or it can be +generated dynamically as part of test execution (see +:ref:`ctest-resource-dynamically-generated-spec-file`). + +If a dashboard script is used and ``RESOURCE_SPEC_FILE`` is not specified, the +value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used +instead. If :option:`--resource-spec-file <ctest --resource-spec-file>`, +``RESOURCE_SPEC_FILE``, and :variable:`CTEST_RESOURCE_SPEC_FILE` in the +dashboard script are not specified, the value of +:variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used instead. +If none of these are specified, no resource spec file is used. The resource specification file must be a JSON object. All examples in this document assume the following resource specification file: @@ -1839,6 +1849,31 @@ fixture in their :prop_test:`FIXTURES_REQUIRED`, and a resource spec file may not be specified with the ``--resource-spec-file`` argument or the :variable:`CTEST_RESOURCE_SPEC_FILE` variable. +.. _`ctest-job-server-integration`: + +Job Server Integration +====================== + +.. versionadded:: 3.29 + +On POSIX systems, when running under the context of a `Job Server`_, +CTest shares its job slots. This is independent of the :prop_test:`PROCESSORS` +test property, which still counts against CTest's :option:`-j <ctest -j>` +parallel level. CTest acquires exactly one token from the job server before +running each test, and returns it when the test finishes. + +For example, consider the ``Makefile``: + +.. literalinclude:: CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make + :language: make + +When invoked via ``make -j 2 test``, ``ctest`` connects to the job server, +acquires a token for each test, and runs at most 2 tests concurrently. + +On Windows systems, job server integration is not yet implemented. + +.. _`Job Server`: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html + See Also ======== diff --git a/Help/policy/CMP0156.rst b/Help/policy/CMP0156.rst new file mode 100644 index 0000000..fa97519 --- /dev/null +++ b/Help/policy/CMP0156.rst @@ -0,0 +1,47 @@ +CMP0156 +------- + +.. versionadded:: 3.29 + +De-duplicate libraries on link lines based on linker capabilities. + +Traditional linkers maintain a set of undefined symbols during linking. The +linker processes each file in the order in which it appears on the command +line, until the set of undefined symbols becomes empty. An object file is +linked into the output object when it is encountered, with its undefined +symbols added to the set. Upon encountering an archive file a traditional +linker searches the objects contained therein, and processes those that satisfy +symbols in the unresolved set. + +Handling mutually dependent archives may be awkward when using a traditional +linker. Archive files may have to be specified multiple times. + +Some linkers (for instance Apple or Windows linkers, as well as``LLVM LLD``) +records all symbols found in objects and archives as it iterates over command +line arguments. When one of these linkers encounters an undefined symbol that +can be resolved by an object file contained in a previously processed archive +file, it immediately extracts and links it into the output object. + +CMake 3.28 and below may generate link lines that repeat static libraries as +a traditional linker would need, even when using a linker does not need it. +They may also de-duplicate shared libraries by keeping their last occurrence, +which on Windows platforms can change DLL load order. + +CMake 3.29 and above prefer to apply different strategies based on linker +capabilities. So, when targeting Apple and Windows platforms, all +libraries are de-duplicated. Moreover, on Windows platforms, libraries +are de-duplicated by keeping their fist occurrence, thus respecting the +project-specified order. This policy provides compatibility with projects +that have not been updated to expect the latter behavior. + +The ``OLD`` behavior for this policy is to always repeat static libraries +as if using a traditional linker, and always de-duplicate shared libraries +by keeping the last occurrence of each. The ``NEW`` behavior for this policy +is to apply different strategies based on linker capabilities. + +This policy was introduced in CMake version 3.29. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. +Unlike many policies, CMake version |release| does *not* warn +when this policy is not set and simply uses ``OLD`` behavior. + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0157.rst b/Help/policy/CMP0157.rst new file mode 100644 index 0000000..a8f8210 --- /dev/null +++ b/Help/policy/CMP0157.rst @@ -0,0 +1,48 @@ +CMP0157 +------- + +.. versionadded:: 3.29 + +Swift compilation mode is selected by an abstraction. + +The Swift compiler can compile modules in different modes. The desired build +mode depends whether the developer is iterating and wants to incrementally make +changes, or if they are building a release for distribution and want more +optimizations applied to the resulting binary. + +CMake versions 3.26 through 3.28 build Swift binaries with whole-module +optimizations enabled when configured in a non-debug build type. +For CMake versions earlier than 3.26, the developer needs to specify +the necessary flag manually for the :ref:`Ninja Generators`, and cannot +not specify whole-module optimizations to the :generator:`Xcode` generator. + +CMake versions 3.29 and above prefer to set the compilation mode using +the :prop_tgt:`Swift_COMPILATION_MODE` target property, which can be +initialized by the :variable:`CMAKE_Swift_COMPILATION_MODE` variable. + +This policy provides compatibility for projects that have not been updated. +The policy setting takes effect as of the first :command:`project` or +:command:`enable_language` command that enables the ``Swift`` language. + +.. note:: + + Once the policy has taken effect at the top of a project, that choice + must be used throughout the tree. In projects that have nested projects + in subdirectories, be sure to convert everything together. + +The ``OLD`` behavior for this policy builds all Swift targets in +``wholemodule`` mode for non-debug configurations. :ref:`Ninja Generators` +prepend the ``-wmo`` flag to the default set of Swift flags. +The :generator:`Xcode` generator sets the ``SWIFT_COMPILATION_MODE`` +attribute to ``wholemodule`` in the generated Xcode project file. + +The ``NEW`` behavior for this policy is to apply the compilation mode specified +in the :prop_tgt:`Swift_COMPILATION_MODE` target property, initialized as each +target is created by the :variable:`CMAKE_Swift_COMPILATION_MODE` variable. + +This policy was introduced in CMake version 3.29. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. +Unlike many policies, CMake version |release| does *not* warn when this policy +is not set and simply uses ``OLD`` behavior. + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0158.rst b/Help/policy/CMP0158.rst new file mode 100644 index 0000000..4289c6d --- /dev/null +++ b/Help/policy/CMP0158.rst @@ -0,0 +1,27 @@ +CMP0158 +------- + +.. versionadded:: 3.29 + +:command:`add_test` honors :variable:`CMAKE_CROSSCOMPILING_EMULATOR` only +when :variable:`cross-compiling <CMAKE_CROSSCOMPILING>`. + +In CMake 3.28 and below, :command:`add_test` unconditionally used the +:prop_tgt:`CROSSCOMPILING_EMULATOR` target property (initialized by the +:variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable) to run test commands +naming executable targets. CMake 3.29 and above prefer to use the emulator +only when the :variable:`CMAKE_CROSSCOMPILING` variable is enabled. The +:variable:`CMAKE_TEST_LAUNCHER` variable may be used instead when not +cross-compiling. This policy provides compatibility for projects that +have not been updated. + +The ``OLD`` behavior for this policy is for :command:`add_test` to use +the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property unconditionally. +The ``NEW`` behavior for this policy is for :command:`add_test` to use +the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property only when +:variable:`cross-compiling <CMAKE_CROSSCOMPILING>`. + +This policy was introduced in CMake version 3.29. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. +Unlike many policies, CMake version |release| does *not* warn +when this policy is not set and simply uses ``OLD`` behavior. diff --git a/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst b/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst index 1ec4517..e94856d 100644 --- a/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst +++ b/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst @@ -1,15 +1,22 @@ FAIL_REGULAR_EXPRESSION ----------------------- -If the output matches this regular expression the test will fail, -regardless of the process exit code. +If the test output (stdout or stderr) matches this regular expression the test +will fail, regardless of the process exit code. Tests that exceed the timeout +specified by :prop_test:`TIMEOUT` fail regardless of +``FAIL_REGULAR_EXPRESSION``. Any non-zero return code or system-level test +failures including segmentation faults, signal abort, or heap errors fail the +test even if the regular expression does not match. -If set, if the output matches one of specified regular expressions, -the test will fail. Example: +If set, if the output matches one of specified regular expressions, the test +will fail. Example: .. code-block:: cmake - set_tests_properties(mytest PROPERTIES + # test would pass, except for FAIL_REGULAR_EXPRESSION + add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Failed") + + set_property(TEST mytest PROPERTY FAIL_REGULAR_EXPRESSION "[^a-z]Error;ERROR;Failed" ) diff --git a/Help/prop_test/PASS_REGULAR_EXPRESSION.rst b/Help/prop_test/PASS_REGULAR_EXPRESSION.rst index 96468c0..b19e637 100644 --- a/Help/prop_test/PASS_REGULAR_EXPRESSION.rst +++ b/Help/prop_test/PASS_REGULAR_EXPRESSION.rst @@ -1,20 +1,50 @@ PASS_REGULAR_EXPRESSION ----------------------- -The output must match this regular expression for the test to pass. -The process exit code is ignored. +The test output (stdout or stderr) must match this regular expression +for the test to pass. The process exit code is ignored. Tests that exceed +the timeout specified by :prop_test:`TIMEOUT` still fail regardless of +``PASS_REGULAR_EXPRESSION``. System-level test failures including +segmentation faults, signal abort, or heap errors may fail the test even +if ``PASS_REGULAR_EXPRESSION`` is matched. -If set, the test output will be checked against the specified regular -expressions and at least one of the regular expressions has to match, -otherwise the test will fail. Example: +Example: .. code-block:: cmake - set_tests_properties(mytest PROPERTIES - PASS_REGULAR_EXPRESSION "TestPassed;All ok" + add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Passed this test") + + set_property(TEST mytest PROPERTY + PASS_REGULAR_EXPRESSION "pass;Passed" ) ``PASS_REGULAR_EXPRESSION`` expects a list of regular expressions. +To run a test that may have a system-level failure, but still pass if +``PASS_REGULAR_EXPRESSION`` matches, use a CMake command to wrap the +executable run. Note that this will prevent automatic handling of the +:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER` +target property. + +.. code-block:: cmake + + add_executable(main main.c) + + add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>) + + set_property(TEST sigabrt PROPERTY PROPERTY_REGULAR_EXPRESSION "pass;Passed") + +.. code-block:: c + + #include <signal.h> + #include <stdio.h> + + int main(void){ + fprintf(stdout, "Passed\n"); + fflush(stdout); /* ensure the output buffer is seen */ + raise(SIGABRT); + return 0; + } + See also the :prop_test:`FAIL_REGULAR_EXPRESSION` and :prop_test:`SKIP_REGULAR_EXPRESSION` test properties. diff --git a/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst index 60038e4..8717a0a 100644 --- a/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst +++ b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst @@ -3,19 +3,45 @@ SKIP_REGULAR_EXPRESSION .. versionadded:: 3.16 -If the output matches this regular expression the test will be marked as skipped. +If the test output (stderr or stdout) matches this regular expression the test +will be marked as skipped, regardless of the process exit code. Tests that +exceed the timeout specified by :prop_test:`TIMEOUT` still fail regardless of +``SKIP_REGULAR_EXPRESSION``. System-level test failures including segmentation +faults, signal abort, or heap errors may fail the test even if the regular +expression matches. -If set, if the output matches one of specified regular expressions, -the test will be marked as skipped. Example: +Example: .. code-block:: cmake + add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Skipped this test") + set_property(TEST mytest PROPERTY SKIP_REGULAR_EXPRESSION "[^a-z]Skip" "SKIP" "Skipped" ) ``SKIP_REGULAR_EXPRESSION`` expects a list of regular expressions. +To run a test that may have a system-level failure, but still skip if +``SKIP_REGULAR_EXPRESSION`` matches, use a CMake command to wrap the +executable run. Note that this will prevent automatic handling of the +:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER` +target property. + +.. code-block:: cmake + + add_executable(main main.c) + + add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>) + + set_property(TEST sigabrt PROPERTY SKIP_REGULAR_EXPRESSION "SIGABRT;[aA]bort") + +.. code-block:: c + + #include <signal.h> + + int main(void){ raise(SIGABRT); return 0; } + See also the :prop_test:`SKIP_RETURN_CODE`, :prop_test:`PASS_REGULAR_EXPRESSION`, and :prop_test:`FAIL_REGULAR_EXPRESSION` test properties. diff --git a/Help/prop_test/SKIP_RETURN_CODE.rst b/Help/prop_test/SKIP_RETURN_CODE.rst index 23c4c62..57fc031 100644 --- a/Help/prop_test/SKIP_RETURN_CODE.rst +++ b/Help/prop_test/SKIP_RETURN_CODE.rst @@ -9,4 +9,40 @@ a return code of the process can be specified that will mark the test as ``Not Run`` if it is encountered. Valid values are in the range of 0 to 255, inclusive. -See also the :prop_test:`SKIP_REGULAR_EXPRESSION` property. +Tests that exceed the timeout specified by :prop_test:`TIMEOUT` still fail +regardless of ``SKIP_RETURN_CODE``. +System-level test failures including segmentation faults, +signal abort, or heap errors may fail the test even if the return code matches. + +.. code-block:: cmake + + # cmake (1) defines this to return code 1 + add_test(NAME r1 COMMAND ${CMAKE_COMMAND} -E false) + + set_tests_properties(r1 PROPERTIES SKIP_RETURN_CODE 1) + + +To run a test that may have a system-level failure, but still skip if +``SKIP_RETURN_CODE`` matches, use a CMake command to wrap the executable run. +Note that this will prevent automatic handling of the +:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER` target +property. + +.. code-block:: cmake + + add_executable(main main.c) + + # cmake -E env <command> returns 1 if the command fails in any way + add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>) + + set_property(TEST sigabrt PROPERTY SKIP_RETURN_CODE 1) + +.. code-block:: c + + #include <signal.h> + + int main(void){ raise(SIGABRT); return 0; } + + +To handle multiple types of cases that may need to be skipped, consider the +:prop_test:`SKIP_REGULAR_EXPRESSION` property. diff --git a/Help/prop_test/WILL_FAIL.rst b/Help/prop_test/WILL_FAIL.rst index 4926f40..9d61ab7 100644 --- a/Help/prop_test/WILL_FAIL.rst +++ b/Help/prop_test/WILL_FAIL.rst @@ -1,8 +1,37 @@ WILL_FAIL --------- -If set to true, this will invert the pass/fail flag of the test. +If ``true``, inverts the pass / fail test criteria. Tests for which +``WILL_FAIL`` is ``true`` fail with return code 0 and pass with non-zero +return code. Tests that exceed the timeout specified by :prop_test:`TIMEOUT` +still fail regardless of ``WILL_FAIL``. +System-level test failures including segmentation faults, +signal abort, or heap errors may fail the test even if ``WILL_FAIL`` is true. -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. +Example of a test that would ordinarily pass, but fails because ``WILL_FAIL`` +is ``true``: + +.. code-block:: cmake + + add_test(NAME failed COMMAND ${CMAKE_COMMAND} -E true) + set_property(TEST failed PROPERTY WILL_FAIL true) + +To run a test that may have a system-level failure, but still pass if +``WILL_FAIL`` is set, use a CMake command to wrap the executable run. +Note that this will prevent automatic handling of the +:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER` +target property. + +.. code-block:: cmake + + add_executable(main main.c) + + add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>) + + set_property(TEST sigabrt PROPERTY WILL_FAIL TRUE) + +.. code-block:: c + + #include <signal.h> + + int main(void){ raise(SIGABRT); return 0; } diff --git a/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst b/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst new file mode 100644 index 0000000..f1e51a7 --- /dev/null +++ b/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst @@ -0,0 +1,18 @@ +AUTOGEN_COMMAND_LINE_LENGTH_MAX +------------------------------- + +.. versionadded:: 3.29 + +Command line length limit for autogen targets, i.e. ``moc`` or ``uic``, +that triggers the use of response files on Windows instead of passing all +arguments to the command line. + +- An empty (or unset) value sets the limit to 32000 +- A positive non zero integer value sets the exact command line length + limit. + +By default ``AUTOGEN_COMMAND_LINE_LENGTH_MAX`` is initialized from +:variable:`CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX`. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 0feb2e8..6f58afb 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -247,5 +247,9 @@ will be generated when this variable is ``ON``. This target property controls the number of ``moc`` or ``uic`` processes to start in parallel during builds. +:prop_tgt:`AUTOGEN_COMMAND_LINE_LENGTH_MAX`: +This target property controls the limit when to use response files for +``moc`` or ``uic`` processes on Windows. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst index dc854b2..95366ee 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -81,5 +81,9 @@ will be generated when this variable is ``ON``. This target property controls the number of ``moc`` or ``uic`` processes to start in parallel during builds. +:prop_tgt:`AUTOGEN_COMMAND_LINE_LENGTH_MAX`: +This target property controls the limit when to use response files for +``moc`` or ``uic`` processes on Windows. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst new file mode 100644 index 0000000..b49f005 --- /dev/null +++ b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst @@ -0,0 +1,12 @@ +EXPORT_FIND_PACKAGE_NAME +------------------------ + +.. versionadded:: 3.29 + +Control the package name associated with a dependency target when exporting a +:command:`find_dependency` call in :command:`install(EXPORT)` or +:command:`export(EXPORT)`. This can be used to assign a package name to a +package that is built by CMake and exported, or to override the package in the +:command:`find_package` call that created the target. + +This property is initialized by :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME`. diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst index 2351374..b162253 100644 --- a/Help/prop_tgt/IMPORTED_LOCATION.rst +++ b/Help/prop_tgt/IMPORTED_LOCATION.rst @@ -32,7 +32,7 @@ listed in the :prop_tgt:`IMPORTED_CONFIGURATIONS` target property may be 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. +or :prop_tgt:`LOCATION_<CONFIG>` properties. For platforms with import libraries (e.g. Windows, AIX or Apple) see also :prop_tgt:`IMPORTED_IMPLIB`. diff --git a/Help/prop_tgt/LINKER_TYPE.rst b/Help/prop_tgt/LINKER_TYPE.rst new file mode 100644 index 0000000..90a663d --- /dev/null +++ b/Help/prop_tgt/LINKER_TYPE.rst @@ -0,0 +1,26 @@ +LINKER_TYPE +----------- + +.. versionadded:: 3.29 + +Specify which linker will be used for the link step. The property value may use +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +.. code-block:: cmake + + add_library(lib1 SHARED ...) + set_property(TARGET lib1 PROPERTY LINKER_TYPE LLD) + +This specifies that ``lib1`` should use linker type ``LLD`` for the link step. +The implementation details will be provided by the variable +:variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` with ``<TYPE>`` having the value +``LLD``. + +This property is not supported on :generator:`Green Hills MULTI` and +:generator:`Visual Studio 9 2008` generators. + +.. note:: + It is assumed that the linker specified is fully compatible with the standard + one. CMake will not do any options translation. + +.. include:: ../variable/LINKER_PREDEFINED_TYPES.txt diff --git a/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt b/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt new file mode 100644 index 0000000..b94380a --- /dev/null +++ b/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt @@ -0,0 +1,19 @@ +``incremental`` + Compiles each Swift source in the module separately, resulting in better + parallelism in the build. The compiler emits additional information into + the build directory improving rebuild performance when small changes are made + to the source between rebuilds. This is the best option to use while + iterating on changes in a project. + +``wholemodule`` + Whole-module optimizations are slowest to compile, but results in the most + optimized library. The entire context is loaded into once instance of the + compiler, so there is no parallelism across source files in the module. + +``singlefile`` + Compiles each source in a Swift modules separately, resulting in better + parallelism. Unlike the ``incremental`` build mode, no additional information + is emitted by the compiler during the build, so rebuilding after making small + changes to the source file will not run faster. This option should be used + sparingly, preferring ``incremental`` builds, unless working around a compiler + bug. diff --git a/Help/prop_tgt/Swift_COMPILATION_MODE.rst b/Help/prop_tgt/Swift_COMPILATION_MODE.rst new file mode 100644 index 0000000..e26474a --- /dev/null +++ b/Help/prop_tgt/Swift_COMPILATION_MODE.rst @@ -0,0 +1,33 @@ +Swift_COMPILATION_MODE +---------------------- + +.. versionadded:: 3.29 + +Specify how Swift compiles a target. + +The allowed values are: + +.. include:: Swift_COMPILATION_MODE-VALUES.txt + +Use :manual:`generator expressions <cmake-generator-expressions(7)>` to support +per-configuration specification. For example, the code: + +.. code-block:: cmake + + add_library(foo foo.swift) + set_property(TARGET foo PROPERTY + Swift_COMPILATION_MODE "$<IF:$<CONFIG:Release>,wholemodule,incremental>") + +sets the Swift compilation mode to wholemodule mode in the release configuration +and sets the property to incremental mode in other configurations. + +The property is initialized from the value of the +:variable:`CMAKE_Swift_COMPILATION_MODE` variable, if it is set. If the property +is not set or is empty, then CMake uses the default value ``incremental`` to +specify the swift compilation mode. + +.. note:: + + This property only has effect when policy :policy:`CMP0157` is set to ``NEW`` + prior to the first :command:`project` or :command:`enable_language` command + that enables the Swift language. diff --git a/Help/prop_tgt/TEST_LAUNCHER.rst b/Help/prop_tgt/TEST_LAUNCHER.rst new file mode 100644 index 0000000..2e44492 --- /dev/null +++ b/Help/prop_tgt/TEST_LAUNCHER.rst @@ -0,0 +1,20 @@ +TEST_LAUNCHER +------------- + +.. versionadded:: 3.29 + +Use the given launcher to run executables. +This command will be added as a prefix to :command:`add_test` commands +for build target system executables and is meant to be run on the host +machine. + +It effectively acts as a run script for tests in a similar way +to how :variable:`CMAKE_<LANG>_COMPILER_LAUNCHER` works for compilation. + +If this property contains a :ref:`semicolon-separated list <CMake Language +Lists>`, then the first value is the command and remaining values are its +arguments. + +This property is initialized by the value of the +:variable:`CMAKE_TEST_LAUNCHER` variable if it is set when a target +is created. diff --git a/Help/prop_tgt/UNITY_BUILD.rst b/Help/prop_tgt/UNITY_BUILD.rst index f827a20..52f4714 100644 --- a/Help/prop_tgt/UNITY_BUILD.rst +++ b/Help/prop_tgt/UNITY_BUILD.rst @@ -30,11 +30,23 @@ values: If no explicit :prop_tgt:`UNITY_BUILD_MODE` has been specified, CMake will default to ``BATCH``. -Unity builds are not currently supported for all languages. CMake version -|release| supports combining ``C`` and ``CXX`` source files. For targets that -mix source files from more than one language, CMake will separate the languages -such that each generated unity source file only contains sources for a single -language. +Unity builds are supported for the following languages: + +``C`` + .. versionadded:: 3.16 + +``CXX`` + .. versionadded:: 3.16 + +``OBJC`` + .. versionadded:: 3.29 + +``OBJCXX`` + .. versionadded:: 3.29 + +For targets that mix source files from more than one language, CMake +separates the languages such that each generated unity source file only +contains sources for a single language. This property is initialized by the value of the :variable:`CMAKE_UNITY_BUILD` variable when a target is created. diff --git a/Help/prop_tgt/XCODE_EMBED_type.rst b/Help/prop_tgt/XCODE_EMBED_type.rst index e27d905..0354f97 100644 --- a/Help/prop_tgt/XCODE_EMBED_type.rst +++ b/Help/prop_tgt/XCODE_EMBED_type.rst @@ -43,6 +43,28 @@ The supported values for ``<type>`` are: The specified items will be added to the ``Embed Resources`` build phase. They must be CMake target names or folder paths. +``XPC_SERVICES`` + .. versionadded:: 3.29 + + The specified items will be added to the ``Embed XPC Services`` build phase. + They must be CMake target names. + +When listing a target as any of the things to embed, Xcode must see that target +as part of the same Xcode project, or a sub-project of the one defining the +bundle. In order to satisfy this constraint, the CMake project must ensure +at least one of the following: + +* The :variable:`CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY` variable is set + to true in the top level ``CMakeLists.txt`` file. This is the simplest and + most robust approach. +* Define the target-to-embed in a subdirectory of the one that defines the + target being embedded into. +* If the target-to-embed and the target being embedded into are in separate, + unrelated directories (i.e. they are siblings, not one a parent of the + other), ensure they have a common :command:`project` call in a parent + directory and no other :command:`project` calls between themselves and that + common :command:`project` call. + See also :prop_tgt:`XCODE_EMBED_<type>_PATH`, :prop_tgt:`XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY` and :prop_tgt:`XCODE_EMBED_<type>_CODE_SIGN_ON_COPY`. diff --git a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst index ef04d14..255aa68 100644 --- a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst +++ b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst @@ -25,3 +25,6 @@ The supported values for ``<type>`` are: ``RESOURCES`` .. versionadded:: 3.28 + +``XPC_SERVICES`` + .. versionadded:: 3.29 diff --git a/Help/release/3.22.rst b/Help/release/3.22.rst index eba5d66..2d060ac 100644 --- a/Help/release/3.22.rst +++ b/Help/release/3.22.rst @@ -19,7 +19,7 @@ Commands * The :command:`string(TIMESTAMP)` command now supports the ``%V`` specifier for ISO 8601 week numbers. -.. _`OS identification variables`: https://www.freedesktop.org/software/systemd/man/os-release.html +.. _`OS identification variables`: https://www.freedesktop.org/software/systemd/man/latest/os-release.html Variables --------- 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/Linker-user-selection.rst b/Help/release/dev/Linker-user-selection.rst new file mode 100644 index 0000000..ed78099 --- /dev/null +++ b/Help/release/dev/Linker-user-selection.rst @@ -0,0 +1,6 @@ +Linker-user-selection +--------------------- + +* The linker tool can now be specified for a selection of compilers/platforms + by setting :variable:`CMAKE_LINKER_TYPE` variable or :prop_tgt:`LINKER_TYPE` + target property. diff --git a/Help/release/dev/cmake-E-cat-stdin.rst b/Help/release/dev/cmake-E-cat-stdin.rst new file mode 100644 index 0000000..43a8aed --- /dev/null +++ b/Help/release/dev/cmake-E-cat-stdin.rst @@ -0,0 +1,5 @@ +cmake-E-cat-stdin +----------------- + +* :manual:`cmake(1)` :option:`-E cat <cmake-E cat>` can now print the standard + input by passing the ``-`` argument. diff --git a/Help/release/dev/cmake-test-launcher.rst b/Help/release/dev/cmake-test-launcher.rst new file mode 100644 index 0000000..a82a8ad --- /dev/null +++ b/Help/release/dev/cmake-test-launcher.rst @@ -0,0 +1,11 @@ +cmake-test-launcher +------------------- + +* A :variable:`CMAKE_TEST_LAUNCHER` variable and corresponding + :prop_tgt:`TEST_LAUNCHER` target property were added to specify + a launcher to be used by executable targets when invoked by + tests added by the :command:`add_test` command. + +* The :command:`add_test` command now honors + :variable:`CMAKE_CROSSCOMPILING_EMULATOR` only when cross-compiling. + See policy :policy:`CMP0158`. diff --git a/Help/release/dev/compiler-linker.rst b/Help/release/dev/compiler-linker.rst new file mode 100644 index 0000000..4783656 --- /dev/null +++ b/Help/release/dev/compiler-linker.rst @@ -0,0 +1,8 @@ +compiler-linker +--------------- + +* The linker effectively used for the link step is now documented with the + :variable:`CMAKE_<LANG>_COMPILER_LINKER`, + :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID`, + :variable:`CMAKE_<LANG>_COMPILER_LINKER_VERSION` and + :variable:`CMAKE_<LANG>_COMPILER_LINKER_FRONTEND_VARIANT` variables. diff --git a/Help/release/dev/cpack-auto-suffixes.rst b/Help/release/dev/cpack-auto-suffixes.rst new file mode 100644 index 0000000..f3fd2af --- /dev/null +++ b/Help/release/dev/cpack-auto-suffixes.rst @@ -0,0 +1,10 @@ +cpack-auto-suffixes +------------------- + +* The :cpack_gen:`CPack DEB Generator` :variable:`CPACK_DEBIAN_FILE_NAME` + variable may now be set without any suffix, and the ``.deb`` suffix + will be added automatically. + +* The :cpack_gen:`CPack RPM Generator` :variable:`CPACK_RPM_FILE_NAME` + variable may now be set without any suffix, and the ``.rpm`` suffix + will be added automatically. diff --git a/Help/release/dev/create_test_sourcelist-full-filepath.rst b/Help/release/dev/create_test_sourcelist-full-filepath.rst new file mode 100644 index 0000000..1afa82c --- /dev/null +++ b/Help/release/dev/create_test_sourcelist-full-filepath.rst @@ -0,0 +1,5 @@ +create_test_sourcelist-full-filepath +------------------------------------ + +* The :command:`create_test_sourcelist` command now provides a full path to + the generated driver source file. diff --git a/Help/release/dev/ctest-jobserver-client.rst b/Help/release/dev/ctest-jobserver-client.rst new file mode 100644 index 0000000..37e22c0 --- /dev/null +++ b/Help/release/dev/ctest-jobserver-client.rst @@ -0,0 +1,5 @@ +ctest-jobserver-client +---------------------- + +* :manual:`ctest(1)` now supports :ref:`job server integration + <ctest-job-server-integration>` on POSIX systems. diff --git a/Help/release/dev/export-find_dependency-calls.rst b/Help/release/dev/export-find_dependency-calls.rst new file mode 100644 index 0000000..58064b7 --- /dev/null +++ b/Help/release/dev/export-find_dependency-calls.rst @@ -0,0 +1,16 @@ +export-find_dependency-calls +---------------------------- + +* :command:`install(EXPORT)` and :command:`export(EXPORT)` learned a new + ``EXPORT_PACKAGE_DEPENDENCIES`` argument, which can be used to generate + :command:`find_dependency` calls based on what targets the exported targets + depend on. +* A new :command:`export(SETUP)` signature was created to configure export + sets. This can be used to configure how :command:`find_dependency` calls are + exported. +* A new :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` target property was created to + allow targets to specify what package name to pass when exporting + :command:`find_dependency` calls. This property is initialized with a new + :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` variable. +* :command:`FetchContent_MakeAvailable` now sets the + :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` variable for CMake projects. diff --git a/Help/release/dev/fileapi-exe-launcher.rst b/Help/release/dev/fileapi-exe-launcher.rst new file mode 100644 index 0000000..09fc2ae --- /dev/null +++ b/Help/release/dev/fileapi-exe-launcher.rst @@ -0,0 +1,7 @@ +fileapi-exe-launcher +-------------------- + +* The :manual:`cmake-file-api(7)` "codemodel" version 2 ``version`` field has + been updated to 2.7. +* The :manual:`cmake-file-api(7)` "codemodel" version 2 "target" object gained + a new "launchers" field. diff --git a/Help/release/dev/find-rustc-importlibs.rst b/Help/release/dev/find-rustc-importlibs.rst new file mode 100644 index 0000000..646afff --- /dev/null +++ b/Help/release/dev/find-rustc-importlibs.rst @@ -0,0 +1,6 @@ +find-rustc-importlibs +--------------------- + +* On Windows, when targeting the MSVC ABI, the :command:`find_library` command + now considers ``.dll.lib`` file names before ``.lib``. This is the default + suffix for DLL import libraries created by Rust toolchains for the MSVC ABI. diff --git a/Help/release/dev/if-check-file-permissions.rst b/Help/release/dev/if-check-file-permissions.rst new file mode 100644 index 0000000..ec69b00 --- /dev/null +++ b/Help/release/dev/if-check-file-permissions.rst @@ -0,0 +1,5 @@ +if-check-file-permissions +------------------------- + +* The :command:`if` command gained new tests ``IS_READABLE``, ``IS_WRITABLE`` + and ``IS_EXECUTABLE`` to check file or directory permissions. diff --git a/Help/release/dev/install-export-xcframework.rst b/Help/release/dev/install-export-xcframework.rst new file mode 100644 index 0000000..513a552 --- /dev/null +++ b/Help/release/dev/install-export-xcframework.rst @@ -0,0 +1,9 @@ +install-export-xcframework +-------------------------- + +* The :command:`export(SETUP)` command gained a new ``XCFRAMEWORK_LOCATION`` + argument, which can be used to specify the location of a ``.xcframework`` + that can be substituted for the installed library. +* The :module:`CMakePackageConfigHelpers` module gained a new + :command:`generate_apple_platform_selection_file` function, which can be + used to generate a file that includes another Apple-platform-specific file. diff --git a/Help/release/dev/link-deduplicate-libs.rst b/Help/release/dev/link-deduplicate-libs.rst new file mode 100644 index 0000000..4a03f2c --- /dev/null +++ b/Help/release/dev/link-deduplicate-libs.rst @@ -0,0 +1,5 @@ +link-deduplicate-libs +--------------------- + +* CMake learned to de-duplicate libraries on link lines based on linker + capabilities. See policy :policy:`CMP0156`. diff --git a/Help/release/dev/make-test-depend-on-all.rst b/Help/release/dev/make-test-depend-on-all.rst new file mode 100644 index 0000000..ef43faf --- /dev/null +++ b/Help/release/dev/make-test-depend-on-all.rst @@ -0,0 +1,6 @@ +make-test-depend-on-all +----------------------- + +* The :variable:`CMAKE_SKIP_TEST_ALL_DEPENDENCY` variable was added + to control whether the ``test`` (or ``RUN_TESTS``) buildsystem + target depends on the ``all`` (or ``ALL_BUILD``) target. diff --git a/Help/release/dev/package-dispatch-apple.rst b/Help/release/dev/package-dispatch-apple.rst new file mode 100644 index 0000000..d49cce1 --- /dev/null +++ b/Help/release/dev/package-dispatch-apple.rst @@ -0,0 +1,7 @@ +package-dispatch-apple +---------------------- + +* The :module:`CMakePackageConfigHelpers` module gained a new + :command:`generate_apple_architecture_selection_file` function, which can be + used to generate a file that includes an architecture-specific implementation + of a package for an Apple platform. diff --git a/Help/release/dev/project-include-multiple.rst b/Help/release/dev/project-include-multiple.rst new file mode 100644 index 0000000..3f7b360 --- /dev/null +++ b/Help/release/dev/project-include-multiple.rst @@ -0,0 +1,11 @@ +project-include-multiple +------------------------ + +* The :variable:`CMAKE_PROJECT_INCLUDE`, + :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, + :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, and + :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` variables learned + to support a :ref:`semicolon-separated list <CMake Language Lists>` of + CMake language files to be included sequentially. These variables can also + reference module names to be found in :variable:`CMAKE_MODULE_PATH` or + builtin to CMake. diff --git a/Help/release/dev/unity-build-objc.rst b/Help/release/dev/unity-build-objc.rst new file mode 100644 index 0000000..ce6460b --- /dev/null +++ b/Help/release/dev/unity-build-objc.rst @@ -0,0 +1,5 @@ +unity-build-objc +---------------- + +* The :prop_tgt:`UNITY_BUILD` target property now supports the + Objective C (``OBJC``) and Objective C++ (``OBJCXX``) languages. diff --git a/Help/release/dev/vs-ifx.rst b/Help/release/dev/vs-ifx.rst new file mode 100644 index 0000000..9e72cc7 --- /dev/null +++ b/Help/release/dev/vs-ifx.rst @@ -0,0 +1,7 @@ +vs-ifx +------ + +* :ref:`Visual Studio Generators` now support selecting between the + Intel oneAPI Fortran compiler (``ifx``) and the Intel classic Fortran + compiler (``ifort``) using a ``fortran=`` field in + :variable:`CMAKE_GENERATOR_TOOLSET`. diff --git a/Help/release/dev/wix-installscope.rst b/Help/release/dev/wix-installscope.rst new file mode 100644 index 0000000..1978698 --- /dev/null +++ b/Help/release/dev/wix-installscope.rst @@ -0,0 +1,13 @@ +wix-installscope +---------------- + +* The :cpack_gen:`CPack WIX Generator` gained a new variable, + :variable:`CPACK_WIX_INSTALL_SCOPE`, to control the + ``InstallScope`` property of WiX MSI installers. + +* The :cpack_gen:`CPack WIX Generator` now produces WiX MSI installers + that create start menu and uninstall entries for all users by default, + as documented by the :variable:`CPACK_WIX_INSTALL_SCOPE` variable + ``perMachine`` value. Previously, without a custom WiX template, + it produced installers that would only create start menu and uninstall + entries for the current user, even though they install for all users. diff --git a/Help/release/dev/xcode-embed-xpc-services b/Help/release/dev/xcode-embed-xpc-services new file mode 100644 index 0000000..f758285 --- /dev/null +++ b/Help/release/dev/xcode-embed-xpc-services @@ -0,0 +1,6 @@ +xcode-embed-xpc-services +------------------------ + +* The :prop_tgt:`XCODE_EMBED_XPC_SERVICES <XCODE_EMBED_<type>>` target property + was added to tell the :generator:`Xcode` generator what targets to put in + the ``Embed XPC Resources`` build phase. diff --git a/Help/release/index.rst b/Help/release/index.rst index b84bdb4..ea13fdc 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_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst b/Help/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst new file mode 100644 index 0000000..eabda43 --- /dev/null +++ b/Help/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst @@ -0,0 +1,10 @@ +CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX +------------------------------------- + +.. versionadded:: 3.29 + +Command line length limit for autogen targets, i.e. ``moc`` or ``uic``, +that triggers the use of response files on Windows instead of passing all +arguments to the command line. + +By default ``CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX`` is unset. diff --git a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst index 58818f4..b375b77 100644 --- a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst +++ b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst @@ -15,4 +15,14 @@ variables which have been stored in the cache will still be there. In that case it is recommended to remove the cache variables for this package from the cache using the cache editor or :option:`cmake -U`. +Note that this variable can lead to inconsistent results within the project. +Consider the case where a dependency is requested via :command:`find_package` +from two different places within the project. If the first call does not +have the ``REQUIRED`` keyword, it will not find the dependency when +``CMAKE_DISABLE_FIND_PACKAGE_<PackageName>`` is set to true for that +dependency. The project will proceed under the assumption that the dependency +isn't available. If the second call elsewhere in the project *does* have the +``REQUIRED`` keyword, it can succeed. Two different parts of the same project +have then seen opposite results for the same dependency. + See also the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable. diff --git a/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst new file mode 100644 index 0000000..b6c8b76 --- /dev/null +++ b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst @@ -0,0 +1,6 @@ +CMAKE_EXPORT_FIND_PACKAGE_NAME +------------------------------ + +.. versionadded:: 3.29 + +Initializes the value of :prop_tgt:`EXPORT_FIND_PACKAGE_NAME`. diff --git a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst index c2c2609..d78dd15 100644 --- a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst +++ b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst @@ -5,8 +5,8 @@ Suffixes to append when looking for libraries. This specifies what suffixes to add to library names when the :command:`find_library` command looks for libraries. On Windows systems this -is typically ``.lib`` and, depending on the compiler, ``.dll.a``, ``.a`` -(e.g. GCC and Clang), so when it tries to find the ``foo`` library, it will -look for ``[<prefix>]foo.lib`` and/or ``[<prefix>]foo[.dll].a``, depending on -the compiler used and the ``<prefix>`` specified in the -:variable:`CMAKE_FIND_LIBRARY_PREFIXES`. +is typically ``.lib`` and, depending on the compiler, ``.dll.lib``, ``.dll.a``, +``.a`` (e.g. rustc, GCC, or Clang), so when it tries to find the ``foo`` +library, it will look for ``[<prefix>]foo[.dll].lib`` and/or +``[<prefix>]foo[.dll].a``, depending on the compiler used and the ``<prefix>`` +specified in the :variable:`CMAKE_FIND_LIBRARY_PREFIXES`. diff --git a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst index 4855477..b83fa01 100644 --- a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst +++ b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst @@ -48,6 +48,20 @@ Supported pairs are: See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA` and :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR` variables. +``fortran=<compiler>`` + .. versionadded:: 3.29 + + Specify the Fortran compiler to use, among those that integrate with VS. + The value may be one of: + + ``ifort`` + Intel classic Fortran compiler. + + ``ifx`` + Intel oneAPI Fortran compiler. + + See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_FORTRAN` variable. + ``host=<arch>`` Specify the host tools architecture as ``x64`` or ``x86``. Supported by VS 2013 and above. diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst new file mode 100644 index 0000000..c0ae1cd --- /dev/null +++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst @@ -0,0 +1,15 @@ +CMAKE_<LANG>_COMPILER_LINKER +---------------------------- + +.. versionadded:: 3.29 + +The full path to the linker for ``LANG``. + +This is the command that will be used as the ``<LANG>`` linker. + +This variable is not guaranteed to be defined for all linkers or languages. + +.. note:: + This variable is read-only. It must not be set by the user. To select a + specific linker, use the :variable:`CMAKE_LINKER_TYPE` variable or the + :prop_tgt:`LINKER_TYPE` target property. diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst new file mode 100644 index 0000000..d7d028a --- /dev/null +++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst @@ -0,0 +1,21 @@ +CMAKE_<LANG>_COMPILER_LINKER_FRONTEND_VARIANT +--------------------------------------------- + +.. versionadded:: 3.29 + +Identification string of the linker frontend variant. + +Some linkers have multiple, different frontends for accepting command +line options. (For example ``LLCM LLD`` originally only had a frontend +compatible with the ``GNU`` compiler but since its port to Windows +(``lld-link``) it now also supports a frontend compatible with ``MSVC``.) +When CMake detects such a linker it sets this variable to what would have been +the :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID` for the linker whose frontend +it resembles. + +.. note:: + In other words, this variable describes what command line options + and language extensions the linker frontend expects. + + This variable is set for ``GNU``, ``MSVC``, ``MOLD`` and ``AppleClang`` + linkers that have only one frontend variant. diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst new file mode 100644 index 0000000..0607aea --- /dev/null +++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst @@ -0,0 +1,33 @@ +CMAKE_<LANG>_COMPILER_LINKER_ID +------------------------------- + +.. versionadded:: 3.29 + +Linker identification string. + +A short string unique to the linker vendor. Possible values +include: + +=============================== =============================================== +Value Name +=============================== =============================================== +``AppleClang`` Apple Clang +``LLD`` `LLVM LLD`_ +``GNU`` `GNU Binutils - ld linker`_ (also known as + ``bfd``) +``GNUgold`` `GNU Binutils - gold linker`_ +``MSVC`` `Microsoft Visual Studio`_ +``MOLD`` `mold: A Modern Linker`_ or, on Apple, `sold`_ + linker +``AIX`` AIX system linker +``Solaris`` SunOS system linker +=============================== =============================================== + +This variable is not guaranteed to be defined for all linkers or languages. + +.. _LLVM LLD: https://lld.llvm.org +.. _GNU Binutils - ld linker: https://sourceware.org/binutils +.. _GNU Binutils - gold linker: https://sourceware.org/binutils +.. _Microsoft Visual Studio: https://visualstudio.microsoft.com +.. _mold\: A Modern Linker: https://github.com/rui314/mold +.. _sold: https://github.com/bluewhalesystems/sold diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst new file mode 100644 index 0000000..72b0551 --- /dev/null +++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst @@ -0,0 +1,10 @@ +CMAKE_<LANG>_COMPILER_LINKER_VERSION +------------------------------------ + +.. versionadded:: 3.29 + +Linker version string. + +Linker version in major[.minor[.patch[.tweak]]] format. This +variable is not guaranteed to be defined for all linkers or +languages. diff --git a/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst b/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst new file mode 100644 index 0000000..c9b6779 --- /dev/null +++ b/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst @@ -0,0 +1,15 @@ +CMAKE_<LANG>_USING_LINKER_MODE +------------------------------ + +.. versionadded:: 3.29 + +This variable specify what is the type of data stored in variable + :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>`. There are two possible values: + +``FLAG`` + :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds compiler flags. This is + the default. + +``TOOL`` + :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds the path to the linker + tool. diff --git a/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst b/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst new file mode 100644 index 0000000..e4d9fa6 --- /dev/null +++ b/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst @@ -0,0 +1,30 @@ +CMAKE_<LANG>_USING_LINKER_<TYPE> +-------------------------------- + +.. versionadded:: 3.29 + +This variable defines how to specify the linker for the link step for the type +as specified by the variable :variable:`CMAKE_LINKER_TYPE` or the target +property :prop_tgt:`LINKER_TYPE`. It can hold compiler flags for the link step +or directly the linker tool. The type of data is given by the variable +:variable:`CMAKE_<LANG>_USING_LINKER_MODE`. + +For example, to specify the ``LLVM`` linker for ``GNU`` compilers, we have: + +.. code-block:: cmake + + set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld") + +Or on ``Windows`` platform, for ``Clang`` compilers simulating ``MSVC``, we +have: + +.. code-block:: cmake + + set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld-link") + +And for the ``MSVC`` compiler, linker is directly used, so we have: + +.. code-block:: cmake + + set(CMAKE_C_USING_LINKER_LLD "/path/to/lld-link.exe") + set(CMAKE_C_USING_LINKER_MODE TOOL) diff --git a/Help/variable/CMAKE_LINKER_TYPE.rst b/Help/variable/CMAKE_LINKER_TYPE.rst new file mode 100644 index 0000000..17ea947 --- /dev/null +++ b/Help/variable/CMAKE_LINKER_TYPE.rst @@ -0,0 +1,18 @@ +CMAKE_LINKER_TYPE +----------------- + +.. versionadded:: 3.29 + +Specify which linker will be used for the link step. + +.. note:: + It is assumed that the linker specified is fully compatible with the standard + one. CMake will not do any options translation. + +This variable is used to initialize the :prop_tgt:`LINKER_TYPE` target +property when they are created by calls to :command:`add_library` or +:command:`add_executable` commands. It is meaningful only for targets having a +link step. If set, its value is also used by the :command:`try_compile` +command. + +.. include:: LINKER_PREDEFINED_TYPES.txt diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE.rst index 76b9d92..217604f 100644 --- a/Help/variable/CMAKE_PROJECT_INCLUDE.rst +++ b/Help/variable/CMAKE_PROJECT_INCLUDE.rst @@ -3,12 +3,17 @@ CMAKE_PROJECT_INCLUDE .. versionadded:: 3.15 -A CMake language file or module to be included as the last step of all +A CMake language file to be included as the last step of all :command:`project` command calls. This is intended for injecting custom code into project builds without modifying their source. See :ref:`Code Injection` for a more detailed discussion of files potentially included during a :command:`project` call. +.. versionadded:: 3.29 + This variable can be a :ref:`semicolon-separated list <CMake Language Lists>` + of CMake language files to be included sequentially. It can also now refer to + module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake. + See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, and diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst index 9a8c4b5..1dc0241 100644 --- a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst +++ b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst @@ -3,12 +3,17 @@ CMAKE_PROJECT_INCLUDE_BEFORE .. versionadded:: 3.15 -A CMake language file or module to be included as the first step of all +A CMake language file to be included as the first step of all :command:`project` command calls. This is intended for injecting custom code into project builds without modifying their source. See :ref:`Code Injection` for a more detailed discussion of files potentially included during a :command:`project` call. +.. versionadded:: 3.29 + This variable can be a :ref:`semicolon-separated list <CMake Language Lists>` + of CMake language files to be included sequentially. It can also now refer to + module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake. + See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`, :variable:`CMAKE_PROJECT_INCLUDE`, and diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst index 3bb5cd8..6d37c1a 100644 --- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst +++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst @@ -1,12 +1,17 @@ CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE ------------------------------------ -A CMake language file or module to be included as the last step of any +A CMake language file to be included as the last step of any :command:`project` command calls that specify ``<PROJECT-NAME>`` as the project name. This is intended for injecting custom code into project builds without modifying their source. See :ref:`Code Injection` for a more detailed discussion of files potentially included during a :command:`project` call. +.. versionadded:: 3.29 + This variable can be a :ref:`semicolon-separated list <CMake Language Lists>` + of CMake language files to be included sequentially. It can also now refer to + module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake. + See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`, :variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables. diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst index ca584c1..96ddbf6 100644 --- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst +++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst @@ -3,12 +3,17 @@ CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE .. versionadded:: 3.17 -A CMake language file or module to be included as the first step of any +A CMake language file to be included as the first step of any :command:`project` command calls that specify ``<PROJECT-NAME>`` as the project name. This is intended for injecting custom code into project builds without modifying their source. See :ref:`Code Injection` for a more detailed discussion of files potentially included during a :command:`project` call. +.. versionadded:: 3.29 + This variable can be a :ref:`semicolon-separated list <CMake Language Lists>` + of CMake language files to be included sequentially. It can also now refer to + module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake. + See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables. diff --git a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst index 2010b08..54f530e 100644 --- a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst +++ b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst @@ -12,6 +12,10 @@ including things like :variable:`CMAKE_<LANG>_COMPILER`, might not be set. See :ref:`Code Injection` for a more detailed discussion of files potentially included during a :command:`project` call. +.. versionadded:: 3.29 + This variable can also now refer to module names to be found in + :variable:`CMAKE_MODULE_PATH` or builtin to CMake. + This variable is intended for specifying files that perform one-time setup for the build. It provides an injection point for things like configuring package managers, adding logic the user shares between projects (e.g. defining diff --git a/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst index 893f1ae..52bf30a 100644 --- a/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst +++ b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst @@ -11,4 +11,35 @@ turned into ``REQUIRED`` by setting the variable This can be used to assert assumptions about build environment and to ensure the build will fail early if they do not hold. +Note that setting this variable to true breaks some commonly used patterns. +Multiple calls to :command:`find_package` are sometimes used to obtain a +different search order to the default. +For example, projects can force checking a known path for a particular +package first before searching any of the other default search paths: + +.. code:: cmake + + find_package(something PATHS /some/local/path NO_DEFAULT_PATH) + find_package(something) + +In the above, the first call looks for the ``something`` package in a specific +directory. If ``CMAKE_REQUIRE_FIND_PACKAGE_something`` is set to true, then +this first call must succeed, otherwise a fatal error occurs. The second call +never gets a chance to provide a fall-back to using the default search +locations. + +A similar pattern is used even by some of CMake's own Find modules to search +for a config package first: + +.. code:: cmake + + find_package(something CONFIG QUIET) + if(NOT something_FOUND) + # Fall back to searching using typical Find module logic... + endif() + +Again, if ``CMAKE_REQUIRE_FIND_PACKAGE_something`` is true, the first call +must succeed. It effectively means a config package must be found for the +dependency, and the Find module logic is never used. + See also the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable. diff --git a/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst b/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst index e88db36..69c762b 100644 --- a/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst +++ b/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst @@ -9,3 +9,5 @@ built, first the ``all`` target is built, then the installation starts. If ``CMAKE_SKIP_INSTALL_ALL_DEPENDENCY`` is set to ``TRUE``, this dependency is not created, so the installation process will start immediately, independent from whether the project has been completely built or not. + +See also :variable:`CMAKE_SKIP_TEST_ALL_DEPENDENCY`. diff --git a/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst b/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst new file mode 100644 index 0000000..bae8e99 --- /dev/null +++ b/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst @@ -0,0 +1,19 @@ +CMAKE_SKIP_TEST_ALL_DEPENDENCY +------------------------------ + +.. versionadded:: 3.29 + +Control whether the ``test`` target depends on the ``all`` target. + +If this variable is not defined, or is set to ``TRUE``, then the +``test`` (or ``RUN_TESTS``) target does not depend on the +``all`` (or ``ALL_BUILD``) target. When the ``test`` target is built, +e.g., via ``make test``, the test process will start immediately, +regardless of whether the project has been completely built or not. + +If ``CMAKE_SKIP_TEST_ALL_DEPENDENCY`` is explicitly set to ``FALSE``, +then the ``test`` target will depend on the ``all`` target. When the +``test`` target is built, e.g., via ``make test``, the ``all`` target +will be built first, and then the tests will run. + +See also :variable:`CMAKE_SKIP_INSTALL_ALL_DEPENDENCY`. diff --git a/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst b/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst new file mode 100644 index 0000000..5e55d8c --- /dev/null +++ b/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst @@ -0,0 +1,32 @@ +CMAKE_Swift_COMPILATION_MODE +---------------------------- + +.. versionadded:: 3.29 + +Specify how Swift compiles a target. This variable is used to initialize the +:prop_tgt:`Swift_COMPILATION_MODE` property on targets as they are created. + +The allowed values are: + +.. include:: ../prop_tgt/Swift_COMPILATION_MODE-VALUES.txt + +Use :manual:`generator expressions <cmake-generator-expressions(7)>` to support +per-configuration specification. For example, the code: + +.. code-block:: cmake + + set(CMAKE_Swift_COMPILATION_MODE + "$<IF:$<CONFIG:Release>,wholemodule,incremental>") + +sets the default Swift compilation mode to wholemodule mode when building a +release configuration and to incremental mode in other configurations. + +If this variable is not set then the :prop_tgt:`Swift_COMPILATION_MODE` target +property will not be set automatically. If that property is unset then CMake +uses the default value ``incremental`` to build the Swift source files. + +.. note:: + + This property only has effect when policy :policy:`CMP0157` is set to ``NEW`` + prior to the first :command:`project` or :command:`enable_language` command + that enables the Swift language. diff --git a/Help/variable/CMAKE_TEST_LAUNCHER.rst b/Help/variable/CMAKE_TEST_LAUNCHER.rst new file mode 100644 index 0000000..c527188 --- /dev/null +++ b/Help/variable/CMAKE_TEST_LAUNCHER.rst @@ -0,0 +1,16 @@ +CMAKE_TEST_LAUNCHER +------------------- + +.. versionadded:: 3.29 + +This variable is used to specify a launcher for running tests, added +by the :command:`add_test` command, that run an executable target. +If this variable contains a :ref:`semicolon-separated list <CMake Language +Lists>`, then the first value is the command and remaining values are its +arguments. + +This variable can be initialized via an +:envvar:`CMAKE_TEST_LAUNCHER` environment variable. + +It is also used as the default value for the +:prop_tgt:`TEST_LAUNCHER` target property of executables. diff --git a/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst new file mode 100644 index 0000000..e0ecb12 --- /dev/null +++ b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst @@ -0,0 +1,12 @@ +CMAKE_VS_PLATFORM_TOOLSET_FORTRAN +--------------------------------- + +.. versionadded:: 3.29 + +Fortran compiler to be used by Visual Studio projects. + +:ref:`Visual Studio Generators` support selecting among Fortran compilers +whose Visual Studio Integration is installed. The compiler may be specified +by a field in :variable:`CMAKE_GENERATOR_TOOLSET` of the form ``fortran=...``. +CMake provides the selected Fortran compiler in this variable. +The value may be empty if the field was not specified. diff --git a/Help/variable/LINKER_PREDEFINED_TYPES.txt b/Help/variable/LINKER_PREDEFINED_TYPES.txt new file mode 100644 index 0000000..e8cb19f --- /dev/null +++ b/Help/variable/LINKER_PREDEFINED_TYPES.txt @@ -0,0 +1,59 @@ +Linker types are case-sensitive and may only contain letters, numbers and +underscores. Linker types defined in all uppercase are reserved for CMake's own +built-in types. The pre-defined linker types are: + +``DEFAULT`` + This type corresponds to standard linking, essentially equivalent to + not specifying :prop_tgt:`LINKER_TYPE` target property. + +``SYSTEM`` + Use the standard linker delivered by the platform or the standard toolkit + (for example, ``SYSTEM`` imply Microsoft linker for all ``MSVC`` compatible + compilers). This type is supported for the following platforms/compilers: + + * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers. + * All Apple variants for ``AppleClang``, ``Clang`` and ``GNU`` compilers. + * Windows, for ``MSVC``, ``GNU``, ``Clang`` and ``NVIDIA`` compilers. + +``LLD`` + Use the ``LLVM`` linker. This type is supported for the following + platforms/compilers: + + * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers. + * All Apple variants for ``Clang`` and ``AppleClang`` compilers. + * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end and + ``CLang``, ``MSVC`` and ``NVIDIA`` compilers with ``MSVC`` front-end. + +``BFD`` + Use the ``GNU`` linker. This type is supported for the following + platforms/compilers: + + * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers. + * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end. + +``GOLD`` + Supported on Linux platform for ``GNU``, ``Clang``, ``LLVMFlang`` and + ``NVIDIA`` compilers. + +``MOLD`` + Use the `mold linker <https://github.com/rui314/mold>`_. This type is + supported on the following platforms: + + * Linux platform for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` + compilers. + * All Apple variants for ``Clang`` and ``AppleClang`` compilers as an + alias to ``SOLD``. + +``SOLD`` + Use the `sold linker <https://github.com/bluewhalesystems/sold>`_. This type + is only supported on Apple platforms for ``Clang`` and ``AppleClang`` + compilers. + +``APPLE_CLASSIC`` + Use the Apple linker in the classic behavior (i.e. before ``Xcode 15.0``). + This type is only supported on Apple platforms for ``GNU``, ``Clang`` and + ``AppleClang`` compilers. + +``MSVC`` + Use the Microsoft linker. This type is only supported on Windows + platform for ``MSVC`` and ``Clang`` compiler with ``MSVC`` front-end. diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in index 1efd9f5..c7dbfae 100644 --- a/Modules/CMakeASMCompiler.cmake.in +++ b/Modules/CMakeASMCompiler.cmake.in @@ -5,6 +5,12 @@ set(CMAKE_ASM@ASM_DIALECT@_COMPILER_AR "@_CMAKE_ASM_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER "@CMAKE_ASM_COMPILER_LINKER@") +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_ID "@CMAKE_ASM_COMPILER_LINKER_ID@") +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_VERSION @CMAKE_ASM_COMPILER_LINKER_VERSION@) +set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_ASM_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1) diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index 2f0b774..df2a060 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in @@ -26,6 +26,12 @@ set(CMAKE_C_COMPILER_AR "@CMAKE_C_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_C_COMPILER_LINKER "@CMAKE_C_COMPILER_LINKER@") +set(CMAKE_C_COMPILER_LINKER_ID "@CMAKE_C_COMPILER_LINKER_ID@") +set(CMAKE_C_COMPILER_LINKER_VERSION @CMAKE_C_COMPILER_LINKER_VERSION@) +set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@) diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake index 665f309..998e476 100644 --- a/Modules/CMakeCInformation.cmake +++ b/Modules/CMakeCInformation.cmake @@ -67,7 +67,7 @@ if (NOT _INCLUDED_FILE) endif () if(CMAKE_C_SIZEOF_DATA_PTR) - foreach(f ${CMAKE_C_ABI_FILES}) + foreach(f IN LISTS CMAKE_C_ABI_FILES) include(${f}) endforeach() unset(CMAKE_C_ABI_FILES) diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index 3c28c28..3af02a4 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in @@ -72,5 +72,12 @@ set(CMAKE_CUDA_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CUDA_IMPLICIT_LINK_FR @_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT@ set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_CUDA_COMPILER_LINKER "@CMAKE_CUDA_COMPILER_LINKER@") +set(CMAKE_CUDA_COMPILER_LINKER_ID "@CMAKE_CUDA_COMPILER_LINKER_ID@") +set(CMAKE_CUDA_COMPILER_LINKER_VERSION @CMAKE_CUDA_COMPILER_LINKER_VERSION@) +set(CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_AR "@CMAKE_AR@") +set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_MT "@CMAKE_MT@") diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 8b6f82b..4459898 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -27,6 +27,12 @@ set(CMAKE_CXX_COMPILER_AR "@CMAKE_CXX_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_CXX_COMPILER_LINKER "@CMAKE_CXX_COMPILER_LINKER@") +set(CMAKE_CXX_COMPILER_LINKER_ID "@CMAKE_CXX_COMPILER_LINKER_ID@") +set(CMAKE_CXX_COMPILER_LINKER_VERSION @CMAKE_CXX_COMPILER_LINKER_VERSION@) +set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@) @@ -40,7 +46,7 @@ set(CMAKE_CXX_COMPILER_ID_RUN 1) set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm;ccm;cxxm;c++m) set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC) -foreach (lang C OBJC OBJCXX) +foreach (lang IN ITEMS C OBJC OBJCXX) if (CMAKE_${lang}_COMPILER_ID_RUN) foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS) list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension}) diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index 53abf37..3753d18 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake @@ -66,7 +66,7 @@ if (NOT _INCLUDED_FILE) endif () if(CMAKE_CXX_SIZEOF_DATA_PTR) - foreach(f ${CMAKE_CXX_ABI_FILES}) + foreach(f IN LISTS CMAKE_CXX_ABI_FILES) include(${f}) endforeach() unset(CMAKE_CXX_ABI_FILES) @@ -182,7 +182,7 @@ if(NOT CMAKE_SHARED_MODULE_CXX_FLAGS) endif() # Initialize CXX link type selection flags from C versions. -foreach(type SHARED_LIBRARY SHARED_MODULE EXE) +foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE) if(NOT CMAKE_${type}_LINK_STATIC_CXX_FLAGS) set(CMAKE_${type}_LINK_STATIC_CXX_FLAGS ${CMAKE_${type}_LINK_STATIC_C_FLAGS}) diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake index 7eb93e2..e4fa43f 100644 --- a/Modules/CMakeCompilerIdDetection.cmake +++ b/Modules/CMakeCompilerIdDetection.cmake @@ -42,11 +42,6 @@ function(compiler_id_detection outvar lang) # Order is relevant here. For example, compilers which pretend to be # GCC must appear before the actual GCC. - if ("x${lang}" STREQUAL "xCXX") - list(APPEND ordered_compilers - Comeau - ) - endif() list(APPEND ordered_compilers Intel IntelLLVM diff --git a/Modules/CMakeDetermineCompiler.cmake b/Modules/CMakeDetermineCompiler.cmake index 3156ca9..fc0b714 100644 --- a/Modules/CMakeDetermineCompiler.cmake +++ b/Modules/CMakeDetermineCompiler.cmake @@ -15,7 +15,7 @@ macro(_cmake_find_compiler lang) set(_${lang}_COMPILER_LIST "${CMAKE_${lang}_COMPILER_LIST}") set(CMAKE_${lang}_COMPILER_LIST "") # Prefer vendors of compilers from reference languages. - foreach(l ${_languages}) + foreach(l IN LISTS _languages) list(APPEND CMAKE_${lang}_COMPILER_LIST ${_${lang}_COMPILER_NAMES_${CMAKE_${l}_COMPILER_ID}}) endforeach() @@ -33,7 +33,7 @@ macro(_cmake_find_compiler lang) # Look for directories containing compilers of reference languages. set(_${lang}_COMPILER_HINTS "${CMAKE_${lang}_COMPILER_HINTS}") - foreach(l ${_languages}) + foreach(l IN LISTS _languages) if(CMAKE_${l}_COMPILER AND IS_ABSOLUTE "${CMAKE_${l}_COMPILER}") get_filename_component(_hint "${CMAKE_${l}_COMPILER}" PATH) if(IS_DIRECTORY "${_hint}") @@ -99,7 +99,7 @@ macro(_cmake_find_compiler lang) if (CMAKE_${lang}_COMPILER MATCHES "^/usr/bin/(.+)$") _query_xcrun("${CMAKE_MATCH_1}" RESULT_VAR xcrun_result) elseif (CMAKE_${lang}_COMPILER STREQUAL "CMAKE_${lang}_COMPILER-NOTFOUND") - foreach(comp ${CMAKE_${lang}_COMPILER_LIST}) + foreach(comp IN LISTS CMAKE_${lang}_COMPILER_LIST) _query_xcrun("${comp}" RESULT_VAR xcrun_result) if(xcrun_result) break() @@ -120,6 +120,10 @@ macro(_cmake_find_compiler_path lang) # CMAKE_${lang}_COMPILER and the rest as CMAKE_${lang}_COMPILER_ARG1 # Otherwise, preserve any existing CMAKE_${lang}_COMPILER_ARG1 that might # have been saved by CMakeDetermine${lang}Compiler in a previous run. + + # Necessary for Windows paths to avoid improper escaping of backslashes + cmake_path(CONVERT "${CMAKE_${lang}_COMPILER}" TO_CMAKE_PATH_LIST CMAKE_${lang}_COMPILER NORMALIZE) + list(LENGTH CMAKE_${lang}_COMPILER _CMAKE_${lang}_COMPILER_LENGTH) if(_CMAKE_${lang}_COMPILER_LENGTH GREATER 1) set(CMAKE_${lang}_COMPILER_ARG1 "${CMAKE_${lang}_COMPILER}") diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index efc18f9..012a87c 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake @@ -6,6 +6,7 @@ # This is used internally by CMake and should not be included by user # code. +include(${CMAKE_ROOT}/Modules/Internal/CMakeDetermineLinkerId.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseImplicitIncludeInfo.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseLibraryArchitecture.cmake) @@ -19,15 +20,19 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) set(BIN "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_${lang}.bin") set(CMAKE_FLAGS ) set(COMPILE_DEFINITIONS ) + set(LINK_OPTIONS ) if(DEFINED CMAKE_${lang}_VERBOSE_FLAG) - set(CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${CMAKE_${lang}_VERBOSE_FLAG}") + set(LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_FLAG}") set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_FLAG}") endif() if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG) set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}") endif() + if(DEFINED CMAKE_${lang}_VERBOSE_LINK_FLAG) + list(APPEND LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_LINK_FLAG}") + endif() if(lang MATCHES "^(CUDA|HIP)$") - if(CMAKE_${lang}_ARCHITECTURES STREQUAL "native") + if(CMAKE_CUDA_ARCHITECTURES STREQUAL "native") # We are about to detect the native architectures, so we do # not yet know them. Use all architectures during detection. set(CMAKE_${lang}_ARCHITECTURES "all") @@ -39,6 +44,9 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) # from which we might detect implicit link libraries. list(APPEND CMAKE_FLAGS "-DCMAKE_${lang}_STANDARD_LIBRARIES=") endif() + list(JOIN LINK_OPTIONS " " LINK_OPTIONS) + list(APPEND CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${LINK_OPTIONS}") + __TestCompiler_setTryCompileTargetType() # Avoid failing ABI detection on warnings. @@ -53,7 +61,6 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) set(ENV{LC_ALL} C) set(ENV{LC_MESSAGES} C) set(ENV{LANG} C) - try_compile(CMAKE_${lang}_ABI_COMPILED SOURCES ${src} CMAKE_FLAGS ${CMAKE_FLAGS} @@ -146,39 +153,42 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) set(implicit_libs "${CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES}") set(implicit_fwks "${CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES}") else() - # Parse implicit linker information for this language, if available. - set(implicit_dirs "") - set(implicit_objs "") - set(implicit_libs "") - set(implicit_fwks "") - if(CMAKE_${lang}_VERBOSE_FLAG) - CMAKE_PARSE_IMPLICIT_LINK_INFO("${OUTPUT}" implicit_libs implicit_dirs implicit_fwks log - "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}" - COMPUTE_IMPLICIT_OBJECTS implicit_objs - LANGUAGE ${lang}) - message(CONFIGURE_LOG - "Parsed ${lang} implicit link information:\n${log}\n\n") - endif() - # for VS IDE Intel Fortran we have to figure out the - # implicit link path for the fortran run time using - # a try-compile - if("${lang}" MATCHES "Fortran" - AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio") - message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path") - # Build a sample project which reports symbols. - try_compile(IFORT_LIB_PATH_COMPILED - PROJECT IntelFortranImplicit - SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath - BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath - CMAKE_FLAGS - "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}" - OUTPUT_VARIABLE _output) - file(WRITE - "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt" - "${_output}") - include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL) - message(CHECK_PASS "done") - endif() + # Parse implicit linker information for this language, if available. + set(implicit_dirs "") + set(implicit_objs "") + set(implicit_libs "") + set(implicit_fwks "") + set(compute_artifacts COMPUTE_LINKER linker_tool) + if(CMAKE_${lang}_VERBOSE_FLAG) + list(APPEND compute_artifacts COMPUTE_IMPLICIT_LIBS implicit_libs + COMPUTE_IMPLICIT_DIRS implicit_dirs + COMPUTE_IMPLICIT_FWKS implicit_fwks + COMPUTE_IMPLICIT_OBJECTS implicit_objs) + endif() + cmake_parse_implicit_link_info2("${OUTPUT}" log "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}" + ${compute_artifacts} LANGUAGE ${lang}) + message(CONFIGURE_LOG + "Parsed ${lang} implicit link information:\n${log}\n\n") + # for VS IDE Intel Fortran we have to figure out the + # implicit link path for the fortran run time using + # a try-compile + if("${lang}" MATCHES "Fortran" + AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio") + message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path") + # Build a sample project which reports symbols. + try_compile(IFORT_LIB_PATH_COMPILED + PROJECT IntelFortranImplicit + SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath + BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath + CMAKE_FLAGS + "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}" + OUTPUT_VARIABLE _output) + file(WRITE + "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt" + "${_output}") + include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL) + message(CHECK_PASS "done") + endif() endif() # Implicit link libraries cannot be used explicitly for multiple @@ -193,6 +203,12 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) list(REMOVE_ITEM implicit_dirs $ENV{CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES_EXCLUDE}) endif() + set(CMAKE_${lang}_COMPILER_LINKER "${linker_tool}" PARENT_SCOPE) + cmake_determine_linker_id(${lang} "${linker_tool}") + set(CMAKE_${lang}_COMPILER_LINKER_ID "${CMAKE_${lang}_COMPILER_LINKER_ID}" PARENT_SCOPE) + set(CMAKE_${lang}_COMPILER_LINKER_VERSION ${CMAKE_${lang}_COMPILER_LINKER_VERSION} PARENT_SCOPE) + set(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT ${CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT} PARENT_SCOPE) + set(CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES "${implicit_libs}" PARENT_SCOPE) set(CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES "${implicit_dirs}" PARENT_SCOPE) set(CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "${implicit_fwks}" PARENT_SCOPE) diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 4f1eaba..b380aa5 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -174,27 +174,30 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) endif() endif() - # FIXME(LLVMFlang): It does not provide predefines identifying the MSVC ABI or architecture. - # It should be taught to define _MSC_VER and its _M_* architecture flags. if("x${lang}" STREQUAL "xFortran" AND "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xLLVMFlang") - # Parse the target triple to detect information we should later be able - # to get during preprocessing above, once LLVMFlang provides it. + # Parse the target triple to detect information not always available from the preprocessor. if(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-msvc([0-9]+)\\.([0-9]+)") - set(CMAKE_${lang}_SIMULATE_ID "MSVC") + # CMakeFortranCompilerId.F.in does not extract the _MSC_VER minor version. + # We can do better using the version parsed here. set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") - set(arch ${CMAKE_MATCH_1}) - if(arch STREQUAL "x86_64") - set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "x64") - elseif(arch STREQUAL "aarch64") - set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64") - elseif(arch STREQUAL "arm64ec") - set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64EC") - elseif(arch MATCHES "^i[3-9]86$") - set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "X86") - else() - message(FATAL_ERROR "LLVMFlang target architecture unrecognized: ${arch}") + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 18.0) + # LLVMFlang < 18.0 does not provide predefines identifying the MSVC ABI or architecture. + set(CMAKE_${lang}_SIMULATE_ID "MSVC") + set(arch ${CMAKE_MATCH_1}) + if(arch STREQUAL "x86_64") + set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "x64") + elseif(arch STREQUAL "aarch64") + set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64") + elseif(arch STREQUAL "arm64ec") + set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64EC") + elseif(arch MATCHES "^i[3-9]86$") + set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "X86") + else() + message(FATAL_ERROR "LLVMFlang target architecture unrecognized: ${arch}") + endif() + set(MSVC_${lang}_ARCHITECTURE_ID "${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}") endif() - set(MSVC_${lang}_ARCHITECTURE_ID "${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}") elseif(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-gnu") set(CMAKE_${lang}_SIMULATE_ID "GNU") endif() @@ -402,7 +405,13 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS} elseif(lang STREQUAL Fortran) set(v Intel) set(ext vfproj) - set(id_cl ifort.exe) + if(CMAKE_VS_PLATFORM_TOOLSET_FORTRAN) + set(id_cl "${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}.exe") + set(id_UseCompiler "UseCompiler=\"${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}Compiler\"") + else() + set(id_cl ifort.exe) + set(id_UseCompiler "") + endif() elseif(lang STREQUAL CSharp) set(v 10) set(ext csproj) diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake index 392f0f1..613b0c4 100644 --- a/Modules/CMakeDetermineFortranCompiler.cmake +++ b/Modules/CMakeDetermineFortranCompiler.cmake @@ -22,7 +22,7 @@ elseif("${CMAKE_GENERATOR}" MATCHES "Xcode") _cmake_find_compiler_path(Fortran) else() if(NOT CMAKE_Fortran_COMPILER) - # prefer the environment variable CC + # prefer the environment variable FC if(NOT $ENV{FC} STREQUAL "") get_filename_component(CMAKE_Fortran_COMPILER_INIT $ENV{FC} PROGRAM PROGRAM_ARGS CMAKE_Fortran_FLAGS_ENV_INIT) if(CMAKE_Fortran_FLAGS_ENV_INIT) @@ -140,11 +140,11 @@ if(NOT CMAKE_Fortran_COMPILER_ID_RUN) set(CMAKE_Fortran_COMPILER_ID_TOOL_MATCH_INDEX 2) set(_version_info "") - foreach(m MAJOR MINOR PATCH TWEAK) + foreach(m IN ITEMS MAJOR MINOR PATCH TWEAK) set(_COMP "_${m}") string(APPEND _version_info " #if defined(COMPILER_VERSION${_COMP})") - foreach(d 1 2 3 4 5 6 7 8) + foreach(d RANGE 1 8) string(APPEND _version_info " # undef DEC # undef HEX @@ -265,8 +265,8 @@ if("${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "LLVMFla "${log}\n" ) set(_CMAKE_Fortran_IMPLICIT_LINK_INFORMATION_DETERMINED_EARLY 1) - if("x${CMAKE_Fortran_COMPILER_ARCHITECTURE_ID}" STREQUAL "xARM64") - # FIXME(LLVMFlang): It does not add `-defaultlib:` fields to object + if("x${CMAKE_Fortran_COMPILER_ARCHITECTURE_ID}" STREQUAL "xARM64" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 18.0) + # LLVMFlang < 18.0 does not add `-defaultlib:` fields to object # files to specify link dependencies on its runtime libraries. # For now, we add them ourselves. list(APPEND CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES "clang_rt.builtins-aarch64.lib") diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index e12b175..cf270c0 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -60,6 +60,13 @@ endfunction() __resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker") __resolve_tool_path(CMAKE_MT "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool") +macro(__resolve_linker_path __linker_type __name __search_path __doc) + if(NOT CMAKE_LINKER_${__linker_type}) + set( CMAKE_LINKER_${__linker_type} "${__name}") + endif() + __resolve_tool_path(CMAKE_LINKER_${__linker_type} "${__search_path}" "${__doc}") +endmacro() + set(_CMAKE_TOOL_VARS "") # if it's the MS C/CXX compiler, search for link @@ -93,6 +100,10 @@ if(("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" AND list(APPEND _CMAKE_TOOL_VARS LINKER MT AR) + # look-up for possible usable linker + __resolve_linker_path(LINK "link" "${_CMAKE_TOOLCHAIN_LOCATION}" "link Linker") + __resolve_linker_path(LLD "lld-link" "${_CMAKE_TOOLCHAIN_LOCATION}" "lld-link Linker") + elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Open)?Watcom$") set(_CMAKE_LINKER_NAMES "wlink") set(_CMAKE_AR_NAMES "wlib") diff --git a/Modules/CMakeFindPackageMode.cmake b/Modules/CMakeFindPackageMode.cmake index 726e2a2..a6bbcc4 100644 --- a/Modules/CMakeFindPackageMode.cmake +++ b/Modules/CMakeFindPackageMode.cmake @@ -5,10 +5,9 @@ CMakeFindPackageMode -------------------- - - -This file is executed by cmake when invoked with --find-package. It -expects that the following variables are set using -D: +This file is executed by cmake when invoked with +:ref:`--find-package <Find-Package Tool Mode>`. +It expects that the following variables are set using ``-D``: ``NAME`` name of the package diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in index 89a00ab..90c2ad0 100644 --- a/Modules/CMakeFortranCompiler.cmake.in +++ b/Modules/CMakeFortranCompiler.cmake.in @@ -15,6 +15,10 @@ set(CMAKE_AR "@CMAKE_AR@") set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_Fortran_COMPILER_LINKER "@CMAKE_Fortran_COMPILER_LINKER@") +set(CMAKE_Fortran_COMPILER_LINKER_ID "@CMAKE_Fortran_COMPILER_LINKER_ID@") +set(CMAKE_Fortran_COMPILER_LINKER_VERSION @CMAKE_Fortran_COMPILER_LINKER_VERSION@) +set(CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@) diff --git a/Modules/CMakeFortranCompilerId.F.in b/Modules/CMakeFortranCompilerId.F.in index f5c2ab5..a040073 100644 --- a/Modules/CMakeFortranCompilerId.F.in +++ b/Modules/CMakeFortranCompilerId.F.in @@ -240,7 +240,7 @@ #else PRINT *, 'INFO:platform[]' #endif -#if defined(_WIN32) && (defined(__INTEL_COMPILER) || defined(__ICC)) +#if defined(_MSC_VER) # if defined(_M_IA64) PRINT *, 'INFO:arch[IA64]' # elif defined(_M_X64) || defined(_M_AMD64) diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index 0f71c6f..e364755 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake @@ -43,7 +43,7 @@ if (NOT _INCLUDED_FILE) endif () if(CMAKE_Fortran_SIZEOF_DATA_PTR) - foreach(f ${CMAKE_Fortran_ABI_FILES}) + foreach(f IN LISTS CMAKE_Fortran_ABI_FILES) include(${f}) endforeach() unset(CMAKE_Fortran_ABI_FILES) diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 6d5e62a..9d70e03 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in @@ -74,5 +74,11 @@ set(CMAKE_HIP_COMPILER_AR "@CMAKE_HIP_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_HIP_COMPILER_LINKER "@CMAKE_HIP_COMPILER_LINKER@") +set(CMAKE_HIP_COMPILER_LINKER_ID "@CMAKE_HIP_COMPILER_LINKER_ID@") +set(CMAKE_HIP_COMPILER_LINKER_VERSION @CMAKE_HIP_COMPILER_LINKER_VERSION@) +set(CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in index de73645..a8bb0d2 100644 --- a/Modules/CMakeOBJCCompiler.cmake.in +++ b/Modules/CMakeOBJCCompiler.cmake.in @@ -24,6 +24,12 @@ set(CMAKE_OBJC_COMPILER_AR "@CMAKE_OBJC_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_OBJC_COMPILER_LINKER "@CMAKE_OBJC_COMPILER_LINKER@") +set(CMAKE_OBJC_COMPILER_LINKER_ID "@CMAKE_OBJC_COMPILER_LINKER_ID@") +set(CMAKE_OBJC_COMPILER_LINKER_VERSION @CMAKE_OBJC_COMPILER_LINKER_VERSION@) +set(CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@) diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake index 4c697da..8b8f10e 100644 --- a/Modules/CMakeOBJCInformation.cmake +++ b/Modules/CMakeOBJCInformation.cmake @@ -67,7 +67,7 @@ if (NOT _INCLUDED_FILE) endif () if(CMAKE_OBJC_SIZEOF_DATA_PTR) - foreach(f ${CMAKE_OBJC_ABI_FILES}) + foreach(f IN LISTS CMAKE_OBJC_ABI_FILES) include(${f}) endforeach() unset(CMAKE_OBJC_ABI_FILES) diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in index 94d24ff..6a80d50 100644 --- a/Modules/CMakeOBJCXXCompiler.cmake.in +++ b/Modules/CMakeOBJCXXCompiler.cmake.in @@ -25,6 +25,12 @@ set(CMAKE_OBJCXX_COMPILER_AR "@CMAKE_OBJCXX_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") +set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@") +set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@") +set(CMAKE_OBJCXX_COMPILER_LINKER "@CMAKE_OBJCXX_COMPILER_LINKER@") +set(CMAKE_OBJCXX_COMPILER_LINKER_ID "@CMAKE_OBJCXX_COMPILER_LINKER_ID@") +set(CMAKE_OBJCXX_COMPILER_LINKER_VERSION @CMAKE_OBJCXX_COMPILER_LINKER_VERSION@) +set(CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT@) set(CMAKE_MT "@CMAKE_MT@") set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@) @@ -44,7 +50,7 @@ if (CMAKE_OBJC_COMPILER_ID_RUN) endforeach() endif() -foreach (lang C CXX OBJC) +foreach (lang IN ITEMS C CXX OBJC) foreach(extension IN LISTS CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS) if (CMAKE_${lang}_COMPILER_ID_RUN) list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension}) diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake index a6d824f..da1d6c6 100644 --- a/Modules/CMakeOBJCXXInformation.cmake +++ b/Modules/CMakeOBJCXXInformation.cmake @@ -62,7 +62,7 @@ if (NOT _INCLUDED_FILE) endif () if(CMAKE_OBJCXX_SIZEOF_DATA_PTR) - foreach(f ${CMAKE_OBJCXX_ABI_FILES}) + foreach(f IN LISTS CMAKE_OBJCXX_ABI_FILES) include(${f}) endforeach() unset(CMAKE_OBJCXX_ABI_FILES) @@ -178,7 +178,7 @@ if(NOT CMAKE_SHARED_MODULE_OBJCXX_FLAGS) endif() # Initialize OBJCXX link type selection flags from OBJC versions. -foreach(type SHARED_LIBRARY SHARED_MODULE EXE) +foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE) if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS) set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS}) diff --git a/Modules/CMakePackageConfigHelpers.cmake b/Modules/CMakePackageConfigHelpers.cmake index 581e65c..eeab67b 100644 --- a/Modules/CMakePackageConfigHelpers.cmake +++ b/Modules/CMakePackageConfigHelpers.cmake @@ -8,9 +8,6 @@ CMakePackageConfigHelpers Helpers functions for creating config files that can be included by other projects to find and use a package. -Adds the :command:`configure_package_config_file()` and -:command:`write_basic_package_version_file()` commands. - Generating a Package Configuration File ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,6 +187,132 @@ Please note that these files are internal to CMake and you should not call :command:`configure_file()` on them yourself, but they can be used as starting point to create more sophisticated custom ``ConfigVersion.cmake`` files. +Generating an Apple Platform Selection File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: generate_apple_platform_selection_file + + .. versionadded:: 3.29 + + Create an Apple platform selection file: + + .. code-block:: cmake + + generate_apple_platform_selection_file(<filename> + INSTALL_DESTINATION <path> + [INSTALL_PREFIX <path>] + [MACOS_CONFIG_FILE <file>] + [IOS_CONFIG_FILE <file>] + [IOS_SIMULATOR_CONFIG_FILE <file>] + [TVOS_CONFIG_FILE <file>] + [TVOS_SIMULATOR_CONFIG_FILE <file>] + [WATCHOS_CONFIG_FILE <file>] + [WATCHOS_SIMULATOR_CONFIG_FILE <file>] + [VISIONOS_CONFIG_FILE <file>] + [VISIONOS_SIMULATOR_CONFIG_FILE <file>] + ) + + Writes a file for use as ``<PackageName>Config.cmake`` which can include an + Apple-platform-specific ``<PackageName>Config.cmake`` from a different + directory. This can be used in conjunction with the ``XCFRAMEWORK_LOCATION`` + argument of :command:`export(SETUP)` to export packages in a way that a project + built for any Apple platform can use them. + + ``INSTALL_DESTINATION <path>`` + Path to which the file will be installed by the caller, e.g., via + :command:`install(FILES)`. The path may be either relative to the + ``INSTALL_PREFIX`` or absolute. + + ``INSTALL_PREFIX <path>`` + Path prefix to which the package will be installed by the caller. + The ``<path>`` argument must be an absolute path. If this argument + is not passed, the :variable:`CMAKE_INSTALL_PREFIX` variable will be + used instead. + + ``MACOS_CONFIG_FILE <file>`` + File to include if the platform is macOS. + + ``IOS_CONFIG_FILE <file>`` + File to include if the platform is iOS. + + ``IOS_SIMULATOR_CONFIG_FILE <file>`` + File to include if the platform is iOS Simulator. + + ``TVOS_CONFIG_FILE <file>`` + File to include if the platform is tvOS. + + ``TVOS_SIMULATOR_CONFIG_FILE <file>`` + File to include if the platform is tvOS Simulator. + + ``WATCHOS_CONFIG_FILE <file>`` + File to include if the platform is watchOS. + + ``WATCHOS_SIMULATOR_CONFIG_FILE <file>`` + File to include if the platform is watchOS Simulator. + + ``VISIONOS_CONFIG_FILE <file>`` + File to include if the platform is visionOS. + + ``VISIONOS_SIMULATOR_CONFIG_FILE <file>`` + File to include if the platform is visionOS Simulator. + + If any of the optional config files are not specified, and the consuming + project is built for their corresponding platform, an error will be thrown + when including the generated file. + +.. command:: generate_apple_architecture_selection_file + + .. versionadded:: 3.29 + + Create an Apple architecture selection file: + + .. code-block:: cmake + + generate_apple_architecture_selection_file(<filename> + INSTALL_DESTINATION <path> + [INSTALL_PREFIX <path>] + [SINGLE_ARCHITECTURES <archs> + SINGLE_ARCHITECTURE_CONFIG_FILES <files>] + [UNIVERSAL_ARCHITECTURES <archs> + UNIVERSAL_CONFIG_FILE <file>] + ) + + Writes a file for use as ``<PackageName>Config.cmake`` on Apple platforms + which can include an architecture-specific ``<PackageName>Config.cmake`` + from a different directory based on :variable:`CMAKE_OSX_ARCHITECTURES`. + + ``INSTALL_DESTINATION <path>`` + Path to which the file will be installed by the caller, e.g., via + :command:`install(FILES)`. The path may be either relative to the + ``INSTALL_PREFIX`` or absolute. + + ``INSTALL_PREFIX <path>`` + Path prefix to which the package will be installed by the caller. + The ``<path>`` argument must be an absolute path. If this argument + is not passed, the :variable:`CMAKE_INSTALL_PREFIX` variable will be + used instead. + + ``SINGLE_ARCHITECTURES <archs>`` + A :ref:`semicolon-separated list <CMake Language Lists>` of + architectures provided by entries of + ``SINGLE_ARCHITECTURE_CONFIG_FILES``. + + ``SINGLE_ARCHITECTURE_CONFIG_FILES <files>`` + A :ref:`semicolon-separated list <CMake Language Lists>` of + architecture-specific files. One of them will be loaded + when :variable:`CMAKE_OSX_ARCHITECTURES` contains a single + architecture matching the corresponding entry of + ``SINGLE_ARCHITECTURES``. + + ``UNIVERSAL_ARCHITECTURES <archs>`` + A :ref:`semicolon-separated list <CMake Language Lists>` of + architectures provided by the ``UNIVERSAL_CONFIG_FILE``. + + ``UNIVERSAL_CONFIG_FILE <file>`` + A file to load when :variable:`CMAKE_OSX_ARCHITECTURES` contains + a (non-strict) subset of the ``UNIVERSAL_ARCHITECTURES`` and + does not match any one of the ``SINGLE_ARCHITECTURES``. + Example Generating Package Files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,3 +467,130 @@ endmacro() configure_file("${_inputFile}" "${_outputFile}" @ONLY) endfunction() + +function(generate_apple_platform_selection_file _output_file) + set(_config_file_options + MACOS_CONFIG_FILE + IOS_CONFIG_FILE + IOS_SIMULATOR_CONFIG_FILE + TVOS_CONFIG_FILE + TVOS_SIMULATOR_CONFIG_FILE + WATCHOS_CONFIG_FILE + WATCHOS_SIMULATOR_CONFIG_FILE + VISIONOS_CONFIG_FILE + VISIONOS_SIMULATOR_CONFIG_FILE + ) + + set(_options) + set(_single + INSTALL_DESTINATION + INSTALL_PREFIX + ${_config_file_options} + ) + set(_multi) + cmake_parse_arguments(PARSE_ARGV 0 _gpsf "${_options}" "${_single}" "${_multi}") + + if(NOT _gpsf_INSTALL_DESTINATION) + message(FATAL_ERROR "No INSTALL_DESTINATION given to generate_apple_platform_selection_file()") + endif() + if(_gpsf_INSTALL_PREFIX) + set(maybe_INSTALL_PREFIX INSTALL_PREFIX ${_gpsf_INSTALL_PREFIX}) + else() + set(maybe_INSTALL_PREFIX "") + endif() + + set(_have_relative 0) + foreach(_opt IN LISTS _config_file_options) + if(_gpsf_${_opt}) + set(_config_file "${_gpsf_${_opt}}") + if(NOT IS_ABSOLUTE "${_config_file}") + string(PREPEND _config_file [[${PACKAGE_PREFIX_DIR}/]]) + set(_have_relative 1) + endif() + set(_branch_${_opt} "include(\"${_config_file}\")") + else() + set(_branch_${_opt} "message(FATAL_ERROR \"Platform not supported\")") + endif() + endforeach() + + configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/ApplePlatformSelection.cmake.in" "${_output_file}" + INSTALL_DESTINATION "${_gpsf_INSTALL_DESTINATION}" + ${maybe_INSTALL_PREFIX} + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) +endfunction() + +function(generate_apple_architecture_selection_file _output_file) + set(_options) + set(_single + INSTALL_DESTINATION + INSTALL_PREFIX + SINGLE_ARCHITECTURES + SINGLE_ARCHITECTURE_CONFIG_FILES + UNIVERSAL_ARCHITECTURES + UNIVERSAL_CONFIG_FILE + ) + set(_multi) + cmake_parse_arguments(PARSE_ARGV 0 _gasf "${_options}" "${_single}" "${_multi}") + + if(NOT _gasf_INSTALL_DESTINATION) + message(FATAL_ERROR "No INSTALL_DESTINATION given to generate_apple_platform_selection_file()") + endif() + if(_gasf_INSTALL_PREFIX) + set(maybe_INSTALL_PREFIX INSTALL_PREFIX ${_gasf_INSTALL_PREFIX}) + else() + set(maybe_INSTALL_PREFIX "") + endif() + + list(LENGTH _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURES_len) + list(LENGTH _gasf_SINGLE_ARCHITECTURE_CONFIG_FILES _gasf_SINGLE_ARCHITECTURE_CONFIG_FILES_len) + if(NOT _gasf_SINGLE_ARCHITECTURES_len EQUAL _gasf_SINGLE_ARCHITECTURE_CONFIG_FILES_len) + message(FATAL_ERROR "SINGLE_ARCHITECTURES and SINGLE_ARCHITECTURE_CONFIG_FILES do not have the same number of entries.") + endif() + + set(_branch_code "") + + foreach(pair IN ZIP_LISTS _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURE_CONFIG_FILES) + set(arch "${pair_0}") + set(config_file "${pair_1}") + if(NOT IS_ABSOLUTE "${config_file}") + string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]]) + endif() + string(APPEND _branch_code + "\n" + "if(CMAKE_OSX_ARCHITECTURES STREQUAL \"${arch}\")\n" + " include(\"${config_file}\")\n" + " return()\n" + "endif()\n" + ) + endforeach() + + if(_gasf_UNIVERSAL_ARCHITECTURES AND _gasf_UNIVERSAL_CONFIG_FILE) + string(JOIN " " universal_archs "${_gasf_UNIVERSAL_ARCHITECTURES}") + set(config_file "${_gasf_UNIVERSAL_CONFIG_FILE}") + if(NOT IS_ABSOLUTE "${config_file}") + string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]]) + endif() + string(APPEND _branch_code + "\n" + "set(_cmake_apple_archs \"\${CMAKE_OSX_ARCHITECTURES}\")\n" + "list(REMOVE_ITEM _cmake_apple_archs ${universal_archs})\n" + "if(NOT _cmake_apple_archs)\n" + " include(\"${config_file}\")\n" + " return()\n" + "endif()\n" + ) + elseif(_gasf_UNIVERSAL_ARCHITECTURES) + message(FATAL_ERROR "UNIVERSAL_CONFIG_FILE requires UNIVERSAL_ARCHITECTURES") + elseif(_gasf_UNIVERSAL_CONFIG_FILE) + message(FATAL_ERROR "UNIVERSAL_ARCHITECTURES requires UNIVERSAL_CONFIG_FILE") + endif() + + configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/AppleArchitectureSelection.cmake.in" "${_output_file}" + INSTALL_DESTINATION "${_gasf_INSTALL_DESTINATION}" + ${maybe_INSTALL_PREFIX} + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) +endfunction() diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake index cbdb915..f62de4c 100644 --- a/Modules/CMakeParseImplicitLinkInfo.cmake +++ b/Modules/CMakeParseImplicitLinkInfo.cmake @@ -15,6 +15,26 @@ cmake_policy(SET CMP0054 NEW) # compatibility don't break. # function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj_regex) + set(keywordArgs) + set(oneValueArgs LANGUAGE COMPUTE_IMPLICIT_OBJECTS) + set(multiValueArgs ) + cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + cmake_parse_implicit_link_info2("${text}" "${log_var}" "${obj_regex}" + COMPUTE_IMPLICIT_LIBS "${lib_var}" COMPUTE_IMPLICIT_DIRS "${dir_var}" + COMPUTE_IMPLICIT_FWKS "${fwk_var}" ${ARGN}) + + set(${lib_var} "${${lib_var}}" PARENT_SCOPE) + set(${dir_var} "${${dir_var}}" PARENT_SCOPE) + set(${fwk_var} "${${fwk_var}}" PARENT_SCOPE) + set(${log_var} "${${log_var}}" PARENT_SCOPE) + + if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS) + set(${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS} "${${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS}}" PARENT_SCOPE) + endif() +endfunction() + +function(cmake_parse_implicit_link_info2 text log_var obj_regex) set(implicit_libs_tmp "") set(implicit_objs_tmp "") set(implicit_dirs_tmp) @@ -22,25 +42,29 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj set(log "") set(keywordArgs) - set(oneValueArgs COMPUTE_IMPLICIT_OBJECTS LANGUAGE) + set(oneValueArgs LANGUAGE + COMPUTE_IMPLICIT_LIBS COMPUTE_IMPLICIT_DIRS COMPUTE_IMPLICIT_FWKS + COMPUTE_IMPLICIT_OBJECTS COMPUTE_LINKER) set(multiValueArgs ) cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(is_msvc 0) if(EXTRA_PARSE_LANGUAGE AND - ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_ID}" STREQUAL "xMSVC" OR + ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC")) set(is_msvc 1) endif() - # Parse implicit linker arguments. - set(linker "CMAKE_LINKER-NOTFOUND") - if(CMAKE_LINKER) - get_filename_component(linker ${CMAKE_LINKER} NAME) - string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" linker "${linker}") - endif() + set(linker "ld[0-9]*(\\.[a-z]+)?") if(is_msvc) - string(APPEND linker "|link\\.exe|lld-link") + string(APPEND linker "|link\\.exe|lld-link(\\.exe)?") + endif() + if(CMAKE_LINKER) + get_filename_component(default_linker ${CMAKE_LINKER} NAME) + if (NOT default_linker MATCHES "(${linker})") + string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" default_linker "${default_linker}") + list(PREPEND linker "${default_linker}|") + endif() endif() set(startfile "CMAKE_LINK_STARTFILE-NOTFOUND") if(CMAKE_LINK_STARTFILE) @@ -50,9 +74,35 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj # whole line and just the command (argv[0]). set(linker_regex "^( *|.*[/\\])(${linker}|${startfile}|([^/\\]+-)?ld|collect2)[^/\\]*( |$)") set(linker_exclude_regex "collect2 version |^[A-Za-z0-9_]+=|/ldfe ") + set(linker_tool_regex "^[ \t]*(->|exec:|\")?[ \t]*(.*[/\\](${linker}))(\"|,| |$)") + set(linker_tool_exclude_regex "cuda-fake-ld|-fuse-ld=|--with-ld=") + set(linker_tool "NOTFOUND") + set(link_line_parsed 0) string(APPEND log " link line regex: [${linker_regex}]\n") + if(EXTRA_PARSE_COMPUTE_LINKER) + string(APPEND log " linker tool regex: [${linker_tool_regex}]\n") + endif() string(REGEX REPLACE "\r?\n" ";" output_lines "${text}") foreach(line IN LISTS output_lines) + if(EXTRA_PARSE_COMPUTE_LINKER AND + NOT linker_tool AND NOT "${line}" MATCHES "${linker_tool_exclude_regex}" + AND "${line}" MATCHES "${linker_tool_regex}") + set(linker_tool "${CMAKE_MATCH_2}") + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # pick-up last path + string(REGEX REPLACE "^.*([A-Za-z]:[/\\][^:]+)$" "\\1" linker_tool "${linker_tool}") + cmake_path(SET linker_tool "${linker_tool}") + endif() + string(APPEND log " linker tool for '${EXTRA_PARSE_LANGUAGE}': ${linker_tool}\n") + endif() + if(NOT (EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS OR EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS + OR EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS OR EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS)) + if(linker_tool) + break() + else() + continue() + endif() + endif() set(cmd) if("${line}" MATCHES "${linker_regex}" AND NOT "${line}" MATCHES "${linker_exclude_regex}") @@ -86,7 +136,8 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj endif() endif() set(search_static 0) - if("${cmd}" MATCHES "${linker_regex}") + if(NOT link_line_parsed AND "${cmd}" MATCHES "${linker_regex}") + set(link_line_parsed 1) string(APPEND log " link line: [${line}]\n") string(REGEX REPLACE ";-([LYz]);" ";-\\1" args "${args}") set(skip_value_of "") @@ -95,60 +146,78 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj string(APPEND log " arg [${arg}] ==> skip value of ${skip_value_of}\n") set(skip_value_of "") elseif("${arg}" MATCHES "^-L(.:)?[/\\]") - # Unix search path. - string(REGEX REPLACE "^-L" "" dir "${arg}") - list(APPEND implicit_dirs_tmp ${dir}) - string(APPEND log " arg [${arg}] ==> dir [${dir}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS) + # Unix search path. + string(REGEX REPLACE "^-L" "" dir "${arg}") + list(APPEND implicit_dirs_tmp ${dir}) + string(APPEND log " arg [${arg}] ==> dir [${dir}]\n") + endif() elseif("${arg}" MATCHES "^[-/](LIBPATH|libpath):(.+)") - # MSVC search path. - set(dir "${CMAKE_MATCH_2}") - list(APPEND implicit_dirs_tmp ${dir}) - string(APPEND log " arg [${arg}] ==> dir [${dir}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS) + # MSVC search path. + set(dir "${CMAKE_MATCH_2}") + list(APPEND implicit_dirs_tmp ${dir}) + string(APPEND log " arg [${arg}] ==> dir [${dir}]\n") + endif() elseif(is_msvc AND "${arg}" STREQUAL "-link") string(APPEND log " arg [${arg}] ==> ignore MSVC cl option\n") elseif(is_msvc AND "${arg}" MATCHES "^[-/][Ii][Mm][Pp][Ll][Ii][Bb]:") string(APPEND log " arg [${arg}] ==> ignore MSVC link option\n") + elseif(is_msvc AND "${arg}" MATCHES "^[-/][Ww][Hh][Oo][Ll][Ee][Aa][Rr][Cc][Hh][Ii][Vv][Ee]:Fortran_main") + string(APPEND log " arg [${arg}] ==> ignore LLVMFlang program entry point\n") elseif(is_msvc AND "${arg}" MATCHES "^(.*\\.[Ll][Ii][Bb])$") - set(lib "${CMAKE_MATCH_1}") - list(APPEND implicit_libs_tmp ${lib}) - string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + set(lib "${CMAKE_MATCH_1}") + list(APPEND implicit_libs_tmp ${lib}) + string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") + endif() elseif("${arg}" STREQUAL "-lto_library") # ld argument "-lto_library <path>" set(skip_value_of "${arg}") string(APPEND log " arg [${arg}] ==> ignore, skip following value\n") elseif("${arg}" MATCHES "^-l([^:].*)$") - # Unix library. - set(lib "${CMAKE_MATCH_1}") - if(search_static AND lib MATCHES "^(gfortran|stdc\\+\\+)$") - # Search for the static library later, once all link dirs are known. - set(lib "SEARCH_STATIC:${lib}") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + # Unix library. + set(lib "${CMAKE_MATCH_1}") + if(search_static AND lib MATCHES "^(gfortran|stdc\\+\\+)$") + # Search for the static library later, once all link dirs are known. + set(lib "SEARCH_STATIC:${lib}") + endif() + list(APPEND implicit_libs_tmp ${lib}) + string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") endif() - list(APPEND implicit_libs_tmp ${lib}) - string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") elseif("${arg}" MATCHES "^(.:)?[/\\].*\\.a$") - # Unix library full path. - list(APPEND implicit_libs_tmp ${arg}) - string(APPEND log " arg [${arg}] ==> lib [${arg}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + # Unix library full path. + list(APPEND implicit_libs_tmp ${arg}) + string(APPEND log " arg [${arg}] ==> lib [${arg}]\n") + endif() elseif("${arg}" MATCHES "^[-/](DEFAULTLIB|defaultlib):(.+)") - # Windows library. - set(lib "${CMAKE_MATCH_2}") - list(APPEND implicit_libs_tmp ${lib}) - string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + # Windows library. + set(lib "${CMAKE_MATCH_2}") + list(APPEND implicit_libs_tmp ${lib}) + string(APPEND log " arg [${arg}] ==> lib [${lib}]\n") + endif() elseif("${arg}" MATCHES "^(.:)?[/\\].*\\.o$") if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS) list(APPEND implicit_objs_tmp ${arg}) string(APPEND log " arg [${arg}] ==> obj [${arg}]\n") endif() - if(obj_regex AND "${arg}" MATCHES "${obj_regex}") - # Object file full path. - list(APPEND implicit_libs_tmp ${arg}) + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + if(obj_regex AND "${arg}" MATCHES "${obj_regex}") + # Object file full path. + list(APPEND implicit_libs_tmp ${arg}) + endif() endif() elseif("${arg}" MATCHES "^-Y(P,)?[^0-9]") - # Sun search path ([^0-9] avoids conflict with Mac -Y<num>). - string(REGEX REPLACE "^-Y(P,)?" "" dirs "${arg}") - string(REPLACE ":" ";" dirs "${dirs}") - list(APPEND implicit_dirs_tmp ${dirs}) - string(APPEND log " arg [${arg}] ==> dirs [${dirs}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS) + # Sun search path ([^0-9] avoids conflict with Mac -Y<num>). + string(REGEX REPLACE "^-Y(P,)?" "" dirs "${arg}") + string(REPLACE ":" ";" dirs "${dirs}") + list(APPEND implicit_dirs_tmp ${dirs}) + string(APPEND log " arg [${arg}] ==> dirs [${dirs}]\n") + endif() elseif("${arg}" STREQUAL "-Bstatic") set(search_static 1) string(APPEND log " arg [${arg}] ==> search static\n" ) @@ -156,13 +225,17 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj set(search_static 0) string(APPEND log " arg [${arg}] ==> search dynamic\n" ) elseif("${arg}" MATCHES "^-l:") - # HP named library. - list(APPEND implicit_libs_tmp ${arg}) - string(APPEND log " arg [${arg}] ==> lib [${arg}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + # HP named library. + list(APPEND implicit_libs_tmp ${arg}) + string(APPEND log " arg [${arg}] ==> lib [${arg}]\n") + endif() elseif("${arg}" MATCHES "^-z(all|default|weak)extract") - # Link editor option. - list(APPEND implicit_libs_tmp ${arg}) - string(APPEND log " arg [${arg}] ==> opt [${arg}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + # Link editor option. + list(APPEND implicit_libs_tmp ${arg}) + string(APPEND log " arg [${arg}] ==> opt [${arg}]\n") + endif() elseif("${arg}" STREQUAL "cl.exe") string(APPEND log " arg [${arg}] ==> recognize MSVC cl\n") set(is_msvc 1) @@ -170,25 +243,29 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj string(APPEND log " arg [${arg}] ==> ignore\n") endif() endforeach() - break() elseif("${line}" MATCHES "LPATH(=| is:? *)(.*)$") - string(APPEND log " LPATH line: [${line}]\n") - # HP search path. - string(REPLACE ":" ";" paths "${CMAKE_MATCH_2}") - list(APPEND implicit_dirs_tmp ${paths}) - string(APPEND log " dirs [${paths}]\n") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS) + string(APPEND log " LPATH line: [${line}]\n") + # HP search path. + string(REPLACE ":" ";" paths "${CMAKE_MATCH_2}") + list(APPEND implicit_dirs_tmp ${paths}) + string(APPEND log " dirs [${paths}]\n") + endif() else() string(APPEND log " ignore line: [${line}]\n") endif() + if((NOT EXTRA_PARSE_COMPUTE_LINKER OR linker_tool) AND link_line_parsed) + break() + endif() endforeach() # Look for library search paths reported by linker. - if("${output_lines}" MATCHES ";Library search paths:((;\t[^;]+)+)") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS AND "${output_lines}" MATCHES ";Library search paths:((;\t[^;]+)+)") string(REPLACE ";\t" ";" implicit_dirs_match "${CMAKE_MATCH_1}") string(APPEND log " Library search paths: [${implicit_dirs_match}]\n") list(APPEND implicit_dirs_tmp ${implicit_dirs_match}) endif() - if("${output_lines}" MATCHES ";Framework search paths:((;\t[^;]+)+)") + if(EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS AND "${output_lines}" MATCHES ";Framework search paths:((;\t[^;]+)+)") string(REPLACE ";\t" ";" implicit_fwks_match "${CMAKE_MATCH_1}") string(APPEND log " Framework search paths: [${implicit_fwks_match}]\n") list(APPEND implicit_fwks_tmp ${implicit_fwks_match}) @@ -273,11 +350,21 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj string(APPEND log " implicit fwks: [${implicit_fwks}]\n") # Return results. - set(${lib_var} "${implicit_libs}" PARENT_SCOPE) - set(${dir_var} "${implicit_dirs}" PARENT_SCOPE) - set(${fwk_var} "${implicit_fwks}" PARENT_SCOPE) + if(EXTRA_PARSE_COMPUTE_LINKER) + set(${EXTRA_PARSE_COMPUTE_LINKER} "${linker_tool}" PARENT_SCOPE) + endif() + set(${log_var} "${log}" PARENT_SCOPE) + if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) + set(${EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS} "${implicit_libs}" PARENT_SCOPE) + endif() + if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS) + set(${EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS} "${implicit_dirs}" PARENT_SCOPE) + endif() + if(EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS) + set(${EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS} "${implicit_fwks}" PARENT_SCOPE) + endif() if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS) set(${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS} "${implicit_objs}" PARENT_SCOPE) endif() diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 1c6f0df..04d500e 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -68,30 +68,42 @@ set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -libc MD) set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -libc MTd) set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -libc MDd) +set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g") +set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O") +set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g") +set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize") + if(CMAKE_GENERATOR STREQUAL "Xcode") + string(APPEND CMAKE_Swift_FLAGS_DEBUG_INIT " ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}") + string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " ${CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS}") +endif() + +# Warns if unset and uses old policy. +# Old policy flag-smashes the wmo and incremental flags onto the compiler flags. +# New policy respects the Swift_COMPILATION_MODE target property to add +# incremental and wholemodule optimization flags as appropriate. +cmake_policy(GET CMP0157 __SWIFT_COMP_MODE_CMP0157) +if(__SWIFT_COMP_MODE_CMP0157 STREQUAL "NEW") + set(CMAKE_Swift_COMPILATION_MODE_DEFAULT "incremental") +else() # Xcode has a separate Xcode project option (SWIFT_COMPILATION_MODE) used to set # whether compiling with whole-module optimizations or incrementally. Setting # 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 ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}") - set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O") - 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") - set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O") - set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g") - set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize") - - # Enable Whole Module Optimization by default unless the old - # C++ driver is being used, which behaves differently under WMO. - if(NOT CMAKE_Swift_COMPILER_USE_OLD_DRIVER) - string(APPEND CMAKE_Swift_FLAGS_RELEASE_INIT " -wmo") - string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " -wmo") - string(APPEND CMAKE_Swift_FLAGS_MINSIZEREL_INIT " -wmo") + # using the old swift driver. Don't append `-incremental` or `-wmo` to the + # flags in the Xcode generator. + if(NOT CMAKE_GENERATOR STREQUAL "Xcode") + # Enable Whole Module Optimization by default unless the old + # C++ driver is being used, which behaves differently under WMO. + if(NOT CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + string(APPEND CMAKE_Swift_FLAGS_RELEASE_INIT " -wmo") + string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " -wmo") + string(APPEND CMAKE_Swift_FLAGS_MINSIZEREL_INIT " -wmo") + endif() + string(APPEND CMAKE_Swift_FLAGS_DEBUG_INIT " -incremental") endif() endif() +unset(__SWIFT_COMP_MODE_CMP0157) if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") if(NOT DEFINED CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG) @@ -104,36 +116,66 @@ endif() cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags") -# NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step -if(NOT CMAKE_Swift_COMPILE_OBJECT) - set(CMAKE_Swift_COMPILE_OBJECT ":") -endif() - if(NOT CMAKE_Swift_NUM_THREADS MATCHES "^[0-9]+$") cmake_host_system_information(RESULT CMAKE_Swift_NUM_THREADS QUERY NUMBER_OF_LOGICAL_CORES) endif() -if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) - set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>") -endif() +# Swift split-compilation requires CMP0157 NEW policy +if(CMAKE_Swift_COMPILATION_MODE_DEFAULT) + set(CMAKE_Swift_PARALLEL_FLAGS "-j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS}") + if(NOT CMAKE_Swift_COMPILE_OBJECT) + # Omit the object output. The output is controlled by the output-file-map + # for normal builds. For wholemodule builds, CMake appends the appropriate + # flags. + set(CMAKE_Swift_COMPILE_OBJECT "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -c <DEFINES> <FLAGS> <INCLUDES> <SOURCE>") + endif() -if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) - set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) -endif() + if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) + set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-library <CMAKE_SHARED_LIBRARY_Swift_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <SONAME_FLAG> <TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>") + endif() -if(NOT CMAKE_Swift_LINK_EXECUTABLE) - set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>") -endif() + if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) + set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + endif() -if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS) - set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path <SWIFT_MODULE> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") -endif() + if(NOT CMAKE_Swift_LINK_EXECUTABLE) + set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-executable -o <TARGET> <FLAGS> <OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>") + endif() + + if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) + set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -emit-library -static -o <TARGET> <OBJECTS> <LINK_FLAGS>") + set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>") + set(CMAKE_Swift_ARCHIVE_FINISH "") + endif() + unset(CMAKE_Swift_PARALLEL_FLAGS) +else() + # NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step + if(NOT CMAKE_Swift_COMPILE_OBJECT) + set(CMAKE_Swift_COMPILE_OBJECT ":") + endif() + + if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY) + set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>") + endif() -if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) - set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>") + if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) + set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + endif() + + if(NOT CMAKE_Swift_LINK_EXECUTABLE) + set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>") + endif() - set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>") - set(CMAKE_Swift_ARCHIVE_FINISH "") + if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS) + set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path <SWIFT_MODULE> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") + endif() + + if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) + set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>") + + set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>") + set(CMAKE_Swift_ARCHIVE_FINISH "") + endif() endif() set(CMAKE_Swift_INFORMATION_LOADED 1) diff --git a/Modules/CSharpUtilities.cmake b/Modules/CSharpUtilities.cmake index dedb146..b5520ef 100644 --- a/Modules/CSharpUtilities.cmake +++ b/Modules/CSharpUtilities.cmake @@ -64,7 +64,7 @@ Main functions provided by the module Sets source file properties of ``.Designer.cs`` files depending on sibling filenames. Use this, if your CSharp target does **not** use Windows Forms (for Windows Forms use - :command:`csharp_set_designer_cs_properties` instead):: + :command:`csharp_set_windows_forms_properties` instead):: csharp_set_designer_cs_properties([<file1> [<file2> [...]]]) diff --git a/Modules/CheckIPOSupported/main.c b/Modules/CheckIPOSupported/main.c index 5be0864..8d2ab01 100644 --- a/Modules/CheckIPOSupported/main.c +++ b/Modules/CheckIPOSupported/main.c @@ -1,6 +1,6 @@ int foo(); -int main() +int main(void) { return foo(); } diff --git a/Modules/Compiler/ADSP-ASM.cmake b/Modules/Compiler/ADSP-ASM.cmake new file mode 100644 index 0000000..9ef5142 --- /dev/null +++ b/Modules/Compiler/ADSP-ASM.cmake @@ -0,0 +1,6 @@ +include(Compiler/ADSP) +__compiler_adsp(ASM) + +set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS asm) +set(CMAKE_ASM_OUTPUT_EXTENSION ".o" ) +set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> <SOURCE>") diff --git a/Modules/Compiler/ADSP.cmake b/Modules/Compiler/ADSP.cmake index 62566a0..39dcf39 100644 --- a/Modules/Compiler/ADSP.cmake +++ b/Modules/Compiler/ADSP.cmake @@ -10,6 +10,8 @@ macro(__compiler_adsp lang) set(_CMAKE_${lang}_ADSP_FLAGS "-proc=${CMAKE_ADSP_PROCESSOR}") + set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -Mo <DEP_FILE>") + set(CMAKE_${lang}_COMPILE_OBJECT "<CMAKE_${lang}_COMPILER> ${_CMAKE_${lang}_ADSP_FLAGS} <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") diff --git a/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake b/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake deleted file mode 100644 index 2265e5e..0000000 --- a/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake +++ /dev/null @@ -1,7 +0,0 @@ - -set(_compiler_id_pp_test "defined(__COMO__)") - -set(_compiler_id_version_compute " - /* __COMO_VERSION__ = VRR */ -# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__COMO_VERSION__ / 100) -# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__COMO_VERSION__ % 100)") diff --git a/Modules/Compiler/GNU-Fortran.cmake b/Modules/Compiler/GNU-Fortran.cmake index 5dfb03e..452598b 100644 --- a/Modules/Compiler/GNU-Fortran.cmake +++ b/Modules/Compiler/GNU-Fortran.cmake @@ -17,10 +17,6 @@ endif() set(CMAKE_Fortran_POSTPROCESS_FLAG "-fpreprocessed") -# No -DNDEBUG for Fortran. -string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " -Os") -string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3") - # No -isystem for Fortran because it will not find .mod files. unset(CMAKE_INCLUDE_SYSTEM_FLAG_Fortran) diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index d01054b..88c8997 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake @@ -74,36 +74,49 @@ macro(__compiler_gnu lang) if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) ## check if this feature is supported by the linker - execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" -Wl,--help - OUTPUT_VARIABLE _linker_capabilities - ERROR_VARIABLE _linker_capabilities) - if(_linker_capabilities MATCHES "--dependency-file") - set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) + if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD") + execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help + OUTPUT_VARIABLE _linker_capabilities + ERROR_VARIABLE _linker_capabilities) + if(_linker_capabilities MATCHES "--dependency-file") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) + else() + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + unset(_linker_capabilities) else() set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) endif() - unset(_linker_capabilities) endif() endif() if (CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER TRUE) else() - unset(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER) + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) endif() - # For now, due to GNU binutils ld bug when LTO is enabled (see GNU bug - # `30568 <https://sourceware.org/bugzilla/show_bug.cgi?id=30568>`_), - # deactivate this feature. - if (NOT DEFINED CMAKE_LINK_DEPENDS_USE_LINKER) - set(CMAKE_LINK_DEPENDS_USE_LINKER FALSE) + # Due to GNU binutils ld bug when LTO is enabled (see GNU bug + # `30568 <https://sourceware.org/bugzilla/show_bug.cgi?id=30568>`_), + # deactivate this feature if the version is less than 2.41. + # For now, all known versions of gold linker have also this bug. + if (CMAKE_${lang}_COMPILER_LINKER_ID + AND (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNUgold" + OR (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNU" + AND CMAKE_${lang}_COMPILER_LINKER_VERSION VERSION_LESS "2.41"))) + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) endif() # Initial configuration flags. string(APPEND CMAKE_${lang}_FLAGS_INIT " ") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") - string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os -DNDEBUG") - string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG") - string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os") + string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3") + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g") + if(NOT "x${lang}" STREQUAL "xFortran") + string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -DNDEBUG") + endif() set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>") if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4) # work around #4462 diff --git a/Modules/Compiler/LLVMFlang-Fortran.cmake b/Modules/Compiler/LLVMFlang-Fortran.cmake index d27f094..0a432a9 100644 --- a/Modules/Compiler/LLVMFlang-Fortran.cmake +++ b/Modules/Compiler/LLVMFlang-Fortran.cmake @@ -15,6 +15,9 @@ set(CMAKE_Fortran_POSTPROCESS_FLAG "-ffixed-line-length-72") set(CMAKE_Fortran_COMPILE_OPTIONS_TARGET "--target=") +set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,") +set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",") + if(NOT "x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC") set(CMAKE_Fortran_VERBOSE_FLAG "-v") diff --git a/Modules/CompilerId/VS-Intel.vfproj.in b/Modules/CompilerId/VS-Intel.vfproj.in index 044dd20..fdd9d9d 100644 --- a/Modules/CompilerId/VS-Intel.vfproj.in +++ b/Modules/CompilerId/VS-Intel.vfproj.in @@ -13,7 +13,7 @@ Name="Debug|@id_platform@" OutputDirectory="." IntermediateDirectory="$(ConfigurationName)" - > + @id_UseCompiler@> <Tool Name="VFFortranCompilerTool" DebugInformationFormat="debugEnabled" diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 3ec0557..b2816a5 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -1934,8 +1934,10 @@ function(_ep_get_build_command set(args) _ep_get_configure_command_id(${name} cfg_cmd_id) if(cfg_cmd_id STREQUAL "cmake") - # CMake project. Select build command based on generator. - get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR) + # Adding a CMake project as an External Project. Select command based on generator + get_property(cmake_generator TARGET ${name} PROPERTY _EP_CMAKE_GENERATOR) + # cmake_generator is the CMake generator of the ExternalProject target being added + # CMAKE_GENERATOR is the CMake generator of the Current Project if("${CMAKE_GENERATOR}" MATCHES "Make" AND ("${cmake_generator}" MATCHES "Make" OR NOT cmake_generator)) # The project uses the same Makefile generator. Use recursive make. @@ -1948,6 +1950,11 @@ function(_ep_get_build_command endif() else() # Drive the project with "cmake --build". + if(NOT cmake_generator) + # If there is no CMake Generator defined on the ExternalProject, + # use the same Generator as the current project + set(cmake_generator "${CMAKE_GENERATOR}") + endif() get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND) if(cmake_command) set(cmd "${cmake_command}") @@ -1977,7 +1984,11 @@ function(_ep_get_build_command list(APPEND args --config ${config}) endif() if(step STREQUAL "INSTALL") - list(APPEND args --target install) + if("${cmake_generator}" MATCHES "Green Hills MULTI") + list(APPEND args --target INSTALL) + else() + list(APPEND args --target install) + endif() endif() # But for "TEST" drive the project with corresponding "ctest". if("x${step}x" STREQUAL "xTESTx") @@ -3767,6 +3778,9 @@ function(_ep_extract_configure_command var name) list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() list(APPEND cmd "-G${CMAKE_GENERATOR}") + # GreenHills needs to know about the compiler and toolset. + # Be sure to update the similar section in + # FetchContent.cmake:__FetchContent_directPopulate() if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI") set(has_cmake_cache_default_args 1) list(APPEND cmake_cache_default_args diff --git a/Modules/FeatureSummary.cmake b/Modules/FeatureSummary.cmake index 77c66d1..009ca38 100644 --- a/Modules/FeatureSummary.cmake +++ b/Modules/FeatureSummary.cmake @@ -17,7 +17,7 @@ packages and/or feature for a build tree such as:: PNG, A PNG image library., <http://www.libpng.org/pub/png/> * Enables saving screenshots -- The following OPTIONAL packages have not been found: - Lua51, The Lua scripting language., <http://www.lua.org> + Lua51, The Lua scripting language., <https://www.lua.org> * Enables macros in MyWordProcessor Foo, Foo provides cool stuff. diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index 9bbeac0..48cdaf4 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -195,6 +195,12 @@ Commands still be called if :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` is set to ``OPT_IN`` or is not set. + It would not normally be appropriate to specify ``REQUIRED`` as one of + the additional arguments after ``FIND_PACKAGE_ARGS``. Doing so would + mean the :command:`find_package` call must succeed, so none of the other + details specified in the ``FetchContent_Declare()`` call would get a + chance to be used as a fall-back. + Everything after the ``FIND_PACKAGE_ARGS`` keyword is appended to the :command:`find_package` call, so all other ``<contentOptions>`` must come before the ``FIND_PACKAGE_ARGS`` keyword. If the @@ -373,6 +379,10 @@ Commands :command:`FetchContent_Declare`, the ``EXCLUDE_FROM_ALL`` keyword will be added to the :command:`add_subdirectory` command as well. + .. versionadded:: 3.29 + :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` is set to the dependency name + before calling :command:`add_subdirectory`. + Projects should aim to declare the details of all dependencies they might use before they call ``FetchContent_MakeAvailable()`` for any of them. This ensures that if any of the dependencies are also sub-dependencies of @@ -675,6 +685,17 @@ A number of cache variables can influence the behavior where details from a any content details, turning this option ``ON`` can significantly speed up the configure stage. It is ``OFF`` by default. + .. note:: + + The ``FETCHCONTENT_FULLY_DISCONNECTED`` variable is not an appropriate way + to prevent any network access on the first run in a build directory. + Doing so can break projects, lead to misleading error messages, and hide + subtle population failures. This variable is specifically intended to + only be turned on *after* the first time CMake has been run. + If you want to prevent network access even on the first run, use a + :ref:`dependency provider <dependency_providers>` and populate the + dependency from local content instead. + .. variable:: FETCHCONTENT_UPDATES_DISCONNECTED This is a less severe download/update control compared to @@ -1194,10 +1215,10 @@ function(__FetchContent_declareDetails contentName) set(propertyName "_FetchContent_${contentNameLower}_find_package_args") define_property(GLOBAL PROPERTY ${propertyName}) if(NOT __sawQuietKeyword) - list(INSERT __findPackageArgs 0 QUIET) + string(PREPEND __findPackageArgs "QUIET ") endif() if(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL AND NOT __sawGlobalKeyword) - list(APPEND __findPackageArgs GLOBAL) + string(APPEND __findPackageArgs " GLOBAL") endif() cmake_language(EVAL CODE "set_property(GLOBAL PROPERTY ${propertyName} ${__findPackageArgs})" @@ -1596,6 +1617,20 @@ ExternalProject_Add_Step(${contentName}-populate copyfile list(APPEND subCMakeOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") endif() + # GreenHills needs to know about the compiler and toolset to run the + # subbuild commands. Be sure to update the similar section in + # ExternalProject.cmake:_ep_extract_configure_command() + if(CMAKE_GENERATOR MATCHES "Green Hills MULTI") + list(APPEND subCMakeOpts + "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}" + "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}" + "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}" + "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}" + "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}" + "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}" + ) + endif() + # Override the sub-build's configuration types for multi-config generators. # This ensures we are not affected by any custom setting from the project # and can always request a known configuration further below. @@ -1930,6 +1965,10 @@ macro(FetchContent_MakeAvailable) "Trying FETCHCONTENT_MAKEAVAILABLE_SERIAL dependency provider for " "${__cmake_contentName}" ) + + list(APPEND __cmake_fcCurrentVarsStack "__fcprefix__${CMAKE_EXPORT_FIND_PACKAGE_NAME}") + set(CMAKE_EXPORT_FIND_PACKAGE_NAME "${__cmake_contentName}") + # It's still valid if there are no saved details. The project may have # been written to assume a dependency provider is always set and will # provide dependencies without having any declared details for them. @@ -1947,12 +1986,12 @@ macro(FetchContent_MakeAvailable) # This property might be defined but empty. As long as it is defined, # find_package() can be called. get_property(__cmake_addfpargs GLOBAL PROPERTY - _FetchContent_${contentNameLower}_find_package_args + _FetchContent_${__cmake_contentNameLower}_find_package_args DEFINED ) if(__cmake_addfpargs) get_property(__cmake_fpargs GLOBAL PROPERTY - _FetchContent_${contentNameLower}_find_package_args + _FetchContent_${__cmake_contentNameLower}_find_package_args ) string(APPEND __cmake_providerArgs " FIND_PACKAGE_ARGS") foreach(__cmake_item IN LISTS __cmake_fpargs) @@ -1984,6 +2023,12 @@ macro(FetchContent_MakeAvailable) unset(__cmake_item) unset(__cmake_contentDetails) + list(POP_BACK __cmake_fcCurrentVarsStack __cmake_original_export_find_package_name) + string(SUBSTRING "${__cmake_original_export_find_package_name}" + 12 -1 __cmake_original_export_find_package_name + ) + set(CMAKE_EXPORT_FIND_PACKAGE_NAME ${__cmake_original_export_find_package_name}) + FetchContent_GetProperties(${__cmake_contentName}) if(${__cmake_contentNameLower}_POPULATED) continue() @@ -2053,6 +2098,9 @@ macro(FetchContent_MakeAvailable) endif() if(EXISTS ${__cmake_srcdir}/CMakeLists.txt) + list(APPEND __cmake_fcCurrentVarsStack "__fcprefix__${CMAKE_EXPORT_FIND_PACKAGE_NAME}") + set(CMAKE_EXPORT_FIND_PACKAGE_NAME "${__cmake_contentName}") + set(__cmake_add_subdirectory_args ${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR}) if(__cmake_arg_EXCLUDE_FROM_ALL) list(APPEND __cmake_add_subdirectory_args EXCLUDE_FROM_ALL) @@ -2061,6 +2109,12 @@ macro(FetchContent_MakeAvailable) list(APPEND __cmake_add_subdirectory_args SYSTEM) endif() add_subdirectory(${__cmake_add_subdirectory_args}) + + list(POP_BACK __cmake_fcCurrentVarsStack __cmake_original_export_find_package_name) + string(SUBSTRING "${__cmake_original_export_find_package_name}" + 12 -1 __cmake_original_export_find_package_name + ) + set(CMAKE_EXPORT_FIND_PACKAGE_NAME ${__cmake_original_export_find_package_name}) endif() unset(__cmake_srcdir) diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 39a1163..e9b118f 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -746,7 +746,11 @@ if(BLA_VENDOR STREQUAL "OpenBLAS" OR BLA_VENDOR STREQUAL "All") set(_blas_openblas_lib "openblas") if(_blas_sizeof_integer EQUAL 8) - string(APPEND _blas_openblas_lib "64") + if(MINGW) + string(APPEND _blas_openblas_lib "_64") + else() + string(APPEND _blas_openblas_lib "64") + endif() endif() if(NOT BLAS_LIBRARIES) diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake index 2f33dac..5e5f1f8 100644 --- a/Modules/FindCURL.cmake +++ b/Modules/FindCURL.cmake @@ -94,8 +94,10 @@ find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(PC_CURL QUIET libcurl) if(PC_CURL_FOUND) - pkg_get_variable(CURL_SUPPORTED_PROTOCOLS libcurl supported_protocols) - pkg_get_variable(CURL_SUPPORTED_FEATURES libcurl supported_features) + pkg_get_variable(CURL_SUPPORTED_PROTOCOLS_STRING libcurl supported_protocols) + string(REPLACE " " ";" CURL_SUPPORTED_PROTOCOLS "${CURL_SUPPORTED_PROTOCOLS_STRING}") + pkg_get_variable(CURL_SUPPORTED_FEATURES_STRING libcurl supported_features) + string(REPLACE " " ";" CURL_SUPPORTED_FEATURES "${CURL_SUPPORTED_FEATURES_STRING}") endif() endif() diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index 4d3ab5a..e142516 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -487,7 +487,11 @@ if(NOT LAPACK_NOT_FOUND_MESSAGE) set(_lapack_openblas_lib "openblas") if(_lapack_sizeof_integer EQUAL 8) - string(APPEND _lapack_openblas_lib "64") + if(MINGW) + string(APPEND _lapack_openblas_lib "_64") + else() + string(APPEND _lapack_openblas_lib "64") + endif() endif() check_lapack_libraries( diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake index 1527c31..5c4839c 100644 --- a/Modules/FindOpenGL.cmake +++ b/Modules/FindOpenGL.cmake @@ -177,9 +177,24 @@ GLVND. For non-GLVND Linux and other systems these are left undefined. macOS-Specific ^^^^^^^^^^^^^^ -On OSX FindOpenGL defaults to using the framework version of OpenGL. People -will have to change the cache values of OPENGL_glu_LIBRARY and -OPENGL_gl_LIBRARY to use OpenGL with X11 on OSX. +On macOS this module defaults to using the macOS-native framework +version of OpenGL. To use the X11 version of OpenGL on macOS, one +can disable searching of frameworks. For example: + +.. code-block:: cmake + + find_package(X11) + if(APPLE AND X11_FOUND) + set(CMAKE_FIND_FRAMEWORK NEVER) + find_package(OpenGL) + unset(CMAKE_FIND_FRAMEWORK) + else() + find_package(OpenGL) + endif() + +An end user building this project may need to point CMake at their +X11 installation, e.g., with ``-DOpenGL_ROOT=/opt/X11``. + #]=======================================================================] set(_OpenGL_REQUIRED_VARS OPENGL_gl_LIBRARY) @@ -207,11 +222,14 @@ if (WIN32) OPENGL_glu_LIBRARY ) elseif (APPLE) - # The OpenGL.framework provides both gl and glu - find_library(OPENGL_gl_LIBRARY OpenGL DOC "OpenGL library for OS X") - find_library(OPENGL_glu_LIBRARY OpenGL DOC - "GLU library for OS X (usually same as OpenGL library)") - find_path(OPENGL_INCLUDE_DIR OpenGL/gl.h DOC "Include for OpenGL on OS X") + # The OpenGL.framework provides both gl and glu in OpenGL + # XQuartz provides libgl and libglu + find_library(OPENGL_gl_LIBRARY NAMES OpenGL GL DOC + "OpenGL GL library") + find_library(OPENGL_glu_LIBRARY NAMES OpenGL GLU DOC + "OpenGL GLU library") + find_path(OPENGL_INCLUDE_DIR NAMES OpenGL/gl.h GL/gl.h DOC + "Include for OpenGL") list(APPEND _OpenGL_REQUIRED_VARS OPENGL_INCLUDE_DIR) list(APPEND _OpenGL_CACHE_VARS diff --git a/Modules/FindOpenThreads.cmake b/Modules/FindOpenThreads.cmake index bc45eea..6be3422 100644 --- a/Modules/FindOpenThreads.cmake +++ b/Modules/FindOpenThreads.cmake @@ -5,26 +5,32 @@ FindOpenThreads --------------- - - OpenThreads is a C++ based threading library. Its largest userbase seems to OpenSceneGraph so you might notice I accept OSGDIR as an -environment path. I consider this part of the Findosg* suite used to +environment path. I consider this part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. -Locate OpenThreads This module defines OPENTHREADS_LIBRARY -OPENTHREADS_FOUND, if false, do not try to link to OpenThreads -OPENTHREADS_INCLUDE_DIR, where to find the headers +This module defines: + +``OPENTHREADS_LIBRARY`` + +``OPENTHREADS_FOUND`` + if false, do not try to link to OpenThreads +``OPENTHREADS_INCLUDE_DIR`` + where to find the headers + +``$OPENTHREADS_DIR`` is an environment variable that would correspond to the:: + + ./configure --prefix=$OPENTHREADS_DIR -$OPENTHREADS_DIR is an environment variable that would correspond to -the ./configure --prefix=$OPENTHREADS_DIR used in building osg. +used in building osg. -[CMake 2.8.10]: The CMake variables OPENTHREADS_DIR or OSG_DIR can now -be used as well to influence detection, instead of needing to specify -an environment variable. +.. versionadded:: 2.8.10 -Created by Eric Wing. + The CMake variables ``OPENTHREADS_DIR`` or ``OSG_DIR`` can now + be used as well to influence detection, instead of needing to specify + an environment variable. #]=======================================================================] # Header files are presumed to be included like diff --git a/Modules/FindPerl.cmake b/Modules/FindPerl.cmake index 26962df..49bc54c 100644 --- a/Modules/FindPerl.cmake +++ b/Modules/FindPerl.cmake @@ -5,15 +5,20 @@ FindPerl -------- -Find perl +Find a Perl interpreter. -this module looks for Perl +This module defines the following variables: -:: +``PERL_EXECUTABLE`` + The full path to Perl. - PERL_EXECUTABLE - the full path to perl - PERL_FOUND - If false, don't attempt to use perl. - PERL_VERSION_STRING - version of perl found (since CMake 2.8.8) +``PERL_FOUND`` + True if the Perl executable was found. + +``PERL_VERSION_STRING`` + .. versionadded:: 2.8.8 + + The version of Perl found. #]=======================================================================] include(${CMAKE_CURRENT_LIST_DIR}/FindCygwin.cmake) diff --git a/Modules/FindwxWidgets.cmake b/Modules/FindwxWidgets.cmake index 78fa481..b42a85e 100644 --- a/Modules/FindwxWidgets.cmake +++ b/Modules/FindwxWidgets.cmake @@ -37,7 +37,7 @@ select a configuration): wxWidgets_EXCLUDE_COMMON_LIBRARIES - Set to TRUE to exclude linking of commonly required libs (e.g., png tiff - jpeg zlib regex expat). + jpeg zlib regex expat scintilla lexilla). @@ -188,6 +188,9 @@ macro(DBG_MSG_V _MSG) # "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}") endmacro() +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + # Clear return values in case the module is loaded more than once. set(wxWidgets_FOUND FALSE) set(wxWidgets_INCLUDE_DIRS "") @@ -244,8 +247,14 @@ macro(wx_extract_version) "\\2" wxWidgets_VERSION_MINOR "${_wx_version_h}" ) string(REGEX REPLACE "^(.*\n)?#define +wxRELEASE_NUMBER +([0-9]+).*" "\\2" wxWidgets_VERSION_PATCH "${_wx_version_h}" ) + string(REGEX REPLACE "^(.*\n)?#define +wxSUBRELEASE_NUMBER +([0-9]+).*" + "\\2" wxWidgets_VERSION_TWEAK "${_wx_version_h}" ) + set(wxWidgets_VERSION_STRING "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}.${wxWidgets_VERSION_PATCH}" ) + if(${wxWidgets_VERSION_TWEAK} GREATER 0) + string(APPEND wxWidgets_VERSION_STRING ".${wxWidgets_VERSION_TWEAK}") + endif() dbg_msg("wxWidgets_VERSION_STRING: ${wxWidgets_VERSION_STRING}") endmacro() @@ -265,6 +274,9 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") # Useful common wx libs needed by almost all components. set(wxWidgets_COMMON_LIBRARIES png tiff jpeg zlib regex expat) + # Libraries needed by stc component + set(wxWidgets_STC_LIBRARIES scintilla lexilla) + # DEPRECATED: Use find_package(wxWidgets COMPONENTS mono) instead. if(NOT wxWidgets_FIND_COMPONENTS) if(wxWidgets_USE_MONOLITHIC) @@ -277,10 +289,15 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") # Add the common (usually required libs) unless # wxWidgets_EXCLUDE_COMMON_LIBRARIES has been set. if(NOT wxWidgets_EXCLUDE_COMMON_LIBRARIES) - list(APPEND wxWidgets_FIND_COMPONENTS - ${wxWidgets_COMMON_LIBRARIES}) + if(stc IN_LIST wxWidgets_FIND_COMPONENTS) + list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_STC_LIBRARIES}) + endif() + list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_COMMON_LIBRARIES}) endif() + # Remove duplicates, for example when user has specified common libraries. + list(REMOVE_DUPLICATES wxWidgets_FIND_COMPONENTS) + #------------------------------------------------------------------- # WIN32: Helper MACROS #------------------------------------------------------------------- @@ -312,7 +329,7 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") # FIXME: What if both regex libs are available. regex should be # found outside the loop and only wx${LIB}${_UCD}${_DBG}. # Find wxWidgets common libraries. - foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla) + foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES}) find_library(WX_${LIB}${_DBG} NAMES wx${LIB}${_UCD}${_DBG} # for regex @@ -371,7 +388,7 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") # Clear all debug or release library paths (arguments are "d" or ""). macro(WX_CLEAR_ALL_LIBS _DBG) # Clear wxWidgets common libraries. - foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla) + foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES}) WX_CLEAR_LIB(WX_${LIB}${_DBG}) endforeach() @@ -441,12 +458,15 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") endif() DBG_MSG_V("OpenGL") - list(FIND ${_LIBS} gl WX_USE_GL) - if(NOT WX_USE_GL EQUAL -1) + if(gl IN_LIST ${_LIBS}) DBG_MSG_V("- is required.") list(APPEND wxWidgets_LIBRARIES opengl32 glu32) endif() + if(stc IN_LIST ${_LIBS}) + list(APPEND wxWidgets_LIBRARIES imm32) + endif() + list(APPEND wxWidgets_LIBRARIES winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32) endmacro() @@ -458,6 +478,9 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") foreach(version ${wx_versions}) foreach(patch RANGE 15 0 -1) list(APPEND wx_paths "wxWidgets-${version}.${patch}") + foreach(tweak RANGE 3 1 -1) + list(APPEND wx_paths "wxWidgets-${version}.${patch}.${tweak}") + endforeach() endforeach() endforeach() @@ -937,35 +960,38 @@ if(wxWidgets_FIND_STYLE STREQUAL "unix") endif() unset(_cygpath_exe CACHE) endif() -endif() -# Check that all libraries are present, as wx-config does not check it -set(_wx_lib_missing "") -foreach(_wx_lib_ ${wxWidgets_LIBRARIES}) - if("${_wx_lib_}" MATCHES "^-l(.*)") - set(_wx_lib_name "${CMAKE_MATCH_1}") - unset(_wx_lib_found CACHE) - find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS}) - if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND) - list(APPEND _wx_lib_missing ${_wx_lib_name}) - endif() - unset(_wx_lib_found CACHE) - endif() -endforeach() + # Check that all libraries are present, as wx-config does not check it + set(_wx_lib_missing "") + foreach(_wx_lib_ ${wxWidgets_LIBRARIES}) + if("${_wx_lib_}" MATCHES "^-l(.*)") + set(_wx_lib_name "${CMAKE_MATCH_1}") + unset(_wx_lib_found CACHE) + find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS}) + if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND) + list(APPEND _wx_lib_missing ${_wx_lib_name}) + endif() + unset(_wx_lib_found CACHE) + endif() + endforeach() -if (_wx_lib_missing) - string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}") - DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}") - set(wxWidgets_FOUND FALSE) - unset(wxWidgets_LIBRARIES) + if (_wx_lib_missing) + string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}") + DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}") + set(wxWidgets_FOUND FALSE) + unset(wxWidgets_LIBRARIES) + endif() + unset(_wx_lib_missing) endif() -unset(_wx_lib_missing) # Check if a specific version was requested by find_package(). if(wxWidgets_FOUND) wx_extract_version() endif() +file(TO_CMAKE_PATH "${wxWidgets_INCLUDE_DIRS}" wxWidgets_INCLUDE_DIRS) +file(TO_CMAKE_PATH "${wxWidgets_LIBRARY_DIRS}" wxWidgets_LIBRARY_DIRS) + # Debug output: DBG_MSG("wxWidgets_FOUND : ${wxWidgets_FOUND}") DBG_MSG("wxWidgets_INCLUDE_DIRS : ${wxWidgets_INCLUDE_DIRS}") @@ -1215,3 +1241,5 @@ function(WXWIDGETS_ADD_RESOURCES _outfiles) set(${_outfiles} ${${_outfiles}} PARENT_SCOPE) endfunction() + +cmake_policy(POP) diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index 57a7476..3c12e14 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -500,7 +500,7 @@ function(gtest_discover_tests TARGET) -D "CTEST_FILE=${ctest_tests_file}" -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}" -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}" - -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}" + -P "${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake" VERBATIM ) @@ -526,7 +526,7 @@ function(gtest_discover_tests TARGET) " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" - " include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")" "\n" + " include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" " gtest_discover_tests_impl(" "\n" " TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n" " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" @@ -573,9 +573,5 @@ endfunction() ############################################################################### -set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT - ${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake -) - # Restore project's policies cmake_policy(POP) diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake index de0f7d6..eea267d 100644 --- a/Modules/GoogleTestAddTests.cmake +++ b/Modules/GoogleTestAddTests.cmake @@ -112,6 +112,7 @@ function(gtest_discover_tests_impl) message(FATAL_ERROR "Error running test executable.\n" " Path: '${path}'\n" + " Working directory: '${_TEST_WORKING_DIR}'\n" " Result: ${result}\n" " Output:\n" " ${output}\n" diff --git a/Modules/Internal/AppleArchitectureSelection.cmake.in b/Modules/Internal/AppleArchitectureSelection.cmake.in new file mode 100644 index 0000000..fec0d41 --- /dev/null +++ b/Modules/Internal/AppleArchitectureSelection.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +if(NOT CMAKE_OSX_ARCHITECTURES) + message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES must be explicitly set for this package") +endif() +@_branch_code@ + +message(FATAL_ERROR "Architecture not supported") diff --git a/Modules/Internal/ApplePlatformSelection.cmake.in b/Modules/Internal/ApplePlatformSelection.cmake.in new file mode 100644 index 0000000..493d650 --- /dev/null +++ b/Modules/Internal/ApplePlatformSelection.cmake.in @@ -0,0 +1,24 @@ +@PACKAGE_INIT@ + +string(TOLOWER "${CMAKE_OSX_SYSROOT}" _CMAKE_OSX_SYSROOT_LOWER) +if(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)iphonesimulator") + @_branch_IOS_SIMULATOR_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)iphoneos") + @_branch_IOS_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)appletvsimulator") + @_branch_TVOS_SIMULATOR_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)appletvos") + @_branch_TVOS_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)watchsimulator") + @_branch_WATCHOS_SIMULATOR_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)watchos") + @_branch_WATCHOS_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xrsimulator") + @_branch_VISIONOS_SIMULATOR_CONFIG_FILE@ +elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xros") + @_branch_VISIONOS_CONFIG_FILE@ +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + @_branch_MACOS_CONFIG_FILE@ +else() + message(FATAL_ERROR "Platform not supported") +endif() diff --git a/Modules/Internal/CMakeDetermineLinkerId.cmake b/Modules/Internal/CMakeDetermineLinkerId.cmake new file mode 100644 index 0000000..05c83d2 --- /dev/null +++ b/Modules/Internal/CMakeDetermineLinkerId.cmake @@ -0,0 +1,104 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Function to identify the linker. This is used internally by CMake and should +# not be included by user code. +# If successful, sets CMAKE_<lang>_COMPILER_LINKER_ID and +# CMAKE_<lang>_COMPILER_LINKER_VERSION + +cmake_policy(PUSH) +cmake_policy(SET CMP0053 NEW) +cmake_policy(SET CMP0054 NEW) + +function(cmake_determine_linker_id lang linker) + if (NOT linker) + # linker was not identified + unset(CMAKE_${lang}_COMPILER_LINKER_ID PARENT_SCOPE) + unset(CMAKE_${lang}_COMPILER_LINKER_VERSION PARENT_SCOPE) + unset(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT PARENT_SCOPE) + return() + endif() + + set(linker_id) + set(linker_frontend) + set(linker_version) + + # Compute the linker ID and version. + foreach(flags IN ITEMS + "-v" # AppleClang, GNU, GNUgold, MOLD + "-V" # AIX, Solaris + "--version" # LLD + ) + execute_process(COMMAND "${linker}" ${flags} + OUTPUT_VARIABLE linker_desc + ERROR_VARIABLE linker_desc + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND linker_desc MATCHES "@\\(#\\)PROGRAM:ld.+PROJECT:[a-z0-9]+-([0-9.]+).+") + set(linker_id "AppleClang") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif(linker_desc MATCHES "mold \\(sold\\) ([0-9.]+)") + set(linker_id "MOLD") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif(linker_desc MATCHES "mold ([0-9.]+)") + set(linker_id "MOLD") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif(linker_desc MATCHES "LLD ([0-9.]+)") + set(linker_id "LLD") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + if(WIN32 AND NOT linker_desc MATCHES "compatible with GNU") + set(linker_frontend "MSVC") + endif() + break() + elseif(linker_desc MATCHES "GNU ld \\([^)]+\\) ([0-9.]+)") + set(linker_id "GNU") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif(linker_desc MATCHES "GNU gold \\([^)]+\\) ([0-9.]+)") + set(linker_id "GNUgold") + set(linker_frontend "GNU") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif(linker_desc MATCHES "Microsoft \\(R\\) Incremental Linker Version ([0-9.]+)") + set(linker_id "MSVC") + set(linker_frontend "MSVC") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND linker_desc MATCHES "Solaris Link Editors: ([0-9.-]+)") + set(linker_id "Solaris") + set(linker_version "${CMAKE_MATCH_1}") + break() + elseif (CMAKE_SYSTEM_NAME STREQUAL "AIX" AND linker_desc MATCHES " LD ([0-9.]+)") + set(linker_id "AIX") + set(linker_version "${CMAKE_MATCH_1}") + break() + endif() + endforeach() + if(NOT linker_id) + # unknown linker + set(linker_id "UNKNOWN") + endif() + + set(CMAKE_${lang}_COMPILER_LINKER_ID "${linker_id}" PARENT_SCOPE) + if (linker_frontend) + set(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT "${linker_frontend}" PARENT_SCOPE) + else() + unset(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT PARENT_SCOPE) + endif() + if (linker_version) + set(CMAKE_${lang}_COMPILER_LINKER_VERSION "${linker_version}" PARENT_SCOPE) + else() + unset(CMAKE_${lang}_COMPILER_LINKER_VERSION PARENT_SCOPE) + endif() +endfunction() + +cmake_policy(POP) diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 38e32c2..55a621c 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -710,7 +710,7 @@ function(cpack_deb_prepare_package_vars) "${CPACK_DEBIAN_PACKAGE_NAME}-dbgsym_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.ddeb") else() if(NOT CPACK_DEBIAN_FILE_NAME MATCHES ".*\\.(deb|ipk)") - message(FATAL_ERROR "'${CPACK_DEBIAN_FILE_NAME}' is not a valid DEB package file name as it must end with '.deb' or '.ipk'!") + set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb") endif() set(CPACK_OUTPUT_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}") diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake index ace2c6b..85c0a93 100644 --- a/Modules/Internal/CPack/CPackRPM.cmake +++ b/Modules/Internal/CPack/CPackRPM.cmake @@ -1577,7 +1577,7 @@ ${TMP_DEBUGINFO_ADDITIONAL_SOURCES} if(NOT CPACK_RPM_FILE_NAME STREQUAL "RPM-DEFAULT") if(CPACK_RPM_FILE_NAME) if(NOT CPACK_RPM_FILE_NAME MATCHES ".*\\.rpm") - message(FATAL_ERROR "'${CPACK_RPM_FILE_NAME}' is not a valid RPM package file name as it must end with '.rpm'!") + set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm") endif() else() # old file name format for back compatibility diff --git a/Modules/Internal/CPack/CPackWIX.cmake b/Modules/Internal/CPack/CPackWIX.cmake index d1875f2..5fe772e 100644 --- a/Modules/Internal/CPack/CPackWIX.cmake +++ b/Modules/Internal/CPack/CPackWIX.cmake @@ -18,3 +18,7 @@ find_program(CPACK_WIX_LIGHT_EXECUTABLE light if(NOT CPACK_WIX_LIGHT_EXECUTABLE) message(FATAL_ERROR "Could not find the WiX light executable.") endif() + +if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) + set(CPACK_WIX_INSTALL_SCOPE "perMachine") +endif() diff --git a/Modules/Internal/CPack/WIX.template.in b/Modules/Internal/CPack/WIX.template.in index c0bf935..fdd8278 100644 --- a/Modules/Internal/CPack/WIX.template.in +++ b/Modules/Internal/CPack/WIX.template.in @@ -12,7 +12,12 @@ Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)"> + + <?if $(var.CPACK_WIX_INSTALL_SCOPE) = "NONE" ?> <Package InstallerVersion="301" Compressed="yes"/> + <?else?> + <Package InstallerVersion="301" Compressed="yes" InstallScope="$(var.CPACK_WIX_INSTALL_SCOPE)"/> + <?endif?> <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/> diff --git a/Modules/Platform/ADSP-Common.cmake b/Modules/Platform/ADSP-Common.cmake index 2ba90b2..7ec8a6f 100644 --- a/Modules/Platform/ADSP-Common.cmake +++ b/Modules/Platform/ADSP-Common.cmake @@ -9,9 +9,9 @@ macro(__platform_adsp_init) set(CMAKE_ADSP_PROCESSOR "ADSP-${CMAKE_SYSTEM_PROCESSOR}") string(TOUPPER "${CMAKE_ADSP_PROCESSOR}" CMAKE_ADSP_PROCESSOR) - set(CMAKE_ADSP_COMPILER_NAME cc21k.exe) + set(CMAKE_ADSP_COMPILER_NAME "cc21k${CMAKE_EXECUTABLE_SUFFIX}") if(CMAKE_ADSP_PROCESSOR MATCHES "^ADSP-BF") - set(CMAKE_ADSP_COMPILER_NAME ccblkfn.exe) + set(CMAKE_ADSP_COMPILER_NAME "ccblkfn${CMAKE_EXECUTABLE_SUFFIX}") endif() set(CMAKE_ADSP_PLATFORM_INITIALIZED TRUE) @@ -20,7 +20,12 @@ endmacro() macro(__platform_adsp lang) __platform_adsp_init() - set(CMAKE_${lang}_COMPILER "${CMAKE_ADSP_ROOT}/${CMAKE_ADSP_COMPILER_NAME}") + find_program( + CMAKE_${lang}_COMPILER + "${CMAKE_ADSP_COMPILER_NAME}" + PATHS "${CMAKE_ADSP_ROOT}" + REQUIRED + ) execute_process( COMMAND "${CMAKE_${lang}_COMPILER}" "-proc=${CMAKE_ADSP_PROCESSOR}" "-version" diff --git a/Modules/Platform/ADSP-Determine.cmake b/Modules/Platform/ADSP-Determine.cmake index 6ccf1ea..1588c92 100644 --- a/Modules/Platform/ADSP-Determine.cmake +++ b/Modules/Platform/ADSP-Determine.cmake @@ -21,6 +21,6 @@ endif() if(NOT CMAKE_ADSP_ROOT) _find_adsp_root("C:/Program Files (x86)/Analog Devices/VisualDSP *") endif() -if(NOT IS_DIRECTORY "${CMAKE_ADSP_ROOT}") - message(FATAL_ERROR "ADSP: could not find CCES/VDSP++ install directory ${CMAKE_ADSP_ROOT}") +if(NOT CMAKE_ADSP_ROOT) + _find_adsp_root("/opt/analog/cces *") endif() diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index a9aa8e0..55a6680 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake @@ -14,8 +14,11 @@ macro(__aix_compiler_gnu lang) string(APPEND CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS " -Wl,-bnoipath") set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall") # CMP0065 old behavior set(CMAKE_${lang}_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH 1) + set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) + if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1) unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) endif() diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake index 902cbb3..c225de9 100644 --- a/Modules/Platform/AIX-XL.cmake +++ b/Modules/Platform/AIX-XL.cmake @@ -17,6 +17,7 @@ macro(__aix_compiler_xl lang) set(CMAKE_SHARED_MODULE_${lang}_FLAGS " ") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) set(_OBJECTS " <OBJECTS>") if(DEFINED CMAKE_XL_CreateExportList AND CMAKE_XL_CreateExportList STREQUAL "") diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake index 57b3910..7986a37 100644 --- a/Modules/Platform/Apple-Clang.cmake +++ b/Modules/Platform/Apple-Clang.cmake @@ -15,9 +15,18 @@ macro(__apple_compiler_clang lang) set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") endif() + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) + # linker selection + set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld") + set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "-fuse-ld=ld" "LINKER:-ld_classic") + set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld") + set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold") + set(CMAKE_${lang}_USING_LINKER_SOLD "-fuse-ld=sold") + if(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneOS") set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-miphoneos-version-min=") elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneSimulator") diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake index 823c790..15f6a71 100644 --- a/Modules/Platform/Apple-GNU.cmake +++ b/Modules/Platform/Apple-GNU.cmake @@ -17,6 +17,9 @@ macro(__apple_compiler_gnu lang) set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_${lang}_USING_LINKER_SYSTEM "") + set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic") endmacro() macro(cmake_gnu_set_sysroot_flag lang) diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake index ef64012..070b24d 100644 --- a/Modules/Platform/CYGWIN-GNU.cmake +++ b/Modules/Platform/CYGWIN-GNU.cmake @@ -52,6 +52,8 @@ macro(__cygwin_compiler_gnu lang) "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--out-implib,<TARGET_IMPLIB> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>") set(CMAKE_${lang}_CREATE_WIN32_EXE "-mwindows") + set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") + # No -fPIC on cygwin set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index d614182..533b9ce 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -125,17 +125,17 @@ set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) # Defines LINK_LIBRARY features for libraries -set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library <LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}") +set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library,<LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library <LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}") +set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library,<LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}") +set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,<LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE) # Defines LINK_LIBRARY feature to Force loading of all members of an archive -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load <LIB_ITEM>") +set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,<LIB_ITEM>") set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) # default to searching for frameworks first diff --git a/Modules/Platform/Linux-Clang-CUDA.cmake b/Modules/Platform/Linux-Clang-CUDA.cmake new file mode 100644 index 0000000..4a9337e --- /dev/null +++ b/Modules/Platform/Linux-Clang-CUDA.cmake @@ -0,0 +1,2 @@ +include(Platform/Linux-GNU) +__linux_compiler_gnu(CUDA) diff --git a/Modules/Platform/Linux-GNU.cmake b/Modules/Platform/Linux-GNU.cmake index 6878254..c3878eb 100644 --- a/Modules/Platform/Linux-GNU.cmake +++ b/Modules/Platform/Linux-GNU.cmake @@ -12,4 +12,13 @@ macro(__linux_compiler_gnu lang) # We pass this for historical reasons. Projects may have # executables that use dlopen but do not set ENABLE_EXPORTS. set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic") + + set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") + + # linker selection + set(CMAKE_${lang}_USING_LINKER_SYSTEM "") + set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld") + set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd") + set(CMAKE_${lang}_USING_LINKER_GOLD "-fuse-ld=gold") + set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold") endmacro() diff --git a/Modules/Platform/Linux-LLVMFlang-Fortran.cmake b/Modules/Platform/Linux-LLVMFlang-Fortran.cmake new file mode 100644 index 0000000..ceecc2f --- /dev/null +++ b/Modules/Platform/Linux-LLVMFlang-Fortran.cmake @@ -0,0 +1 @@ +include(Platform/Linux-GNU-Fortran) diff --git a/Modules/Platform/Linux-NVIDIA-CUDA.cmake b/Modules/Platform/Linux-NVIDIA-CUDA.cmake new file mode 100644 index 0000000..f383720 --- /dev/null +++ b/Modules/Platform/Linux-NVIDIA-CUDA.cmake @@ -0,0 +1,9 @@ + +set(CMAKE_CUDA_VERBOSE_LINK_FLAG "-Wl,-v") + +# linker selection +set(CMAKE_CUDA_USING_LINKER_SYSTEM "") +set(CMAKE_CUDA_USING_LINKER_LLD "-fuse-ld=lld") +set(CMAKE_CUDA_USING_LINKER_BFD "-fuse-ld=bfd") +set(CMAKE_CUDA_USING_LINKER_GOLD "-fuse-ld=gold") +set(CMAKE_CUDA_USING_LINKER_MOLD "-fuse-ld=mold") diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake index 33d271d..f081f42 100644 --- a/Modules/Platform/Windows-Clang.cmake +++ b/Modules/Platform/Windows-Clang.cmake @@ -45,6 +45,7 @@ macro(__windows_compiler_clang_gnu lang) math(EXPR MSVC_VERSION "${CMAKE_MATCH_1}*100 + ${CMAKE_MATCH_2}") endif() + set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-v") # No -fPIC on Windows set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") @@ -53,6 +54,14 @@ macro(__windows_compiler_clang_gnu lang) set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL) + + # linker selection + set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link") + set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=link") + set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld-link") + set(CMAKE_${lang}_USING_LINKER_MSVC "-fuse-ld=link") + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1) set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1) set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1) @@ -74,10 +83,10 @@ macro(__windows_compiler_clang_gnu lang) set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>") set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>") set(CMAKE_${lang}_CREATE_SHARED_LIBRARY - "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>") + "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>") set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY}) set(CMAKE_${lang}_LINK_EXECUTABLE - "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>") + "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>") set(CMAKE_${lang}_CREATE_WIN32_EXE "-Xlinker /subsystem:windows") set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Xlinker /subsystem:console") diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index 412af6b..9f81882 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake @@ -112,6 +112,13 @@ macro(__windows_compiler_gnu lang) set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic") endforeach() + set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") + + # linker selection + set(CMAKE_${lang}_USING_LINKER_SYSTEM "") + set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd") + set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld") + # No -fPIC on Windows set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") diff --git a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake index 57e36c6..10e3b2c 100644 --- a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake +++ b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake @@ -5,17 +5,22 @@ elseif("x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC") include(Platform/Windows-MSVC) __windows_compiler_msvc(Fortran) - # FIXME(LLVMFlang): It does not provides MSVC runtime library selection flags. - # It should be given a flag like classic Flang's `-Xclang --dependent-lib=`, or a - # dedicated flag to select among multiple `Fortran*.lib` runtime library variants - # that each depend on a different MSVC runtime library. For now, LLVMFlang's - # `Fortran*.lib` runtime libraries hard-code use of msvcrt (MultiThreadedDLL), - # so we link to it ourselves. - set(_LLVMFlang_LINK_RUNTIME "-defaultlib:msvcrt") - set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") - set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "") - set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "") - set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "") + if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0) + set(_LLVMFlang_LINK_RUNTIME "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "-fms-runtime-lib=static") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "-fms-runtime-lib=dll") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "-fms-runtime-lib=static_dbg") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "-fms-runtime-lib=dll_dbg") + else() + # LLVMFlang < 18.0 does not have MSVC runtime library selection flags. + # The official distrubtion's `Fortran*.lib` runtime libraries hard-code + # use of msvcrt (MultiThreadedDLL), so we link to it ourselves. + set(_LLVMFlang_LINK_RUNTIME "-defaultlib:msvcrt") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "") + set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "") + endif() # LLVMFlang, like Clang, does not provide all debug information format flags. # In order to provide easy integration with C and C++ projects that use the diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 829ab9b..48e7c4e 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -512,6 +512,14 @@ macro(__windows_compiler_msvc lang) set(CMAKE_DEPFILE_FLAGS_${lang} "/showIncludes") set(CMAKE_${lang}_DEPFILE_FORMAT msvc) endif() + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL) + + # linker selection + set(CMAKE_${lang}_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}") + set(CMAKE_${lang}_USING_LINKER_LLD "${CMAKE_LINKER_LLD}") + set(CMAKE_${lang}_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}") + set(CMAKE_${lang}_USING_LINKER_MODE TOOL) endmacro() macro(__windows_compiler_msvc_enable_rc flags) diff --git a/Modules/Platform/Windows-NVIDIA-CUDA.cmake b/Modules/Platform/Windows-NVIDIA-CUDA.cmake index 326e715..6489841 100644 --- a/Modules/Platform/Windows-NVIDIA-CUDA.cmake +++ b/Modules/Platform/Windows-NVIDIA-CUDA.cmake @@ -47,6 +47,12 @@ set(CMAKE_CUDA_DEVICE_LINK_EXECUTABLE "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS} -shared -dlink <OBJECTS> -o <TARGET> <LINK_LIBRARIES> -Xcompiler=-Fd<TARGET_COMPILE_PDB>,-FS${__IMPLICIT_DLINK_FLAGS}") unset(__IMPLICIT_DLINK_FLAGS) +# linker selection +set(CMAKE_CUDA_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}") +set(CMAKE_CUDA_USING_LINKER_LLD "${CMAKE_LINKER_LLD}") +set(CMAKE_CUDA_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}") +set(CMAKE_CUDA_USING_LINKER_MODE TOOL) + string(REPLACE "/D" "-D" _PLATFORM_DEFINES_CUDA "${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_CXX}") if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT) diff --git a/Modules/Platform/Windows.cmake b/Modules/Platform/Windows.cmake index 1bf39cf..bc93caa 100644 --- a/Modules/Platform/Windows.cmake +++ b/Modules/Platform/Windows.cmake @@ -14,7 +14,10 @@ set(CMAKE_DL_LIBS "") set(CMAKE_EXTRA_LINK_EXTENSIONS ".targets") set(CMAKE_FIND_LIBRARY_PREFIXES "") -set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") +set(CMAKE_FIND_LIBRARY_SUFFIXES + ".dll.lib" # import library from Rust toolchain for MSVC ABI + ".lib" # static or import library from MSVC tooling + ) # for borland make long command lines are redirected to a file # with the following syntax, see Windows-bcc32.cmake for use diff --git a/Modules/UseEcos.cmake b/Modules/UseEcos.cmake index 83c9b20..5e6f606 100644 --- a/Modules/UseEcos.cmake +++ b/Modules/UseEcos.cmake @@ -8,21 +8,31 @@ UseEcos This module defines variables and macros required to build eCos application. This file contains the following macros: -ECOS_ADD_INCLUDE_DIRECTORIES() - add the eCos include dirs -ECOS_ADD_EXECUTABLE(name source1 ... sourceN ) - create an eCos -executable ECOS_ADJUST_DIRECTORY(VAR source1 ... sourceN ) - adjusts -the path of the source files and puts the result into VAR - -Macros for selecting the toolchain: ECOS_USE_ARM_ELF_TOOLS() - enable -the ARM ELF toolchain for the directory where it is called -ECOS_USE_I386_ELF_TOOLS() - enable the i386 ELF toolchain for the -directory where it is called ECOS_USE_PPC_EABI_TOOLS() - enable the -PowerPC toolchain for the directory where it is called - -It contains the following variables: ECOS_DEFINITIONS -ECOSCONFIG_EXECUTABLE ECOS_CONFIG_FILE - defaults to ecos.ecc, if your -eCos configuration file has a different name, adjust this variable for -internal use only: + +``ECOS_ADD_INCLUDE_DIRECTORIES()`` + add the eCos include dirs +``ECOS_ADD_EXECUTABLE(name source1 ... sourceN )`` + create an eCos executable +``ECOS_ADJUST_DIRECTORY(VAR source1 ... sourceN )`` + adjusts the path of the source files and puts the result into ``VAR`` + +Macros for selecting the toolchain: + +``ECOS_USE_ARM_ELF_TOOLS()`` + enable the ARM ELF toolchain for the directory where it is called +``ECOS_USE_I386_ELF_TOOLS()`` + enable the i386 ELF toolchain for the directory where it is called +``ECOS_USE_PPC_EABI_TOOLS()`` + enable the PowerPC toolchain for the directory where it is called + +It contains the following variables: + +``ECOS_DEFINITIONS`` + +``ECOSCONFIG_EXECUTABLE`` + +``ECOS_CONFIG_FILE`` + defaults to ecos.ecc, if your eCos configuration file has a different name, adjust this variable for internal use only: :: diff --git a/Modules/UseJava/javaTargets.cmake.in b/Modules/UseJava/javaTargets.cmake.in index 6e14256..f3670c2 100644 --- a/Modules/UseJava/javaTargets.cmake.in +++ b/Modules/UseJava/javaTargets.cmake.in @@ -1,6 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) cmake_policy(PUSH) -cmake_policy(VERSION 2.8) +cmake_policy(VERSION 2.8.12...3.27) #---------------------------------------------------------------- # Generated CMake Java target import file. diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake index cece973..f264fb6 100644 --- a/Modules/UseSWIG.cmake +++ b/Modules/UseSWIG.cmake @@ -657,7 +657,7 @@ function(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile) if(NOT ("-dllimport" IN_LIST swig_source_file_flags OR "-dllimport" IN_LIST SWIG_MODULE_${name}_EXTRA_FLAGS)) # This makes sure that the name used in the generated DllImport # matches the library name created by CMake - list (APPEND SWIG_MODULE_${name}_EXTRA_FLAGS "-dllimport" "$<TARGET_FILE_PREFIX:${target_name}>$<TARGET_FILE_BASE_NAME:${target_name}>") + list (APPEND SWIG_MODULE_${name}_EXTRA_FLAGS "-dllimport" "$<TARGET_FILE_BASE_NAME:${target_name}>") endif() endif() if (SWIG_MODULE_${name}_LANGUAGE STREQUAL "PYTHON" AND NOT SWIG_MODULE_${name}_NOPROXY) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1bc855e..8c57762 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -450,7 +450,6 @@ add_library( cmUVProcessChain.h cmUVStream.h cmUVStreambuf.h - cmUVSignalHackRAII.h cmVariableWatch.cxx cmVariableWatch.h cmVersion.cxx @@ -558,6 +557,8 @@ add_library( cmFindLibraryCommand.h cmFindPackageCommand.cxx cmFindPackageCommand.h + cmFindPackageStack.cxx + cmFindPackageStack.h cmFindPathCommand.cxx cmFindPathCommand.h cmFindProgramCommand.cxx @@ -1090,6 +1091,9 @@ add_library( CTest/cmCTestP4.cxx CTest/cmCTestP4.h + CTest/cmUVJobServerClient.cxx + CTest/cmUVJobServerClient.h + LexerParser/cmCTestResourceGroupsLexer.cxx LexerParser/cmCTestResourceGroupsLexer.h LexerParser/cmCTestResourceGroupsLexer.in.l diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index a51ba2e..f629903 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 28) -set(CMake_VERSION_PATCH 1) +set(CMake_VERSION_PATCH 20240103) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 5077596..6918b5e 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -359,6 +359,7 @@ void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() GetOption("CPACK_PACKAGE_NAME")); CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER"); CopyDefinition(includeFile, "CPACK_WIX_UI_REF"); + CopyDefinition(includeFile, "CPACK_WIX_INSTALL_SCOPE"); } void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile() diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index c9c069c..b7b6785 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -5,6 +5,8 @@ #include <cstring> #include <map> #include <ostream> +#include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> @@ -17,6 +19,121 @@ #include "cmValue.h" #include "cmWorkingDirectory.h" +enum class DeduplicateStatus +{ + Skip, + Add, + Error +}; + +/** + * @class cmCPackArchiveGenerator::Deduplicator + * @brief A utility class for deduplicating files, folders, and symlinks. + * + * This class is responsible for identifying duplicate files, folders, and + * symlinks when generating an archive. It keeps track of the paths that have + * been processed and helps in deciding whether a new path should be added, + * skipped, or flagged as an error. + */ +class cmCPackArchiveGenerator::Deduplicator +{ +private: + /** + * @brief Compares a file with already processed files. + * + * @param path The path of the file to compare. + * @param localTopLevel The top-level directory for the file. + * @return DeduplicateStatus indicating whether to add, skip, or flag an + * error for the file. + */ + DeduplicateStatus CompareFile(const std::string& path, + const std::string& localTopLevel) + { + auto fileItr = this->Files.find(path); + if (fileItr != this->Files.end()) { + return cmSystemTools::FilesDiffer(path, fileItr->second) + ? DeduplicateStatus::Error + : DeduplicateStatus::Skip; + } + + this->Files[path] = cmStrCat(localTopLevel, "/", path); + return DeduplicateStatus::Add; + } + + /** + * @brief Compares a folder with already processed folders. + * + * @param path The path of the folder to compare. + * @return DeduplicateStatus indicating whether to add or skip the folder. + */ + DeduplicateStatus CompareFolder(const std::string& path) + { + if (this->Folders.find(path) != this->Folders.end()) { + return DeduplicateStatus::Skip; + } + + this->Folders.emplace(path); + return DeduplicateStatus::Add; + } + + /** + * @brief Compares a symlink with already processed symlinks. + * + * @param path The path of the symlink to compare. + * @return DeduplicateStatus indicating whether to add, skip, or flag an + * error for the symlink. + */ + DeduplicateStatus CompareSymlink(const std::string& path) + { + auto symlinkItr = this->Symlink.find(path); + std::string symlinkValue; + auto status = cmSystemTools::ReadSymlink(path, symlinkValue); + if (!status.IsSuccess()) { + return DeduplicateStatus::Error; + } + + if (symlinkItr != this->Symlink.end()) { + return symlinkValue == symlinkItr->second ? DeduplicateStatus::Skip + : DeduplicateStatus::Error; + } + + this->Symlink[path] = symlinkValue; + return DeduplicateStatus::Add; + } + +public: + /** + * @brief Determines the deduplication status of a given path. + * + * This method identifies whether the given path is a file, folder, or + * symlink and then delegates to the appropriate comparison method. + * + * @param path The path to check for deduplication. + * @param localTopLevel The top-level directory for the path. + * @return DeduplicateStatus indicating the action to take for the given + * path. + */ + DeduplicateStatus IsDeduplicate(const std::string& path, + const std::string& localTopLevel) + { + DeduplicateStatus status; + if (cmSystemTools::FileIsDirectory(path)) { + status = this->CompareFolder(path); + } else if (cmSystemTools::FileIsSymlink(path)) { + status = this->CompareSymlink(path); + } else { + status = this->CompareFile(path, localTopLevel); + } + + return status; + } + +private: + std::unordered_map<std::string, std::string> Symlink; + std::unordered_set<std::string> Folders; + std::unordered_map<std::string, std::string> Files; +}; + cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator() { return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip", @@ -110,7 +227,8 @@ int cmCPackArchiveGenerator::InitializeInternal() } int cmCPackArchiveGenerator::addOneComponentToArchive( - cmArchiveWrite& archive, cmCPackComponent* component) + cmArchiveWrite& archive, cmCPackComponent* component, + Deduplicator* deduplicator) { cmCPackLogger(cmCPackLog::LOG_VERBOSE, " - packaging component: " << component->Name << std::endl); @@ -139,8 +257,25 @@ int cmCPackArchiveGenerator::addOneComponentToArchive( } for (std::string const& file : component->Files) { std::string rp = filePrefix + file; - cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl); - archive.Add(rp, 0, nullptr, false); + + DeduplicateStatus status = DeduplicateStatus::Add; + if (deduplicator != nullptr) { + status = deduplicator->IsDeduplicate(rp, localToplevel); + } + + if (deduplicator == nullptr || status == DeduplicateStatus::Add) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl); + archive.Add(rp, 0, nullptr, false); + } else if (status == DeduplicateStatus::Error) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "ERROR The data in files with the " + "same filename is different."); + return 0; + } else { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Passing file: " << rp << std::endl); + } + if (!archive) { cmCPackLogger(cmCPackLog::LOG_ERROR, "ERROR while packaging files: " << archive.GetError() @@ -197,6 +332,8 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) std::string packageFileName = std::string(this->toplevel) + "/" + this->GetArchiveComponentFileName(compG.first, true); + Deduplicator deduplicator; + // open a block in order to automatically close archive // at the end of the block { @@ -204,7 +341,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) // now iterate over the component of this group for (cmCPackComponent* comp : (compG.second).Components) { // Add the files of this component to the archive - this->addOneComponentToArchive(archive, comp); + this->addOneComponentToArchive(archive, comp, &deduplicator); } } // add the generated package to package file names list @@ -231,7 +368,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) { DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); // Add the files of this component to the archive - this->addOneComponentToArchive(archive, &(comp.second)); + this->addOneComponentToArchive(archive, &(comp.second), nullptr); } // add the generated package to package file names list this->packageFileNames.push_back(std::move(packageFileName)); @@ -252,7 +389,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) { DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); // Add the files of this component to the archive - this->addOneComponentToArchive(archive, &(comp.second)); + this->addOneComponentToArchive(archive, &(comp.second), nullptr); } // add the generated package to package file names list this->packageFileNames.push_back(std::move(packageFileName)); @@ -282,10 +419,12 @@ int cmCPackArchiveGenerator::PackageComponentsAllInOne() << std::endl); DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); + Deduplicator deduplicator; + // The ALL COMPONENTS in ONE package case for (auto& comp : this->Components) { // Add the files of this component to the archive - this->addOneComponentToArchive(archive, &(comp.second)); + this->addOneComponentToArchive(archive, &(comp.second), &deduplicator); } // archive goes out of scope so it will finalized and closed. diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h index 8a9bbc6..b8a1afa 100644 --- a/Source/CPack/cmCPackArchiveGenerator.h +++ b/Source/CPack/cmCPackArchiveGenerator.h @@ -47,6 +47,8 @@ private: std::string GetArchiveComponentFileName(const std::string& component, bool isGroupName); + class Deduplicator; + protected: int InitializeInternal() override; /** @@ -54,9 +56,11 @@ protected: * to the provided (already opened) archive. * @param[in,out] archive the archive object * @param[in] component the component whose file will be added to archive + * @param[in] deduplicator file deduplicator utility. */ int addOneComponentToArchive(cmArchiveWrite& archive, - cmCPackComponent* component); + cmCPackComponent* component, + Deduplicator* deduplicator); /** * The main package file method. diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx index b8bf070..bf90b06 100644 --- a/Source/CPack/cmCPackInnoSetupGenerator.cxx +++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx @@ -579,8 +579,9 @@ bool cmCPackInnoSetupGenerator::ProcessFiles() bool cmCPackInnoSetupGenerator::ProcessComponents() { - codeIncludes.push_back("{ The following lines are required by CPack because " - "this script uses components }"); + codeIncludes.emplace_back( + "{ The following lines are required by CPack because " + "this script uses components }"); // Installation types std::vector<cmCPackInstallationType*> types(InstallationTypes.size()); @@ -607,7 +608,7 @@ bool cmCPackInnoSetupGenerator::ProcessComponents() "\"{code:CPackGetCustomInstallationMessage}\""; customTypeParams["Flags"] = "iscustom"; - allTypes.push_back("custom"); + allTypes.emplace_back("custom"); typeInstructions.push_back(ISKeyValueLine(customTypeParams)); // Components @@ -633,6 +634,7 @@ bool cmCPackInnoSetupGenerator::ProcessComponents() } else if (!component->InstallationTypes.empty()) { std::vector<std::string> installationTypes; + installationTypes.reserve(component->InstallationTypes.size()); for (cmCPackInstallationType* j : component->InstallationTypes) { installationTypes.push_back(j->Name); } diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h index 2ab2f8e..347b0f7 100644 --- a/Source/CPack/cmCPackLog.h +++ b/Source/CPack/cmCPackLog.h @@ -29,7 +29,7 @@ public: cmCPackLog(const cmCPackLog&) = delete; cmCPackLog& operator=(const cmCPackLog&) = delete; - enum __log_tags + enum cm_log_tags { NOTAG = 0, LOG_OUTPUT = 0x1, diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx index 36df344..87081f0 100644 --- a/Source/CTest/cmCTestBZR.cxx +++ b/Source/CTest/cmCTestBZR.cxx @@ -374,7 +374,7 @@ bool cmCTestBZR::UpdateImpl() // Use "bzr pull" to update the working tree. std::vector<std::string> bzr_update; bzr_update.push_back(this->CommandLineTool); - bzr_update.push_back("pull"); + bzr_update.emplace_back("pull"); cm::append(bzr_update, args); diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index ef95b25..badd43e 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -92,8 +92,8 @@ bool cmCTestCVS::UpdateImpl() // Run "cvs update" to update the work tree. std::vector<std::string> cvs_update; cvs_update.push_back(this->CommandLineTool); - cvs_update.push_back("-z3"); - cvs_update.push_back("update"); + cvs_update.emplace_back("-z3"); + cvs_update.emplace_back("update"); cm::append(cvs_update, args); UpdateParser out(this, "up-out> "); diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx index af495bb..2c92d77 100644 --- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx +++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx @@ -2,25 +2,27 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestEmptyBinaryDirectoryCommand.h" -#include <sstream> - #include "cmCTestScriptHandler.h" - -class cmExecutionStatus; +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" bool cmCTestEmptyBinaryDirectoryCommand::InitialPass( - std::vector<std::string> const& args, cmExecutionStatus& /*unused*/) + std::vector<std::string> const& args, cmExecutionStatus& status) { if (args.size() != 1) { this->SetError("called with incorrect number of arguments"); return false; } - if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0])) { - std::ostringstream ostr; - ostr << "problem removing the binary directory: " << args[0]; - this->SetError(ostr.str()); - return false; + std::string err; + if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0], err)) { + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Did not remove the binary directory:\n ", args[0], + "\nbecause:\n ", err)); + return true; } return true; diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index ca8659e..984c837 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -159,7 +159,7 @@ bool cmCTestGIT::UpdateByFetchAndReset() // Use "git fetch" to get remote commits. std::vector<std::string> git_fetch; git_fetch.push_back(git); - git_fetch.push_back("fetch"); + git_fetch.emplace_back("fetch"); // Add user-specified update options. std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions"); diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx index e1a945d..3d56be0 100644 --- a/Source/CTest/cmCTestHG.cxx +++ b/Source/CTest/cmCTestHG.cxx @@ -137,9 +137,9 @@ bool cmCTestHG::UpdateImpl() // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) std::vector<std::string> hg_update; - hg_update.push_back(this->CommandLineTool.c_str()); - hg_update.push_back("update"); - hg_update.push_back("-v"); + hg_update.emplace_back(this->CommandLineTool); + hg_update.emplace_back("update"); + hg_update.emplace_back("-v"); // Add user-specified update options. std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions"); diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index ca07a08..7b72f30 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -40,7 +40,7 @@ #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUVSignalHackRAII.h" // IWYU pragma: keep +#include "cmUVJobServerClient.h" #include "cmWorkingDirectory.h" namespace cmsys { @@ -83,17 +83,12 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default; // Set the tests -void cmCTestMultiProcessHandler::SetTests(TestMap& tests, - PropertiesMap& properties) +void cmCTestMultiProcessHandler::SetTests(TestMap tests, + PropertiesMap properties) { - this->Tests = tests; - this->Properties = properties; - this->Total = this->Tests.size(); - // set test run map to false for all - for (auto const& t : this->Tests) { - this->TestRunningMap[t.first] = false; - this->TestFinishMap[t.first] = false; - } + this->PendingTests = std::move(tests); + this->Properties = std::move(properties); + this->Total = this->PendingTests.size(); if (!this->CTest->GetShowOnly()) { this->ReadCostData(); this->HasCycles = !this->CheckCycles(); @@ -126,25 +121,50 @@ void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load) } } +bool cmCTestMultiProcessHandler::Complete() +{ + return this->Completed == this->Total; +} + +void cmCTestMultiProcessHandler::InitializeLoop() +{ + this->Loop.init(); + this->StartNextTestsOnIdle_.init(*this->Loop, this); + this->StartNextTestsOnTimer_.init(*this->Loop, this); + + this->JobServerClient = cmUVJobServerClient::Connect( + *this->Loop, /*onToken=*/[this]() { this->JobServerReceivedToken(); }, + /*onDisconnect=*/nullptr); + if (this->JobServerClient) { + cmCTestLog(this->CTest, OUTPUT, + "Connected to MAKE jobserver" << std::endl); + } +} + +void cmCTestMultiProcessHandler::FinalizeLoop() +{ + this->JobServerClient.reset(); + this->StartNextTestsOnTimer_.reset(); + this->StartNextTestsOnIdle_.reset(); + this->Loop.reset(); +} + void cmCTestMultiProcessHandler::RunTests() { this->CheckResume(); if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) { return; } -#ifdef CMAKE_UV_SIGNAL_HACK - cmUVSignalHackRAII hackRAII; -#endif this->TestHandler->SetMaxIndex(this->FindMaxIndex()); - uv_loop_init(&this->Loop); - this->StartNextTests(); - uv_run(&this->Loop, UV_RUN_DEFAULT); - uv_loop_close(&this->Loop); + this->InitializeLoop(); + this->StartNextTestsOnIdle(); + uv_run(this->Loop, UV_RUN_DEFAULT); + this->FinalizeLoop(); if (!this->StopTimePassed && !this->CheckStopOnFailure()) { - assert(this->Completed == this->Total); - assert(this->Tests.empty()); + assert(this->Complete()); + assert(this->PendingTests.empty()); } assert(this->AllResourcesAvailable()); @@ -152,38 +172,17 @@ void cmCTestMultiProcessHandler::RunTests() this->UpdateCostData(); } -bool cmCTestMultiProcessHandler::StartTestProcess(int test) +void cmCTestMultiProcessHandler::StartTestProcess(int test) { - if (this->HaveAffinity && this->Properties[test]->WantAffinity) { - size_t needProcessors = this->GetProcessorsUsed(test); - if (needProcessors > this->ProcessorsAvailable.size()) { - return false; - } - std::vector<size_t> affinity; - affinity.reserve(needProcessors); - for (size_t i = 0; i < needProcessors; ++i) { - auto p = this->ProcessorsAvailable.begin(); - affinity.push_back(*p); - this->ProcessorsAvailable.erase(p); - } - this->Properties[test]->Affinity = std::move(affinity); - } - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "test " << test << "\n", this->Quiet); - this->TestRunningMap[test] = true; // mark the test as running - // now remove the test itself - this->EraseTest(test); - this->RunningCount += this->GetProcessorsUsed(test); - auto testRun = cm::make_unique<cmCTestRunTest>(*this); + auto testRun = cm::make_unique<cmCTestRunTest>(*this, test); if (this->RepeatMode != cmCTest::Repeat::Never) { testRun->SetRepeatMode(this->RepeatMode); testRun->SetNumberOfRuns(this->RepeatCount); } - testRun->SetIndex(test); - testRun->SetTestProperties(this->Properties[test]); if (this->UseResourceSpec) { testRun->SetUseAllocatedResources(true); testRun->SetAllocatedResources(this->AllocatedResources[test]); @@ -197,22 +196,18 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test) } } - // Always lock the resources we'll be using, even if we fail to set the - // working directory because FinishTestProcess() will try to unlock them - this->LockResources(test); - - if (!this->ResourceAllocationErrors[test].empty()) { + if (!this->ResourceAvailabilityErrors[test].empty()) { std::ostringstream e; e << "Insufficient resources for test " << this->Properties[test]->Name << ":\n\n"; - for (auto const& it : this->ResourceAllocationErrors[test]) { + for (auto const& it : this->ResourceAvailabilityErrors[test]) { switch (it.second) { - case ResourceAllocationError::NoResourceType: + case ResourceAvailabilityError::NoResourceType: e << " Test requested resources of type '" << it.first << "' which does not exist\n"; break; - case ResourceAllocationError::InsufficientResources: + case ResourceAvailabilityError::InsufficientResources: e << " Test requested resources of type '" << it.first << "' in the following amounts:\n"; for (auto const& group : this->Properties[test]->ResourceGroups) { @@ -236,7 +231,7 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test) e << "Resource spec file:\n\n " << this->ResourceSpecFile; cmCTestRunTest::StartFailure(std::move(testRun), this->Total, e.str(), "Insufficient resources"); - return false; + return; } cmWorkingDirectory workdir(this->Properties[test]->Directory); @@ -246,13 +241,12 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test) this->Properties[test]->Directory + " : " + std::strerror(workdir.GetLastResult()), "Failed to change working directory"); - return false; + return; } // Ownership of 'testRun' has moved to another structure. // When the test finishes, FinishTestProcess will be called. - return cmCTestRunTest::StartTest(std::move(testRun), this->Completed, - this->Total); + cmCTestRunTest::StartTest(std::move(testRun), this->Completed, this->Total); } bool cmCTestMultiProcessHandler::AllocateResources(int index) @@ -261,6 +255,12 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index) return true; } + // If the test needs unavailable resources then do not allocate anything + // because it will never run. We will issue the recorded errors instead. + if (!this->ResourceAvailabilityErrors[index].empty()) { + return true; + } + std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations; if (!this->TryAllocateResources(index, allocations)) { return false; @@ -285,7 +285,7 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index) bool cmCTestMultiProcessHandler::TryAllocateResources( int index, std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations, - std::map<std::string, ResourceAllocationError>* errors) + std::map<std::string, ResourceAvailabilityError>* errors) { allocations.clear(); @@ -305,7 +305,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources( for (auto& it : allocations) { if (!availableResources.count(it.first)) { if (errors) { - (*errors)[it.first] = ResourceAllocationError::NoResourceType; + (*errors)[it.first] = ResourceAvailabilityError::NoResourceType; result = false; } else { return false; @@ -313,7 +313,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources( } else if (!cmAllocateCTestResourcesRoundRobin( availableResources.at(it.first), it.second)) { if (errors) { - (*errors)[it.first] = ResourceAllocationError::InsufficientResources; + (*errors)[it.first] = ResourceAvailabilityError::InsufficientResources; result = false; } else { return false; @@ -360,14 +360,14 @@ bool cmCTestMultiProcessHandler::AllResourcesAvailable() return true; } -void cmCTestMultiProcessHandler::CheckResourcesAvailable() +void cmCTestMultiProcessHandler::CheckResourceAvailability() { if (this->UseResourceSpec) { - for (auto test : this->SortedTests) { + for (auto const& t : this->PendingTests) { std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations; - this->TryAllocateResources(test, allocations, - &this->ResourceAllocationErrors[test]); + this->TryAllocateResources(t.first, allocations, + &this->ResourceAvailabilityErrors[t.first]); } } } @@ -403,30 +403,49 @@ void cmCTestMultiProcessHandler::SetStopTimePassed() void cmCTestMultiProcessHandler::LockResources(int index) { - this->LockedResources.insert( - this->Properties[index]->LockedResources.begin(), - this->Properties[index]->LockedResources.end()); + this->RunningCount += this->GetProcessorsUsed(index); + + auto* properties = this->Properties[index]; + + this->ProjectResourcesLocked.insert(properties->ProjectResources.begin(), + properties->ProjectResources.end()); - if (this->Properties[index]->RunSerial) { + if (properties->RunSerial) { this->SerialTestRunning = true; } + + if (this->HaveAffinity && properties->WantAffinity) { + size_t needProcessors = this->GetProcessorsUsed(index); + assert(needProcessors <= this->ProcessorsAvailable.size()); + std::vector<size_t> affinity; + affinity.reserve(needProcessors); + for (size_t i = 0; i < needProcessors; ++i) { + auto p = this->ProcessorsAvailable.begin(); + affinity.push_back(*p); + this->ProcessorsAvailable.erase(p); + } + properties->Affinity = std::move(affinity); + } } void cmCTestMultiProcessHandler::UnlockResources(int index) { - for (std::string const& i : this->Properties[index]->LockedResources) { - this->LockedResources.erase(i); + auto* properties = this->Properties[index]; + + for (auto p : properties->Affinity) { + this->ProcessorsAvailable.insert(p); } - if (this->Properties[index]->RunSerial) { + properties->Affinity.clear(); + + for (std::string const& i : properties->ProjectResources) { + this->ProjectResourcesLocked.erase(i); + } + + if (properties->RunSerial) { this->SerialTestRunning = false; } -} -void cmCTestMultiProcessHandler::EraseTest(int test) -{ - this->Tests.erase(test); - this->SortedTests.erase( - std::find(this->SortedTests.begin(), this->SortedTests.end(), test)); + this->RunningCount -= this->GetProcessorsUsed(index); } inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test) @@ -450,50 +469,40 @@ std::string cmCTestMultiProcessHandler::GetName(int test) return this->Properties[test]->Name; } -bool cmCTestMultiProcessHandler::StartTest(int test) +void cmCTestMultiProcessHandler::StartTest(int test) { - // Check for locked resources - for (std::string const& i : this->Properties[test]->LockedResources) { - if (cm::contains(this->LockedResources, i)) { - return false; - } - } - - // Allocate resources - if (this->ResourceAllocationErrors[test].empty() && - !this->AllocateResources(test)) { - this->DeallocateResources(test); - return false; + if (this->JobServerClient) { + // There is a job server. Request a token and queue the test to run + // when a token is received. Note that if we do not get a token right + // away it's possible that the system load will be higher when the + // token is received and we may violate the test-load limit. However, + // this is unlikely because if we do not get a token right away, some + // other job that's currently running must finish before we get one. + this->JobServerClient->RequestToken(); + this->JobServerQueuedTests.emplace_back(test); + } else { + // There is no job server. Start the test now. + this->StartTestProcess(test); } +} - // if there are no depends left then run this test - if (this->Tests[test].empty()) { - return this->StartTestProcess(test); - } - // This test was not able to start because it is waiting - // on depends to run - this->DeallocateResources(test); - return false; +void cmCTestMultiProcessHandler::JobServerReceivedToken() +{ + assert(!this->JobServerQueuedTests.empty()); + int test = this->JobServerQueuedTests.front(); + this->JobServerQueuedTests.pop_front(); + this->StartTestProcess(test); } void cmCTestMultiProcessHandler::StartNextTests() { - if (this->TestLoadRetryTimer.get() != nullptr) { - // This timer may be waiting to call StartNextTests again. - // Since we have been called it is no longer needed. - uv_timer_stop(this->TestLoadRetryTimer); - } - - if (this->Tests.empty()) { - this->TestLoadRetryTimer.reset(); - return; - } + // One or more events may be scheduled to call this method again. + // Since this method has been called they are no longer needed. + this->StartNextTestsOnIdle_.stop(); + this->StartNextTestsOnTimer_.stop(); - if (this->CheckStopTimePassed()) { - return; - } - - if (this->CheckStopOnFailure() && !this->Failed->empty()) { + if (this->PendingTests.empty() || this->CheckStopTimePassed() || + (this->CheckStopOnFailure() && !this->Failed->empty())) { return; } @@ -545,50 +554,79 @@ void cmCTestMultiProcessHandler::StartNextTests() } } - TestList copy = this->SortedTests; - for (auto const& test : copy) { - // Take a nap if we're currently performing a RUN_SERIAL test. - if (this->SerialTestRunning) { - break; - } + // Start tests in the preferred order, each subject to readiness checks. + auto ti = this->OrderedTests.begin(); + while (numToStart > 0 && !this->SerialTestRunning && + ti != this->OrderedTests.end()) { + // Increment the test iterator now because the current list + // entry may be deleted below. + auto cti = ti++; + int test = *cti; + // We can only start a RUN_SERIAL test if no other tests are also // running. if (this->Properties[test]->RunSerial && this->RunningCount > 0) { continue; } + // Exclude tests that depend on unfinished tests. + if (!this->PendingTests[test].Depends.empty()) { + continue; + } + size_t processors = this->GetProcessorsUsed(test); - bool testLoadOk = true; if (this->TestLoad > 0) { - if (processors <= spareLoad) { - cmCTestLog(this->CTest, DEBUG, - "OK to run " << this->GetName(test) << ", it requires " - << processors << " procs & system load is: " - << systemLoad << std::endl); - allTestsFailedTestLoadCheck = false; - } else { - testLoadOk = false; + // Exclude tests that are too big to fit in the spare load. + if (processors > spareLoad) { + // Keep track of the smallest excluded test to report in message below. + if (processors <= minProcessorsRequired) { + minProcessorsRequired = processors; + testWithMinProcessors = this->GetName(test); + } + continue; } + + // We found a test that fits in the spare load. + allTestsFailedTestLoadCheck = false; + cmCTestLog(this->CTest, DEBUG, + "OK to run " + << this->GetName(test) << ", it requires " << processors + << " procs & system load is: " << systemLoad << std::endl); } - if (processors <= minProcessorsRequired) { - minProcessorsRequired = processors; - testWithMinProcessors = this->GetName(test); + // Exclude tests that are too big to fit in the concurrency limit. + if (processors > numToStart) { + continue; } - if (testLoadOk && processors <= numToStart && this->StartTest(test)) { - numToStart -= processors; - } else if (numToStart == 0) { - break; + // Exclude tests that depend on currently-locked project resources. + for (std::string const& i : this->Properties[test]->ProjectResources) { + if (cm::contains(this->ProjectResourcesLocked, i)) { + continue; + } } + + // Allocate system resources needed by this test. + if (!this->AllocateResources(test)) { + continue; + } + + // Lock resources needed by this test. + this->LockResources(test); + + // The test is ready to run. + numToStart -= processors; + this->OrderedTests.erase(cti); + this->PendingTests.erase(test); + this->StartTest(test); } if (allTestsFailedTestLoadCheck) { // Find out whether there are any non RUN_SERIAL tests left, so that the // correct warning may be displayed. bool onlyRunSerialTestsLeft = true; - for (auto const& test : copy) { - if (!this->Properties[test]->RunSerial) { + for (auto const& t : this->PendingTests) { + if (!this->Properties[t.first]->RunSerial) { onlyRunSerialTestsLeft = false; } } @@ -600,7 +638,7 @@ void cmCTestMultiProcessHandler::StartNextTests() } else if (onlyRunSerialTestsLeft) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Only RUN_SERIAL tests remain, awaiting available slot."); - } else { + } else if (!testWithMinProcessors.empty()) { /* clang-format off */ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "System Load: " << systemLoad << ", " @@ -608,26 +646,43 @@ void cmCTestMultiProcessHandler::StartNextTests() "Smallest test " << testWithMinProcessors << " requires " << minProcessorsRequired); /* clang-format on */ + } else { + /* clang-format off */ + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "System Load: " << systemLoad << ", " + "Max Allowed Load: " << this->TestLoad); + /* clang-format on */ } cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "*****" << std::endl); - // Wait between 1 and 5 seconds before trying again. - unsigned int milliseconds = (cmSystemTools::RandomSeed() % 5 + 1) * 1000; - if (this->FakeLoadForTesting) { - milliseconds = 10; - } - if (this->TestLoadRetryTimer.get() == nullptr) { - this->TestLoadRetryTimer.init(this->Loop, this); - } - this->TestLoadRetryTimer.start( - &cmCTestMultiProcessHandler::OnTestLoadRetryCB, milliseconds, 0); + // Try again later when the load might be lower. + this->StartNextTestsOnTimer(); } } -void cmCTestMultiProcessHandler::OnTestLoadRetryCB(uv_timer_t* timer) +void cmCTestMultiProcessHandler::StartNextTestsOnIdle() { - auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data); - self->StartNextTests(); + // Start more tests on the next loop iteration. + this->StartNextTestsOnIdle_.start([](uv_idle_t* idle) { + uv_idle_stop(idle); + auto* self = static_cast<cmCTestMultiProcessHandler*>(idle->data); + self->StartNextTests(); + }); +} + +void cmCTestMultiProcessHandler::StartNextTestsOnTimer() +{ + // Wait between 1 and 5 seconds before trying again. + unsigned int const milliseconds = this->FakeLoadForTesting + ? 10 + : (cmSystemTools::RandomSeed() % 5 + 1) * 1000; + this->StartNextTestsOnTimer_.start( + [](uv_timer_t* timer) { + uv_timer_stop(timer); + auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data); + self->StartNextTests(); + }, + milliseconds, 0); } void cmCTestMultiProcessHandler::FinishTestProcess( @@ -657,26 +712,20 @@ void cmCTestMultiProcessHandler::FinishTestProcess( this->Failed->push_back(properties->Name); } - for (auto& t : this->Tests) { - t.second.erase(test); + for (auto& t : this->PendingTests) { + t.second.Depends.erase(test); } - this->TestFinishMap[test] = true; - this->TestRunningMap[test] = false; this->WriteCheckpoint(test); this->DeallocateResources(test); this->UnlockResources(test); - this->RunningCount -= this->GetProcessorsUsed(test); - - for (auto p : properties->Affinity) { - this->ProcessorsAvailable.insert(p); - } - properties->Affinity.clear(); runner.reset(); - if (started) { - this->StartNextTests(); + + if (this->JobServerClient) { + this->JobServerClient->ReleaseToken(); } + this->StartNextTestsOnIdle(); } void cmCTestMultiProcessHandler::UpdateCostData() @@ -807,7 +856,7 @@ void cmCTestMultiProcessHandler::CreateTestCostList() void cmCTestMultiProcessHandler::CreateParallelTestCostList() { - TestSet alreadySortedTests; + TestSet alreadyOrderedTests; std::list<TestSet> priorityStack; priorityStack.emplace_back(); @@ -815,11 +864,11 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList() // In parallel test runs add previously failed tests to the front // of the cost list and queue other tests for further sorting - for (auto const& t : this->Tests) { + for (auto const& t : this->PendingTests) { if (cm::contains(this->LastTestsFailed, this->Properties[t.first]->Name)) { // If the test failed last time, it should be run first. - this->SortedTests.push_back(t.first); - alreadySortedTests.insert(t.first); + this->OrderedTests.push_back(t.first); + alreadyOrderedTests.insert(t.first); } else { topLevel.insert(t.first); } @@ -834,7 +883,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList() TestSet& currentSet = priorityStack.back(); for (auto const& i : previousSet) { - TestSet const& dependencies = this->Tests[i]; + TestSet const& dependencies = this->PendingTests[i].Depends; currentSet.insert(dependencies.begin(), dependencies.end()); } @@ -855,9 +904,9 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList() TestComparator(this)); for (auto const& j : sortedCopy) { - if (!cm::contains(alreadySortedTests, j)) { - this->SortedTests.push_back(j); - alreadySortedTests.insert(j); + if (!cm::contains(alreadyOrderedTests, j)) { + this->OrderedTests.push_back(j); + alreadyOrderedTests.insert(j); } } } @@ -866,7 +915,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList() void cmCTestMultiProcessHandler::GetAllTestDependencies(int test, TestList& dependencies) { - TestSet const& dependencySet = this->Tests[test]; + TestSet const& dependencySet = this->PendingTests[test].Depends; for (int i : dependencySet) { this->GetAllTestDependencies(i, dependencies); dependencies.push_back(i); @@ -877,17 +926,17 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList() { TestList presortedList; - for (auto const& i : this->Tests) { + for (auto const& i : this->PendingTests) { presortedList.push_back(i.first); } std::stable_sort(presortedList.begin(), presortedList.end(), TestComparator(this)); - TestSet alreadySortedTests; + TestSet alreadyOrderedTests; for (int test : presortedList) { - if (cm::contains(alreadySortedTests, test)) { + if (cm::contains(alreadyOrderedTests, test)) { continue; } @@ -895,14 +944,14 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList() this->GetAllTestDependencies(test, dependencies); for (int testDependency : dependencies) { - if (!cm::contains(alreadySortedTests, testDependency)) { - alreadySortedTests.insert(testDependency); - this->SortedTests.push_back(testDependency); + if (!cm::contains(alreadyOrderedTests, testDependency)) { + alreadyOrderedTests.insert(testDependency); + this->OrderedTests.push_back(testDependency); } } - alreadySortedTests.insert(test); - this->SortedTests.push_back(test); + alreadyOrderedTests.insert(test); + this->OrderedTests.push_back(test); } } @@ -1089,9 +1138,9 @@ static Json::Value DumpCTestProperties( properties.append(DumpCTestProperty( "REQUIRED_FILES", DumpToJsonArray(testProperties.RequiredFiles))); } - if (!testProperties.LockedResources.empty()) { + if (!testProperties.ProjectResources.empty()) { properties.append(DumpCTestProperty( - "RESOURCE_LOCK", DumpToJsonArray(testProperties.LockedResources))); + "RESOURCE_LOCK", DumpToJsonArray(testProperties.ProjectResources))); } if (testProperties.RunSerial) { properties.append( @@ -1259,9 +1308,7 @@ void cmCTestMultiProcessHandler::PrintOutputAsJson() // Don't worry if this fails, we are only showing the test list, not // running the tests cmWorkingDirectory workdir(p.Directory); - cmCTestRunTest testRun(*this); - testRun.SetIndex(p.Index); - testRun.SetTestProperties(&p); + cmCTestRunTest testRun(*this, p.Index); testRun.ComputeArguments(); // Skip tests not available in this configuration. @@ -1298,9 +1345,7 @@ void cmCTestMultiProcessHandler::PrintTestList() // running the tests cmWorkingDirectory workdir(p.Directory); - cmCTestRunTest testRun(*this); - testRun.SetIndex(p.Index); - testRun.SetTestProperties(&p); + cmCTestRunTest testRun(*this, p.Index); testRun.ComputeArguments(); // logs the command in verbose mode if (!p.Labels.empty()) // print the labels @@ -1394,17 +1439,17 @@ void cmCTestMultiProcessHandler::CheckResume() void cmCTestMultiProcessHandler::RemoveTest(int index) { - this->EraseTest(index); + this->OrderedTests.erase( + std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index)); + this->PendingTests.erase(index); this->Properties.erase(index); - this->TestRunningMap[index] = false; - this->TestFinishMap[index] = true; this->Completed++; } int cmCTestMultiProcessHandler::FindMaxIndex() { int max = 0; - for (auto const& i : this->Tests) { + for (auto const& i : this->PendingTests) { if (i.first > max) { max = i.first; } @@ -1418,7 +1463,7 @@ bool cmCTestMultiProcessHandler::CheckCycles() cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Checking test dependency graph..." << std::endl, this->Quiet); - for (auto const& it : this->Tests) { + for (auto const& it : this->PendingTests) { // DFS from each element to itself int root = it.first; std::set<int> visited; @@ -1428,7 +1473,7 @@ bool cmCTestMultiProcessHandler::CheckCycles() int test = s.top(); s.pop(); if (visited.insert(test).second) { - for (auto const& d : this->Tests[test]) { + for (auto const& d : this->PendingTests[test].Depends) { if (d == root) { // cycle exists cmCTestLog( diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index 3b4e9c5..02589ca 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cstddef> +#include <list> #include <map> #include <memory> #include <set> @@ -13,13 +14,12 @@ #include <cm/optional> -#include <cm3p/uv.h> - #include "cmCTest.h" #include "cmCTestResourceAllocator.h" #include "cmCTestResourceSpec.h" #include "cmCTestTestHandler.h" #include "cmUVHandlePtr.h" +#include "cmUVJobServerClient.h" struct cmCTestBinPackerAllocation; class cmCTestRunTest; @@ -38,7 +38,11 @@ public: struct TestSet : public std::set<int> { }; - struct TestMap : public std::map<int, TestSet> + struct TestInfo + { + TestSet Depends; + }; + struct TestMap : public std::map<int, TestInfo> { }; struct TestList : public std::vector<int> @@ -57,7 +61,7 @@ public: cmCTestMultiProcessHandler(); virtual ~cmCTestMultiProcessHandler(); // Set the tests - void SetTests(TestMap& tests, PropertiesMap& properties); + void SetTests(TestMap tests, PropertiesMap properties); // Set the max number of tests that can be run at the same time. void SetParallelLevel(size_t); void SetTestLoad(unsigned long load); @@ -99,14 +103,14 @@ public: void SetQuiet(bool b) { this->Quiet = b; } - void CheckResourcesAvailable(); + void CheckResourceAvailability(); protected: // Start the next test or tests as many as are allowed by // ParallelLevel void StartNextTests(); - bool StartTestProcess(int test); - bool StartTest(int test); + void StartTestProcess(int test); + void StartTest(int test); // Mark the checkpoint for the given test void WriteCheckpoint(int index); @@ -124,10 +128,10 @@ protected: // Removes the checkpoint file void MarkFinished(); - void EraseTest(int index); void FinishTestProcess(std::unique_ptr<cmCTestRunTest> runner, bool started); - static void OnTestLoadRetryCB(uv_timer_t* timer); + void StartNextTestsOnIdle(); + void StartNextTestsOnTimer(); void RemoveTest(int index); // Check if we need to resume an interrupted test set @@ -143,21 +147,25 @@ protected: bool CheckStopTimePassed(); void SetStopTimePassed(); + void InitializeLoop(); + void FinalizeLoop(); + void LockResources(int index); void UnlockResources(int index); - enum class ResourceAllocationError + enum class ResourceAvailabilityError { NoResourceType, InsufficientResources, }; + bool Complete(); bool AllocateResources(int index); bool TryAllocateResources( int index, std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations, - std::map<std::string, ResourceAllocationError>* errors = nullptr); + std::map<std::string, ResourceAvailabilityError>* errors = nullptr); void DeallocateResources(int index); bool AllResourcesAvailable(); bool InitResourceAllocator(std::string& error); @@ -170,9 +178,10 @@ protected: cm::optional<std::size_t> ResourceSpecSetupTest; bool HasInvalidGeneratedResourceSpec; - // map from test number to set of depend tests - TestMap Tests; - TestList SortedTests; + // Tests pending selection to start. They may have dependencies. + TestMap PendingTests; + // List of pending test indexes, ordered by cost. + std::list<int> OrderedTests; // Total number of tests we'll be running size_t Total; // Number of tests that are complete @@ -183,25 +192,33 @@ protected: bool StopTimePassed = false; // list of test properties (indices concurrent to the test map) PropertiesMap Properties; - std::map<int, bool> TestRunningMap; - std::map<int, bool> TestFinishMap; std::map<int, std::string> TestOutput; std::vector<std::string>* Passed; std::vector<std::string>* Failed; std::vector<std::string> LastTestsFailed; - std::set<std::string> LockedResources; + std::set<std::string> ProjectResourcesLocked; std::map<int, std::vector<std::map<std::string, std::vector<ResourceAllocation>>>> AllocatedResources; - std::map<int, std::map<std::string, ResourceAllocationError>> - ResourceAllocationErrors; + std::map<int, std::map<std::string, ResourceAvailabilityError>> + ResourceAvailabilityErrors; cmCTestResourceAllocator ResourceAllocator; std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults; size_t ParallelLevel; // max number of process that can be run at once + + // 'make' jobserver client. If connected, we acquire a token + // for each test before running its process. + cm::optional<cmUVJobServerClient> JobServerClient; + // List of tests that are queued to run when a token is available. + std::list<int> JobServerQueuedTests; + // Callback invoked when a token is received. + void JobServerReceivedToken(); + unsigned long TestLoad; unsigned long FakeLoadForTesting; - uv_loop_t Loop; - cm::uv_timer_ptr TestLoadRetryTimer; + cm::uv_loop_ptr Loop; + cm::uv_idle_ptr StartNextTestsOnIdle_; + cm::uv_timer_ptr StartNextTestsOnTimer_; cmCTestTestHandler* TestHandler; cmCTest* CTest; bool HasCycles; diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx index 5d71b84..20bd0ec 100644 --- a/Source/CTest/cmCTestP4.cxx +++ b/Source/CTest/cmCTestP4.cxx @@ -151,9 +151,9 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username) if (it == this->Users.end()) { std::vector<std::string> p4_users; this->SetP4Options(p4_users); - p4_users.push_back("users"); - p4_users.push_back("-m"); - p4_users.push_back("1"); + p4_users.emplace_back("users"); + p4_users.emplace_back("-m"); + p4_users.emplace_back("1"); p4_users.push_back(username); UserParser out(this, "users-out> "); @@ -335,10 +335,10 @@ std::string cmCTestP4::GetWorkingRevision() std::vector<std::string> p4_identify; this->SetP4Options(p4_identify); - p4_identify.push_back("changes"); - p4_identify.push_back("-m"); - p4_identify.push_back("1"); - p4_identify.push_back("-t"); + p4_identify.emplace_back("changes"); + p4_identify.emplace_back("-m"); + p4_identify.emplace_back("1"); + p4_identify.emplace_back("-t"); std::string source = this->SourceDirectory + "/...#have"; p4_identify.push_back(source); @@ -403,7 +403,7 @@ bool cmCTestP4::LoadRevisions() .append(",") .append(this->NewRevision); - p4_changes.push_back("changes"); + p4_changes.emplace_back("changes"); p4_changes.push_back(range); ChangesParser out(this, "p4_changes-out> "); @@ -420,8 +420,8 @@ bool cmCTestP4::LoadRevisions() std::vector<std::string> p4_describe; for (std::string const& i : cmReverseRange(this->ChangeLists)) { this->SetP4Options(p4_describe); - p4_describe.push_back("describe"); - p4_describe.push_back("-s"); + p4_describe.emplace_back("describe"); + p4_describe.emplace_back("-s"); p4_describe.push_back(i); DescribeParser outDescribe(this, "p4_describe-out> "); @@ -436,10 +436,10 @@ bool cmCTestP4::LoadModifications() std::vector<std::string> p4_diff; this->SetP4Options(p4_diff); - p4_diff.push_back("diff"); + p4_diff.emplace_back("diff"); // Ideally we would use -Od but not all clients support it - p4_diff.push_back("-dn"); + p4_diff.emplace_back("-dn"); std::string source = this->SourceDirectory + "/..."; p4_diff.push_back(source); @@ -480,7 +480,7 @@ bool cmCTestP4::UpdateImpl() std::vector<std::string> p4_sync; this->SetP4Options(p4_sync); - p4_sync.push_back("sync"); + p4_sync.emplace_back("sync"); // Get user-specified update options. std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions"); diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 8ceb9db..483b3b4 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -25,13 +25,17 @@ #include "cmProcess.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" #include "cmWorkingDirectory.h" -cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler) +cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, + int index) : MultiTestHandler(multiHandler) + , Index(index) + , CTest(MultiTestHandler.CTest) + , TestHandler(MultiTestHandler.TestHandler) + , TestProperties(MultiTestHandler.Properties[Index]) { - this->CTest = multiHandler.CTest; - this->TestHandler = multiHandler.TestHandler; } void cmCTestRunTest::CheckOutput(std::string const& line) @@ -161,7 +165,7 @@ cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed, reason = "Invalid resource spec file"; forceFail = true; } else { - this->MultiTestHandler.CheckResourcesAvailable(); + this->MultiTestHandler.CheckResourceAvailability(); } } std::ostringstream outputStream; @@ -526,7 +530,7 @@ std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const return outputStream.str(); } -bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner, +void cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner, size_t completed, size_t total) { auto* testRun = runner.get(); @@ -535,10 +539,7 @@ bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner, if (!testRun->StartTest(completed, total)) { testRun->FinalizeTest(false); - return false; } - - return true; } // Starts the execution of a test. Returns once it has started @@ -887,7 +888,7 @@ bool cmCTestRunTest::ForkProcess() this->TestResult.Environment.erase(this->TestResult.Environment.length() - 1); - return this->TestProcess->StartProcess(this->MultiTestHandler.Loop, + return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop, &this->TestProperties->Affinity); } diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index 34f23c4..71d0865 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -24,7 +24,7 @@ class cmCTestRunTest { public: - explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler); + explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, int index); void SetNumberOfRuns(int n) { @@ -33,18 +33,12 @@ public: } void SetRepeatMode(cmCTest::Repeat r) { this->RepeatMode = r; } - void SetTestProperties(cmCTestTestHandler::cmCTestTestProperties* prop) - { - this->TestProperties = prop; - } cmCTestTestHandler::cmCTestTestProperties* GetTestProperties() { return this->TestProperties; } - void SetIndex(int i) { this->Index = i; } - int GetIndex() { return this->Index; } void AddFailedDependency(const std::string& failedTest) @@ -62,7 +56,7 @@ public: // Read and store output. Returns true if it must be called again. void CheckOutput(std::string const& line); - static bool StartTest(std::unique_ptr<cmCTestRunTest> runner, + static void StartTest(std::unique_ptr<cmCTestRunTest> runner, size_t completed, size_t total); static bool StartAgain(std::unique_ptr<cmCTestRunTest> runner, size_t completed); @@ -124,16 +118,15 @@ private: // Returns "completed/total Test #Index: " std::string GetTestPrefix(size_t completed, size_t total) const; - cmCTestTestHandler::cmCTestTestProperties* TestProperties; - // Pointer back to the "parent"; the handler that invoked this test run - cmCTestTestHandler* TestHandler; + cmCTestMultiProcessHandler& MultiTestHandler; + int Index; cmCTest* CTest; + cmCTestTestHandler* TestHandler; + cmCTestTestHandler::cmCTestTestProperties* TestProperties; + std::unique_ptr<cmProcess> TestProcess; std::string ProcessOutput; - // The test results cmCTestTestHandler::cmCTestTestResult TestResult; - cmCTestMultiProcessHandler& MultiTestHandler; - int Index; std::set<std::string> FailedDependencies; std::string StartTime; std::string ActualCommand; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 14bc510..fc7051c 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -34,7 +34,7 @@ cmCTestSVN::~cmCTestSVN() = default; void cmCTestSVN::CleanupImpl() { std::vector<std::string> svn_cleanup; - svn_cleanup.push_back("cleanup"); + svn_cleanup.emplace_back("cleanup"); OutputLogger out(this->Log, "cleanup-out> "); OutputLogger err(this->Log, "cleanup-err> "); this->RunSVNCommand(svn_cleanup, &out, &err); @@ -89,7 +89,7 @@ std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo) { // Run "svn info" to get the repository info from the work tree. std::vector<std::string> svn_info; - svn_info.push_back("info"); + svn_info.emplace_back("info"); svn_info.push_back(svninfo.LocalPath); std::string rev; InfoParser out(this, "info-out> ", rev, svninfo); @@ -252,7 +252,7 @@ bool cmCTestSVN::UpdateImpl() } std::vector<std::string> svn_update; - svn_update.push_back("update"); + svn_update.emplace_back("update"); cm::append(svn_update, args); UpdateParser out(this, "up-out> "); @@ -270,7 +270,7 @@ bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, std::vector<std::string> args; args.push_back(this->CommandLineTool); cm::append(args, parameters); - args.push_back("--non-interactive"); + args.emplace_back("--non-interactive"); std::string userOptions = this->CTest->GetCTestConfiguration("SVNOptions"); @@ -388,11 +388,11 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo) // Run "svn log" to get all global revisions of interest. std::vector<std::string> svn_log; - svn_log.push_back("log"); - svn_log.push_back("--xml"); - svn_log.push_back("-v"); - svn_log.push_back(revs.c_str()); - svn_log.push_back(svninfo.LocalPath.c_str()); + svn_log.emplace_back("log"); + svn_log.emplace_back("--xml"); + svn_log.emplace_back("-v"); + svn_log.emplace_back(revs); + svn_log.emplace_back(svninfo.LocalPath); LogParser out(this, "log-out> ", svninfo); OutputLogger err(this->Log, "log-err> "); return this->RunSVNCommand(svn_log, &out, &err); @@ -467,7 +467,7 @@ bool cmCTestSVN::LoadModifications() { // Run "svn status" which reports local modifications. std::vector<std::string> svn_status; - svn_status.push_back("status"); + svn_status.emplace_back("status"); StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunSVNCommand(svn_status, &out, &err); @@ -529,7 +529,7 @@ bool cmCTestSVN::LoadRepositories() // Run "svn status" to get the list of external repositories std::vector<std::string> svn_status; - svn_status.push_back("status"); + svn_status.emplace_back("status"); ExternalParser out(this, "external-out> "); OutputLogger err(this->Log, "external-err> "); return this->RunSVNCommand(svn_status, &out, &err); diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 48f8f6d..0beee67 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -672,9 +672,11 @@ int cmCTestScriptHandler::RunConfigurationDashboard() // clear the binary directory? if (this->EmptyBinDir) { - if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir)) { + std::string err; + if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir, err)) { cmCTestLog(this->CTest, ERROR_MESSAGE, - "Problem removing the binary directory" << std::endl); + "Problem removing the binary directory (" + << err << "): " << this->BinaryDir << std::endl); } } @@ -860,10 +862,12 @@ bool cmCTestScriptHandler::RunScript(cmCTest* ctest, cmMakefile* mf, return true; } -bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname) +bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname, + std::string& err) { // try to avoid deleting root if (sname.size() < 2) { + err = "path too short"; return false; } @@ -876,20 +880,24 @@ bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname) std::string check = cmStrCat(sname, "/CMakeCache.txt"); if (!cmSystemTools::FileExists(check)) { + err = "path does not contain an existing CMakeCache.txt file"; return false; } + cmsys::Status status; for (int i = 0; i < 5; ++i) { - if (TryToRemoveBinaryDirectoryOnce(sname)) { + status = TryToRemoveBinaryDirectoryOnce(sname); + if (status) { return true; } cmSystemTools::Delay(100); } + err = status.GetString(); return false; } -bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce( +cmsys::Status cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce( const std::string& directoryPath) { cmsys::Directory directory; @@ -907,18 +915,18 @@ bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce( bool isDirectory = cmSystemTools::FileIsDirectory(fullPath) && !cmSystemTools::FileIsSymlink(fullPath); + cmsys::Status status; if (isDirectory) { - if (!cmSystemTools::RemoveADirectory(fullPath)) { - return false; - } + status = cmSystemTools::RemoveADirectory(fullPath); } else { - if (!cmSystemTools::RemoveFile(fullPath)) { - return false; - } + status = cmSystemTools::RemoveFile(fullPath); + } + if (!status) { + return status; } } - return static_cast<bool>(cmSystemTools::RemoveADirectory(directoryPath)); + return cmSystemTools::RemoveADirectory(directoryPath); } cmDuration cmCTestScriptHandler::GetRemainingTimeAllowed() diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h index b7764b2..8aa07e7 100644 --- a/Source/CTest/cmCTestScriptHandler.h +++ b/Source/CTest/cmCTestScriptHandler.h @@ -9,6 +9,8 @@ #include <string> #include <vector> +#include "cmsys/Status.hxx" + #include "cmCTestGenericHandler.h" #include "cmDuration.h" @@ -80,7 +82,7 @@ public: /* * Empty Binary Directory */ - static bool EmptyBinaryDirectory(const std::string& dir); + static bool EmptyBinaryDirectory(const std::string& dir, std::string& err); /* * Write an initial CMakeCache.txt from the given contents. @@ -139,7 +141,8 @@ private: std::unique_ptr<cmCTestCommand> command); // Try to remove the binary directory once - static bool TryToRemoveBinaryDirectoryOnce(const std::string& directoryPath); + static cmsys::Status TryToRemoveBinaryDirectoryOnce( + const std::string& directoryPath); std::vector<std::string> ConfigurationScripts; std::vector<bool> ScriptProcessScope; diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index eb3b4dd..1918b2c 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -1381,15 +1381,15 @@ bool cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed, } } } - tests[p.Index] = depends; + tests[p.Index].Depends = depends; properties[p.Index] = &p; } parallel->SetResourceSpecFile(this->ResourceSpecFile); - parallel->SetTests(tests, properties); + parallel->SetTests(std::move(tests), std::move(properties)); parallel->SetPassFailVectors(&passed, &failed); this->TestResults.clear(); parallel->SetTestResults(&this->TestResults); - parallel->CheckResourcesAvailable(); + parallel->CheckResourceAvailability(); if (this->CTest->ShouldPrintLabels()) { parallel->PrintLabels(); @@ -2233,7 +2233,7 @@ bool cmCTestTestHandler::SetTestsProperties( } else if (key == "RESOURCE_LOCK"_s) { cmList lval{ val }; - rt.LockedResources.insert(lval.begin(), lval.end()); + rt.ProjectResources.insert(lval.begin(), lval.end()); } else if (key == "FIXTURES_SETUP"_s) { cmList lval{ val }; diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 23f0a76..b81fcd5 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -165,7 +165,7 @@ public: std::vector<std::string> Environment; std::vector<std::string> EnvironmentModification; std::vector<std::string> Labels; - std::set<std::string> LockedResources; + std::set<std::string> ProjectResources; // RESOURCE_LOCK std::set<std::string> FixturesSetup; std::set<std::string> FixturesCleanup; std::set<std::string> FixturesRequired; diff --git a/Source/CTest/cmUVJobServerClient.cxx b/Source/CTest/cmUVJobServerClient.cxx new file mode 100644 index 0000000..d7d76c9 --- /dev/null +++ b/Source/CTest/cmUVJobServerClient.cxx @@ -0,0 +1,518 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmUVJobServerClient.h" + +#include <cassert> +#include <utility> + +#ifndef _WIN32 +# include <cstdio> +# include <string> +# include <vector> + +# include <fcntl.h> +# include <unistd.h> +#endif + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> + +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmUVHandlePtr.h" + +class cmUVJobServerClient::Impl +{ +public: + uv_loop_t& Loop; + + cm::uv_idle_ptr ImplicitToken; + std::function<void()> OnToken; + std::function<void(int)> OnDisconnect; + + // The number of tokens held by this client. + unsigned int HeldTokens = 0; + + // The number of tokens we need to receive from the job server. + unsigned int NeedTokens = 0; + + Impl(uv_loop_t& loop); + virtual ~Impl(); + + virtual void SendToken() = 0; + virtual void StartReceivingTokens() = 0; + virtual void StopReceivingTokens() = 0; + + void RequestToken(); + void ReleaseToken(); + void RequestExplicitToken(); + void DecrementNeedTokens(); + void HoldToken(); + void RequestImplicitToken(); + void ReleaseImplicitToken(); + void ReceivedToken(); + void Disconnected(int status); +}; + +cmUVJobServerClient::Impl::Impl(uv_loop_t& loop) + : Loop(loop) +{ + this->ImplicitToken.init(this->Loop, this); +} + +cmUVJobServerClient::Impl::~Impl() = default; + +void cmUVJobServerClient::Impl::RequestToken() +{ + if (this->HeldTokens == 0 && !uv_is_active(this->ImplicitToken)) { + this->RequestImplicitToken(); + } else { + this->RequestExplicitToken(); + } +} + +void cmUVJobServerClient::Impl::ReleaseToken() +{ + assert(this->HeldTokens > 0); + --this->HeldTokens; + if (this->HeldTokens == 0) { + // This was the token implicitly owned by our process. + this->ReleaseImplicitToken(); + } else { + // This was a token we received from the job server. Send it back. + this->SendToken(); + } +} + +void cmUVJobServerClient::Impl::RequestExplicitToken() +{ + ++this->NeedTokens; + this->StartReceivingTokens(); +} + +void cmUVJobServerClient::Impl::DecrementNeedTokens() +{ + assert(this->NeedTokens > 0); + --this->NeedTokens; + if (this->NeedTokens == 0) { + this->StopReceivingTokens(); + } +} + +void cmUVJobServerClient::Impl::HoldToken() +{ + ++this->HeldTokens; + if (this->OnToken) { + this->OnToken(); + } else { + this->ReleaseToken(); + } +} + +void cmUVJobServerClient::Impl::RequestImplicitToken() +{ + assert(this->HeldTokens == 0); + this->ImplicitToken.start([](uv_idle_t* handle) { + uv_idle_stop(handle); + auto* self = static_cast<Impl*>(handle->data); + self->HoldToken(); + }); +} + +void cmUVJobServerClient::Impl::ReleaseImplicitToken() +{ + assert(this->HeldTokens == 0); + // Use the implicit token in place of receiving one from the job server. + if (this->NeedTokens > 0) { + this->DecrementNeedTokens(); + this->RequestImplicitToken(); + } +} + +void cmUVJobServerClient::Impl::ReceivedToken() +{ + this->DecrementNeedTokens(); + this->HoldToken(); +} + +void cmUVJobServerClient::Impl::Disconnected(int status) +{ + if (this->OnDisconnect) { + this->OnDisconnect(status); + } +} + +//--------------------------------------------------------------------------- +// Implementation on POSIX platforms. +// https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html + +#ifndef _WIN32 +namespace { +class ImplPosix : public cmUVJobServerClient::Impl +{ +public: + enum class Connection + { + None, + FDs, + FIFO, + }; + Connection Conn = Connection::None; + + cm::uv_pipe_ptr ConnRead; + cm::uv_pipe_ptr ConnWrite; + cm::uv_pipe_ptr ConnFIFO; + + std::shared_ptr<std::function<void(int)>> OnWrite; + + void Connect(); + void ConnectFDs(int rfd, int wfd); + void ConnectFIFO(const char* path); + void Disconnect(int status); + + cm::uv_pipe_ptr OpenFD(int fd); + + uv_stream_t* GetWriter() const; + uv_stream_t* GetReader() const; + + static void OnAllocateCB(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf); + static void OnReadCB(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf); + + void OnAllocate(size_t suggested_size, uv_buf_t* buf); + void OnRead(ssize_t nread, const uv_buf_t* buf); + + char ReadBuf = '.'; + + bool ReceivingTokens = false; + + bool IsConnected() const; + + void SendToken() override; + void StartReceivingTokens() override; + void StopReceivingTokens() override; + + ImplPosix(uv_loop_t& loop); + ~ImplPosix() override; +}; + +ImplPosix::ImplPosix(uv_loop_t& loop) + : Impl(loop) + , OnWrite(std::make_shared<std::function<void(int)>>([this](int status) { + if (status != 0) { + this->Disconnect(status); + } + })) +{ + this->Connect(); +} + +ImplPosix::~ImplPosix() +{ + this->Disconnect(0); +} + +void ImplPosix::Connect() +{ + // --jobserver-auth= for gnu make versions >= 4.2 + // --jobserver-fds= for gnu make versions < 4.2 + // -J for bsd make + static const std::vector<cm::string_view> prefixes = { + "--jobserver-auth=", "--jobserver-fds=", "-J" + }; + + cm::optional<std::string> makeflags = cmSystemTools::GetEnvVar("MAKEFLAGS"); + if (!makeflags) { + return; + } + + // Look for the *last* occurrence of jobserver flags. + cm::optional<std::string> auth; + std::vector<std::string> args; + cmSystemTools::ParseUnixCommandLine(makeflags->c_str(), args); + for (cm::string_view arg : cmReverseRange(args)) { + for (cm::string_view prefix : prefixes) { + if (cmHasPrefix(arg, prefix)) { + auth = cmTrimWhitespace(arg.substr(prefix.length())); + break; + } + } + if (auth) { + break; + } + } + + if (!auth) { + return; + } + + // fifo:PATH + if (cmHasLiteralPrefix(*auth, "fifo:")) { + ConnectFIFO(auth->substr(cmStrLen("fifo:")).c_str()); + return; + } + + // reader,writer + int reader; + int writer; + if (std::sscanf(auth->c_str(), "%d,%d", &reader, &writer) == 2) { + ConnectFDs(reader, writer); + } +} + +cm::uv_pipe_ptr ImplPosix::OpenFD(int fd) +{ + // Create a CLOEXEC duplicate so `uv_pipe_ptr` can close it + // without closing the original file descriptor, which our + // child processes might want to use too. + cm::uv_pipe_ptr p; + int fd_dup = dup(fd); + if (fd_dup < 0) { + return p; + } + if (fcntl(fd_dup, F_SETFD, FD_CLOEXEC) == -1) { + close(fd_dup); + return p; + } + p.init(this->Loop, 0, this); + if (uv_pipe_open(p, fd_dup) < 0) { + close(fd_dup); + } + return p; +} + +void ImplPosix::ConnectFDs(int rfd, int wfd) +{ + cm::uv_pipe_ptr connRead = this->OpenFD(rfd); + cm::uv_pipe_ptr connWrite = this->OpenFD(wfd); + + // Verify that the read end is readable and the write end is writable. + if (!connRead || !uv_is_readable(connRead) || // + !connWrite || !uv_is_writable(connWrite)) { + return; + } + + this->ConnRead = std::move(connRead); + this->ConnWrite = std::move(connWrite); + this->Conn = Connection::FDs; +} + +void ImplPosix::ConnectFIFO(const char* path) +{ + int fd = open(path, O_RDWR); + if (fd < 0) { + return; + } + + cm::uv_pipe_ptr connFIFO; + connFIFO.init(this->Loop, 0, this); + if (uv_pipe_open(connFIFO, fd) != 0) { + close(fd); + return; + } + + // Verify that the fifo is both readable and writable. + if (!connFIFO || !uv_is_readable(connFIFO) || !uv_is_writable(connFIFO)) { + return; + } + + this->ConnFIFO = std::move(connFIFO); + this->Conn = Connection::FIFO; +} + +void ImplPosix::Disconnect(int status) +{ + if (this->Conn == Connection::None) { + return; + } + + this->StopReceivingTokens(); + + switch (this->Conn) { + case Connection::FDs: + this->ConnRead.reset(); + this->ConnWrite.reset(); + break; + case Connection::FIFO: + this->ConnFIFO.reset(); + break; + default: + break; + } + + this->Conn = Connection::None; + if (status != 0) { + this->Disconnected(status); + } +} + +uv_stream_t* ImplPosix::GetWriter() const +{ + switch (this->Conn) { + case Connection::FDs: + return this->ConnWrite; + case Connection::FIFO: + return this->ConnFIFO; + default: + return nullptr; + } +} + +uv_stream_t* ImplPosix::GetReader() const +{ + switch (this->Conn) { + case Connection::FDs: + return this->ConnRead; + case Connection::FIFO: + return this->ConnFIFO; + default: + return nullptr; + } +} + +void ImplPosix::OnAllocateCB(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf) +{ + auto* self = static_cast<ImplPosix*>(handle->data); + self->OnAllocate(suggested_size, buf); +} + +void ImplPosix::OnReadCB(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) +{ + auto* self = static_cast<ImplPosix*>(stream->data); + self->OnRead(nread, buf); +} + +void ImplPosix::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf) +{ + *buf = uv_buf_init(&this->ReadBuf, 1); +} + +void ImplPosix::OnRead(ssize_t nread, const uv_buf_t* /*buf*/) +{ + if (nread == 0) { + return; + } + + if (nread < 0) { + auto status = static_cast<int>(nread); + this->Disconnect(status); + return; + } + + assert(nread == 1); + this->ReceivedToken(); +} + +bool ImplPosix::IsConnected() const +{ + return this->Conn != Connection::None; +} + +void ImplPosix::SendToken() +{ + if (this->Conn == Connection::None) { + return; + } + + static char token = '.'; + + uv_buf_t const buf = uv_buf_init(&token, sizeof(token)); + int status = cm::uv_write(this->GetWriter(), &buf, 1, this->OnWrite); + if (status != 0) { + this->Disconnect(status); + } +} + +void ImplPosix::StartReceivingTokens() +{ + if (this->Conn == Connection::None) { + return; + } + if (this->ReceivingTokens) { + return; + } + + int status = uv_read_start(this->GetReader(), &ImplPosix::OnAllocateCB, + &ImplPosix::OnReadCB); + if (status != 0) { + this->Disconnect(status); + return; + } + + this->ReceivingTokens = true; +} + +void ImplPosix::StopReceivingTokens() +{ + if (this->Conn == Connection::None) { + return; + } + if (!this->ReceivingTokens) { + return; + } + + this->ReceivingTokens = false; + uv_read_stop(this->GetReader()); +} +} +#endif + +//--------------------------------------------------------------------------- +// Implementation of public interface. + +cmUVJobServerClient::cmUVJobServerClient(std::unique_ptr<Impl> impl) + : Impl_(std::move(impl)) +{ +} + +cmUVJobServerClient::~cmUVJobServerClient() = default; + +cmUVJobServerClient::cmUVJobServerClient(cmUVJobServerClient&&) noexcept = + default; +cmUVJobServerClient& cmUVJobServerClient::operator=( + cmUVJobServerClient&&) noexcept = default; + +void cmUVJobServerClient::RequestToken() +{ + this->Impl_->RequestToken(); +} + +void cmUVJobServerClient::ReleaseToken() +{ + this->Impl_->ReleaseToken(); +} + +int cmUVJobServerClient::GetHeldTokens() const +{ + return this->Impl_->HeldTokens; +} + +int cmUVJobServerClient::GetNeedTokens() const +{ + return this->Impl_->NeedTokens; +} + +cm::optional<cmUVJobServerClient> cmUVJobServerClient::Connect( + uv_loop_t& loop, std::function<void()> onToken, + std::function<void(int)> onDisconnect) +{ +#if defined(_WIN32) + // FIXME: Windows job server client not yet implemented. + static_cast<void>(loop); + static_cast<void>(onToken); + static_cast<void>(onDisconnect); +#else + auto impl = cm::make_unique<ImplPosix>(loop); + if (impl && impl->IsConnected()) { + impl->OnToken = std::move(onToken); + impl->OnDisconnect = std::move(onDisconnect); + return cmUVJobServerClient(std::move(impl)); + } +#endif + return cm::nullopt; +} diff --git a/Source/CTest/cmUVJobServerClient.h b/Source/CTest/cmUVJobServerClient.h new file mode 100644 index 0000000..bbb5f08 --- /dev/null +++ b/Source/CTest/cmUVJobServerClient.h @@ -0,0 +1,96 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <functional> +#include <memory> + +#include <cm/optional> + +#include <cm3p/uv.h> + +/** \class cmUVJobServerClient + * \brief Job server client that can integrate with a libuv event loop. + * + * Use the \a Connect method to connect to an ambient job server as + * described by the MAKEFLAGS environment variable, if any. Request + * a token using the \a RequestToken method. The \a onToken callback + * will be invoked asynchronously when the token is received. Act + * on the token, and then use \a ReleaseToken to release it. + * + * The job server protocol states that a client process implicitly + * has one free token available, corresponding to the token its + * parent used to start it. \a cmUVJobServerClient will use the + * implicit token whenever it is available instead of requesting + * an explicit token from the job server. However, clients of + * this class must still request and receive the token before + * acting on it, and cannot assume that it is always held. + * + * If the job server connection breaks, \a onDisconnect will be + * called with the libuv error. No further tokens can be received + * from the job server, but progress can still be made serially + * using the implicit token. + */ +class cmUVJobServerClient +{ +public: + class Impl; + +private: + std::unique_ptr<Impl> Impl_; + + cmUVJobServerClient(std::unique_ptr<Impl> impl); + +public: + /** + * Disconnect from the job server. + */ + ~cmUVJobServerClient(); + + cmUVJobServerClient(cmUVJobServerClient&&) noexcept; + cmUVJobServerClient(cmUVJobServerClient const&) = delete; + cmUVJobServerClient& operator=(cmUVJobServerClient&&) noexcept; + cmUVJobServerClient& operator=(cmUVJobServerClient const&) = delete; + + /** + * Request a token from the job server. + * When the token is held, the \a onToken callback will be invoked. + */ + void RequestToken(); + + /** + * Release a token to the job server. + * This may be called only after a corresponding \a onToken callback. + */ + void ReleaseToken(); + + /** + * Get the number of implicit and explicit tokens currently held. + * This is the number of times \a onToken has been called but not + * yet followed by a call to \a ReleaseToken. + * This is meant for testing and debugging. + */ + int GetHeldTokens() const; + + /** + * Get the number of explicit tokens currently requested from the + * job server but not yet received. If the implicit token becomes + * available, it is used in place of a requested token, and this + * is decremented without receiving an explicit token. + * This is meant for testing and debugging. + */ + int GetNeedTokens() const; + + /** + * Connect to an ambient job server, if any. + * \param loop The libuv event loop on which to schedule events. + * \param onToken Function to call when a new token is held. + * \param onDisconnect Function to call on disconnect, with libuv error. + * \returns Connected instance, or cm::nullopt. + */ + static cm::optional<cmUVJobServerClient> Connect( + uv_loop_t& loop, std::function<void()> onToken, + std::function<void(int)> onDisconnect); +}; diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt index bc6b906..6f5f145 100644 --- a/Source/Checks/Curses/CMakeLists.txt +++ b/Source/Checks/Curses/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) project(CheckCurses C) set(CURSES_NEED_NCURSES TRUE) diff --git a/Source/Checks/Curses/CheckCurses.c b/Source/Checks/Curses/CheckCurses.c index 7d827e6..3264fa0 100644 --- a/Source/Checks/Curses/CheckCurses.c +++ b/Source/Checks/Curses/CheckCurses.c @@ -8,7 +8,7 @@ # include <curses.h> #endif -int main() +int main(void) { curses_version(); return 0; diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx index c8df589..bdc7c6d 100644 --- a/Source/Checks/cm_cxx_filesystem.cxx +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -9,12 +9,13 @@ int main() std::filesystem::path p0(L"/a/b/c"); std::filesystem::path p1("/a/b/c"); - std::filesystem::path p2("/a/b/c"); - if (p1 != p2) { + std::filesystem::path p2("/a/b//c"); + if (p1 != p2.lexically_normal()) { return 1; } #if defined(_WIN32) + // "//host/" is not preserved in some environments like GNU under MinGW. std::filesystem::path p3("//host/a/b/../c"); if (p3.lexically_normal().generic_string() != "//host/a/c") { return 1; @@ -24,6 +25,12 @@ int main() if (p4.lexically_normal().generic_string() != "c:/a/") { return 1; } + + std::filesystem::path b1("C:\\path\\y\\..\\"); + if (std::filesystem::weakly_canonical("\\\\?\\C:\\path\\x\\..") != + b1.lexically_normal()) { + return 1; + } #endif // If std::string is copy-on-write, the std::filesystem::path diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake index 21f04e6..4e7f0fe 100644 --- a/Source/Modules/CMakeBuildUtilities.cmake +++ b/Source/Modules/CMakeBuildUtilities.cmake @@ -287,6 +287,8 @@ else() set(ENABLE_CPIO_SHARED OFF) set(ENABLE_CAT OFF) set(ENABLE_CAT_SHARED OFF) + set(ENABLE_UNZIP OFF) + set(ENABLE_UNZIP_SHARED OFF) set(ENABLE_XATTR OFF) set(ENABLE_ACL OFF) set(ENABLE_ICONV OFF) diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index b1589ff..ea97287 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -189,8 +189,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, } else if (copy == keyDEPFILE) { doing = doing_depfile; if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) { - status.SetError("Option DEPFILE not supported by " + - mf.GetGlobalGenerator()->GetName()); + status.SetError(cmStrCat("Option DEPFILE not supported by ", + mf.GetGlobalGenerator()->GetName())); return false; } } else if (copy == keyJOB_POOL) { diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 0efb9a4..699e23b 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -270,7 +270,7 @@ std::map<std::string, std::string> GetOSReleaseVariables( std::map<std::string, std::string> data; // Based on - // https://www.freedesktop.org/software/systemd/man/os-release.html + // https://www.freedesktop.org/software/systemd/man/latest/os-release.html for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) { const auto& filename = cmStrCat(sysroot, name); if (cmSystemTools::FileExists(filename)) { diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 320c57c..f4b26f3 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -16,6 +16,8 @@ #include <cm/string_view> #include <cmext/string_view> +#include "cmsys/RegularExpression.hxx" + #include "cmComputeComponentGraph.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionDAGChecker.h" @@ -26,6 +28,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmRange.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -184,15 +187,6 @@ items that we know the linker will reuse automatically (shared libs). namespace { // LINK_LIBRARY helpers -const auto LL_BEGIN = "<LINK_LIBRARY:"_s; -const auto LL_END = "</LINK_LIBRARY:"_s; - -inline std::string ExtractFeature(std::string const& item) -{ - return item.substr(LL_BEGIN.length(), - item.find('>', LL_BEGIN.length()) - LL_BEGIN.length()); -} - bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage, std::string const& feature) { @@ -231,9 +225,208 @@ bool IsGroupFeatureSupported(cmMakefile* makefile, cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED"); return makefile->GetDefinition(featureSupported).IsOn(); } + +class EntriesProcessing +{ +public: + using LinkEntry = cmComputeLinkDepends::LinkEntry; + using EntryVector = cmComputeLinkDepends::EntryVector; + + EntriesProcessing(const cmGeneratorTarget* target, + const std::string& linkLanguage, EntryVector& entries, + EntryVector& finalEntries) + : Entries(entries) + , FinalEntries(finalEntries) + { + const auto* makefile = target->Makefile; + + switch (target->GetPolicyStatusCMP0156()) { + case cmPolicies::WARN: + if (!makefile->GetCMakeInstance()->GetIsInTryCompile() && + makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0156")) { + makefile->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156), + "\nSince the policy is not set, legacy libraries " + "de-duplication strategy will be applied."), + target->GetBacktrace()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // rely on default initialization of the class + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0156), + target->GetBacktrace()); + CM_FALLTHROUGH; + case cmPolicies::NEW: { + if (auto libProcessing = makefile->GetDefinition(cmStrCat( + "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) { + cmsys::RegularExpression processingOption{ + "^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$" + }; + std::string errorMessage; + for (auto const& option : cmList{ libProcessing }) { + if (processingOption.find(option)) { + if (processingOption.match(1) == "ORDER") { + if (processingOption.match(2) == "FORWARD") { + this->Order = Forward; + } else if (processingOption.match(2) == "REVERSE") { + this->Order = Reverse; + } else { + errorMessage += cmStrCat(" ", option, '\n'); + } + } else if (processingOption.match(1) == "UNICITY") { + if (processingOption.match(2) == "ALL") { + this->Unicity = All; + } else if (processingOption.match(2) == "NONE") { + this->Unicity = None; + } else if (processingOption.match(2) == "SHARED") { + this->Unicity = Shared; + } else { + errorMessage += cmStrCat(" ", option, '\n'); + } + } + } else { + errorMessage += cmStrCat(" ", option, '\n'); + } + } + if (!errorMessage.empty()) { + makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Erroneous option(s) for 'CMAKE_", linkLanguage, + "_LINK_LIBRARIES_PROCESSING':\n", errorMessage), + target->GetBacktrace()); + } + } + } + } + } + + void AddGroups(const std::map<size_t, std::vector<size_t>>& groups) + { + if (!groups.empty()) { + this->Groups = &groups; + // record all libraries as part of groups to ensure correct + // deduplication: libraries as part of groups are always kept. + for (const auto& group : groups) { + for (auto index : group.second) { + this->Emitted.insert(index); + } + } + } + } + + void AddLibraries(const std::vector<size_t>& libEntries) + { + if (this->Order == Reverse) { + // Iterate in reverse order so we can keep only the last occurrence + // of a library. + this->AddLibraries(cmReverseRange(libEntries)); + } else { + this->AddLibraries(cmMakeRange(libEntries)); + } + } + + void AddObjects(const std::vector<size_t>& objectEntries) + { + // Place explicitly linked object files in the front. The linker will + // always use them anyway, and they may depend on symbols from libraries. + if (this->Order == Reverse) { + // Append in reverse order at the end since we reverse the final order. + for (auto index : cmReverseRange(objectEntries)) { + this->FinalEntries.emplace_back(this->Entries[index]); + } + } else { + // Append in reverse order to ensure correct final order + for (auto index : cmReverseRange(objectEntries)) { + this->FinalEntries.emplace(this->FinalEntries.begin(), + this->Entries[index]); + } + } + } + + void Finalize() + { + if (this->Order == Reverse) { + // Reverse the resulting order since we iterated in reverse. + std::reverse(this->FinalEntries.begin(), this->FinalEntries.end()); + } + + // expand groups + if (this->Groups != nullptr) { + for (const auto& group : *this->Groups) { + const LinkEntry& groupEntry = this->Entries[group.first]; + auto it = this->FinalEntries.begin(); + while (true) { + it = std::find_if(it, this->FinalEntries.end(), + [&groupEntry](const LinkEntry& entry) -> bool { + return groupEntry.Item == entry.Item; + }); + if (it == this->FinalEntries.end()) { + break; + } + it->Item.Value = "</LINK_GROUP>"; + for (auto index = group.second.rbegin(); + index != group.second.rend(); ++index) { + it = this->FinalEntries.insert(it, this->Entries[*index]); + } + it = this->FinalEntries.insert(it, groupEntry); + it->Item.Value = "<LINK_GROUP>"; + } + } + } + } + +private: + enum OrderKind + { + Forward, + Reverse + }; + + enum UnicityKind + { + None, + Shared, + All + }; + + bool IncludeEntry(LinkEntry const& entry) const + { + return this->Unicity == None || + (this->Unicity == Shared && + (entry.Target == nullptr || + entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) || + (this->Unicity == All && entry.Kind != LinkEntry::Library); + } + + template <typename Range> + void AddLibraries(const Range& libEntries) + { + for (auto index : libEntries) { + LinkEntry const& entry = this->Entries[index]; + if (this->IncludeEntry(entry) || this->Emitted.insert(index).second) { + this->FinalEntries.emplace_back(entry); + } + } + } + + OrderKind Order = Reverse; + UnicityKind Unicity = Shared; + EntryVector& Entries; + EntryVector& FinalEntries; + std::set<size_t> Emitted; + const std::map<size_t, std::vector<size_t>>* Groups = nullptr; +}; } -const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT"; +std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT = + cmLinkItem::DEFAULT; cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, const std::string& config, @@ -376,49 +569,14 @@ cmComputeLinkDepends::Compute() this->OrderLinkEntries(); // Compute the final set of link entries. - // Iterate in reverse order so we can keep only the last occurrence - // of a shared library. - std::set<size_t> emitted; - for (size_t i : cmReverseRange(this->FinalLinkOrder)) { - LinkEntry const& e = this->EntryList[i]; - cmGeneratorTarget const* t = e.Target; - // Entries that we know the linker will reuse do not need to be repeated. - bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY; - if (!uniquify || emitted.insert(i).second) { - this->FinalLinkEntries.push_back(e); - } - } - // Place explicitly linked object files in the front. The linker will - // always use them anyway, and they may depend on symbols from libraries. - // Append in reverse order since we reverse the final order below. - for (size_t i : cmReverseRange(this->ObjectEntries)) { - this->FinalLinkEntries.emplace_back(this->EntryList[i]); - } - // Reverse the resulting order since we iterated in reverse. - std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end()); - - // Expand group items - if (!this->GroupItems.empty()) { - for (const auto& group : this->GroupItems) { - const LinkEntry& groupEntry = this->EntryList[group.first]; - auto it = this->FinalLinkEntries.begin(); - while (true) { - it = std::find_if(it, this->FinalLinkEntries.end(), - [&groupEntry](const LinkEntry& entry) -> bool { - return groupEntry.Item == entry.Item; - }); - if (it == this->FinalLinkEntries.end()) { - break; - } - it->Item.Value = "</LINK_GROUP>"; - for (auto i = group.second.rbegin(); i != group.second.rend(); ++i) { - it = this->FinalLinkEntries.insert(it, this->EntryList[*i]); - } - it = this->FinalLinkEntries.insert(it, groupEntry); - it->Item.Value = "<LINK_GROUP>"; - } - } - } + EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage, + this->EntryList, + this->FinalLinkEntries }; + // Add groups first, to ensure that libraries of the groups are always kept. + entriesProcessing.AddGroups(this->GroupItems); + entriesProcessing.AddLibraries(this->FinalLinkOrder); + entriesProcessing.AddObjects(this->ObjectEntries); + entriesProcessing.Finalize(); // Display the final set. if (this->DebugMode) { @@ -466,10 +624,12 @@ std::pair<size_t, bool> cmComputeLinkDepends::AddLinkEntry( LinkEntry& entry = this->EntryList[index]; entry.Item = BT<std::string>(item.AsStr(), item.Backtrace); entry.Target = item.Target; + entry.Feature = item.Feature; if (!entry.Target && entry.Item.Value[0] == '-' && entry.Item.Value[1] != 'l' && entry.Item.Value.substr(0, 10) != "-framework") { entry.Kind = LinkEntry::Flag; + entry.Feature = LinkEntry::DEFAULT; } else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) && cmHasSuffix(entry.Item.Value, '>')) { entry.Kind = LinkEntry::Group; @@ -710,7 +870,6 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, { // Track inferred dependency sets implied by this list. std::map<size_t, DependSet> dependSets; - std::string feature = LinkEntry::DEFAULT; bool inGroup = false; std::pair<size_t, bool> groupIndex{ @@ -727,34 +886,27 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, continue; } - if (cmHasPrefix(item.AsStr(), LL_BEGIN) && - cmHasSuffix(item.AsStr(), '>')) { - feature = ExtractFeature(item.AsStr()); - // emit a warning if an undefined feature is used as part of - // an imported target - if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { - const auto& depender = this->EntryList[depender_index]; - if (depender.Target != nullptr && depender.Target->IsImported() && - !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) { - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_ERROR, - cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), - "' uses the generator-expression '$<LINK_LIBRARY>' with " - "the feature '", - feature, - "', which is undefined or unsupported.\nDid you miss to " - "define it by setting variables \"CMAKE_", - this->LinkLanguage, "_LINK_LIBRARY_USING_", feature, - "\" and \"CMAKE_", this->LinkLanguage, - "_LINK_LIBRARY_USING_", feature, "_SUPPORTED\"?"), - this->Target->GetBacktrace()); - } + // emit a warning if an undefined feature is used as part of + // an imported target + if (item.Feature != LinkEntry::DEFAULT && + depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { + const auto& depender = this->EntryList[depender_index]; + if (depender.Target != nullptr && depender.Target->IsImported() && + !IsFeatureSupported(this->Makefile, this->LinkLanguage, + item.Feature)) { + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_ERROR, + cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), + "' uses the generator-expression '$<LINK_LIBRARY>' with " + "the feature '", + item.Feature, + "', which is undefined or unsupported.\nDid you miss to " + "define it by setting variables \"CMAKE_", + this->LinkLanguage, "_LINK_LIBRARY_USING_", item.Feature, + "\" and \"CMAKE_", this->LinkLanguage, + "_LINK_LIBRARY_USING_", item.Feature, "_SUPPORTED\"?"), + this->Target->GetBacktrace()); } - continue; - } - if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) { - feature = LinkEntry::DEFAULT; - continue; } if (cmHasPrefix(item.AsStr(), LG_BEGIN) && @@ -815,7 +967,7 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, dependee_index = ale.first; LinkEntry& entry = this->EntryList[dependee_index]; auto const& itemFeature = - this->GetCurrentFeature(entry.Item.Value, feature); + this->GetCurrentFeature(entry.Item.Value, item.Feature); if (inGroup && ale.second && entry.Target != nullptr && (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY || entry.Target->GetType() == @@ -834,30 +986,27 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, " library '", entry.Item.Value, "'."), this->Target->GetBacktrace()); } - if (itemFeature != LinkEntry::DEFAULT) { - if (ale.second) { - // current item not yet defined - if (entry.Target != nullptr && - (entry.Target->GetType() == - cmStateEnums::TargetType::OBJECT_LIBRARY || - entry.Target->GetType() == - cmStateEnums::TargetType::INTERFACE_LIBRARY)) { - this->CMakeInstance->IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat("The feature '", feature, - "', specified as part of a generator-expression " - "'$", - LL_BEGIN, feature, ">', will not be applied to the ", - (entry.Target->GetType() == - cmStateEnums::TargetType::OBJECT_LIBRARY - ? "OBJECT" - : "INTERFACE"), - " library '", entry.Item.Value, "'."), - this->Target->GetBacktrace()); - } else { - entry.Feature = itemFeature; - } + if (ale.second) { + // current item not yet defined + if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr && + (entry.Target->GetType() == + cmStateEnums::TargetType::OBJECT_LIBRARY || + entry.Target->GetType() == + cmStateEnums::TargetType::INTERFACE_LIBRARY)) { + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("The feature '", itemFeature, + "', specified as part of a generator-expression " + "'$<LINK_LIBRARY:", + itemFeature, ">', will not be applied to the ", + (entry.Target->GetType() == + cmStateEnums::TargetType::OBJECT_LIBRARY + ? "OBJECT" + : "INTERFACE"), + " library '", entry.Item.Value, "'."), + this->Target->GetBacktrace()); } + entry.Feature = itemFeature; } bool supportedItem = entry.Target == nullptr || diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 3233217..66daa07 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -49,7 +49,7 @@ public: { } - static const std::string DEFAULT; + static std::string const& DEFAULT; enum EntryKind { diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx index 6f9f541..eba4c57 100644 --- a/Source/cmConditionEvaluator.cxx +++ b/Source/cmConditionEvaluator.cxx @@ -33,6 +33,9 @@ auto const keyCOMMAND = "COMMAND"_s; auto const keyDEFINED = "DEFINED"_s; auto const keyEQUAL = "EQUAL"_s; auto const keyEXISTS = "EXISTS"_s; +auto const keyIS_READABLE = "IS_READABLE"_s; +auto const keyIS_WRITABLE = "IS_WRITABLE"_s; +auto const keyIS_EXECUTABLE = "IS_EXECUTABLE"_s; auto const keyGREATER = "GREATER"_s; auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s; auto const keyIN_LIST = "IN_LIST"_s; @@ -568,6 +571,24 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&, newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()), args); } + // check if a file is readable + else if (this->IsKeyword(keyIS_READABLE, *args.current)) { + newArgs.ReduceOneArg(cmSystemTools::TestFileAccess( + args.next->GetValue(), cmsys::TEST_FILE_READ), + args); + } + // check if a file is writable + else if (this->IsKeyword(keyIS_WRITABLE, *args.current)) { + newArgs.ReduceOneArg(cmSystemTools::TestFileAccess( + args.next->GetValue(), cmsys::TEST_FILE_WRITE), + args); + } + // check if a file is executable + else if (this->IsKeyword(keyIS_EXECUTABLE, *args.current)) { + newArgs.ReduceOneArg(cmSystemTools::TestFileAccess( + args.next->GetValue(), cmsys::TEST_FILE_EXECUTE), + args); + } // does a directory with this name exist else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) { newArgs.ReduceOneArg( diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 8c84ace..a4f36cc 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -14,6 +14,7 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" #include "cmArgumentParser.h" #include "cmConfigureLog.h" @@ -83,6 +84,7 @@ std::string const kCMAKE_HIP_PLATFORM = "CMAKE_HIP_PLATFORM"; std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY"; std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS"; std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX"; +std::string const kCMAKE_LINKER_TYPE = "CMAKE_LINKER_TYPE"; std::string const kCMAKE_LINK_SEARCH_END_STATIC = "CMAKE_LINK_SEARCH_END_STATIC"; std::string const kCMAKE_LINK_SEARCH_START_STATIC = @@ -176,6 +178,7 @@ auto const TryCompileBaseSourcesArgParser = ArgumentParser::ExpectAtLeast{ 0 }) .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries) .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions) + .Bind("LINKER_LANGUAGE"_s, &Arguments::LinkerLanguage) .Bind("COPY_FILE"_s, &Arguments::CopyFileTo) .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError) .BIND_LANG_PROPS(C) @@ -852,8 +855,30 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( fclose(fout); return cm::nullopt; } - fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n", + fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n", fname.c_str()); + // Create all relevant alias targets + if (arguments.LinkLibraries) { + const auto& aliasTargets = this->Makefile->GetAliasTargets(); + for (std::string const& i : *arguments.LinkLibraries) { + auto alias = aliasTargets.find(i); + if (alias != aliasTargets.end()) { + const auto& aliasTarget = + this->Makefile->FindTargetToUse(alias->second); + // Create equivalent library/executable alias + if (aliasTarget->GetType() == cmStateEnums::EXECUTABLE) { + fprintf(fout, "add_executable(\"%s\" ALIAS \"%s\")\n", i.c_str(), + alias->second.c_str()); + } else { + // Other cases like UTILITY and GLOBAL_TARGET are excluded when + // arguments.LinkLibraries is initially parsed in this function. + fprintf(fout, "add_library(\"%s\" ALIAS \"%s\")\n", i.c_str(), + alias->second.c_str()); + } + } + } + } + fprintf(fout, "\n"); } /* Set the appropriate policy information for ENABLE_EXPORTS */ @@ -877,6 +902,14 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( ? "NEW" : "OLD"); + /* Set the appropriate policy information for Swift compilation mode */ + fprintf( + fout, "cmake_policy(SET CMP0157 %s)\n", + this->Makefile->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT") + .IsEmpty() + ? "OLD" + : "NEW"); + // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid // adding that flag in the platform and compiler language files fprintf(fout, @@ -1041,6 +1074,19 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( } } + if (arguments.LinkerLanguage) { + std::string LinkerLanguage = *arguments.LinkerLanguage; + if (testLangs.find(LinkerLanguage) == testLangs.end()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "Linker language '" + LinkerLanguage + + "' must be enabled in project(LANGUAGES)."); + } + + fprintf(fout, "set_property(TARGET %s PROPERTY LINKER_LANGUAGE %s)\n", + targetName.c_str(), LinkerLanguage.c_str()); + } + if (arguments.LinkLibraries) { std::string libsToLink = " "; for (std::string const& i : *arguments.LinkLibraries) { @@ -1112,6 +1158,20 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( vars.insert(varList.begin(), varList.end()); } + if (this->Makefile->GetDefinition(kCMAKE_LINKER_TYPE)) { + // propagate various variables to support linker selection + vars.insert(kCMAKE_LINKER_TYPE); + auto defs = this->Makefile->GetDefinitions(); + cmsys::RegularExpression linkerTypeDef{ + "^CMAKE_[A-Za-z]+_USING_LINKER_" + }; + for (auto const& def : defs) { + if (linkerTypeDef.find(def)) { + vars.insert(def); + } + } + } + if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) == cmPolicies::NEW) { // To ensure full support of PIE, propagate cache variables diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 3217a1b..6a26e88 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -91,6 +91,7 @@ public: cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> LinkLibraries; ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions; + cm::optional<std::string> LinkerLanguage; std::map<std::string, std::string> LangProps; std::string CMakeInternal; cm::optional<std::string> OutputVariable; diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 75c25e3..9edbafe 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -81,8 +81,8 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, } std::string func_name; if (!cmSystemTools::GetFilenamePath(*i).empty()) { - func_name = cmSystemTools::GetFilenamePath(*i) + "/" + - cmSystemTools::GetFilenameWithoutLastExtension(*i); + func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/', + cmSystemTools::GetFilenameWithoutLastExtension(*i)); } else { func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i); } @@ -93,9 +93,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, tests_func_name.end(); tests_func_name.push_back(func_name); if (!already_declared) { - forwardDeclareCode += "int "; - forwardDeclareCode += func_name; - forwardDeclareCode += "(int, char*[]);\n"; + forwardDeclareCode += cmStrCat("int ", func_name, "(int, char*[]);\n"); } } @@ -105,8 +103,8 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, ++i, ++j) { std::string func_name; if (!cmSystemTools::GetFilenamePath(*i).empty()) { - func_name = cmSystemTools::GetFilenamePath(*i) + "/" + - cmSystemTools::GetFilenameWithoutLastExtension(*i); + func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/', + cmSystemTools::GetFilenameWithoutLastExtension(*i)); } else { func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i); } @@ -137,12 +135,12 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, { cmSourceFile* sf = mf.GetOrCreateSource(driver); sf->SetProperty("ABSTRACT", "0"); - sourceListValue = args[1]; + sourceListValue = driver; } for (i = testsBegin; i != tests.end(); ++i) { cmSourceFile* sf = mf.GetOrCreateSource(*i); sf->SetProperty("ABSTRACT", "0"); - sourceListValue += ";"; + sourceListValue += ';'; sourceListValue += *i; } diff --git a/Source/cmDebuggerStackFrame.h b/Source/cmDebuggerStackFrame.h index dc3b2ab..f4e6612 100644 --- a/Source/cmDebuggerStackFrame.h +++ b/Source/cmDebuggerStackFrame.h @@ -28,6 +28,10 @@ public: std::string const& GetFileName() const noexcept { return this->FileName; } int64_t GetLine() const noexcept; cmMakefile* GetMakefile() const noexcept { return this->Makefile; } + cmListFileFunction const& GetFunction() const noexcept + { + return this->Function; + } }; } // namespace cmDebugger diff --git a/Source/cmDebuggerThread.cxx b/Source/cmDebuggerThread.cxx index fd52f5a..f7a1778 100644 --- a/Source/cmDebuggerThread.cxx +++ b/Source/cmDebuggerThread.cxx @@ -13,6 +13,7 @@ #include "cmDebuggerVariables.h" #include "cmDebuggerVariablesHelper.h" #include "cmDebuggerVariablesManager.h" +#include "cmListFileCache.h" namespace cmDebugger { @@ -135,8 +136,7 @@ dap::StackTraceResponse GetStackTraceResponse( #endif stackFrame.line = thread->Frames[i]->GetLine(); stackFrame.column = 1; - stackFrame.name = thread->Frames[i]->GetFileName() + " Line " + - std::to_string(stackFrame.line); + stackFrame.name = thread->Frames[i]->GetFunction().OriginalName(); stackFrame.id = thread->Frames[i]->GetId(); stackFrame.source = source; diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index edf56ff..ea46f03 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx @@ -242,14 +242,16 @@ Json::Value CollationInformationExports(cmGeneratorTarget const* gt) auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators(); for (auto const& exp : all_build_exports) { - std::vector<std::string> targets; + std::vector<cmExportBuildFileGenerator::TargetExport> targets; exp->GetTargets(targets); // Ignore exports sets which are not for this target. auto const& name = gt->GetName(); bool has_current_target = std::any_of(targets.begin(), targets.end(), - [name](std::string const& tname) { return tname == name; }); + [name](cmExportBuildFileGenerator::TargetExport const& te) { + return te.Name == name; + }); if (!has_current_target) { continue; } diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index c54e6ac..34bda1b 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx @@ -57,8 +57,8 @@ void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( } void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, cmGeneratorTarget const*, - ImportPropertyMap const&) + std::ostream&, const std::string&, const std::string&, + cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) { } diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index 7067488..9562cee 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -51,10 +51,11 @@ protected: void GenerateExpectedTargetsCode( std::ostream& os, const std::string& expectedTargets) override; void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, - cmGeneratorTarget const* target, - ImportPropertyMap const& properties) override; + std::ostream& os, const std::string& config, const std::string& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + const std::string& importedXcFrameworkLocation) override; void GenerateMissingTargetsCheckCode(std::ostream& os) override; + void GenerateFindDependencyCalls(std::ostream&) override {} void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties) override; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index bf3bb8b..8ba8d97 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -10,7 +10,6 @@ #include <utility> #include <cm/string_view> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmExportSet.h" @@ -54,15 +53,15 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) { std::string expectedTargets; std::string sep; - std::vector<std::string> targets; + std::vector<TargetExport> targets; bool generatedInterfaceRequired = false; this->GetTargets(targets); - for (std::string const& tei : targets) { - cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei); + for (auto const& tei : targets) { + cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name); expectedTargets += sep + this->Namespace + te->GetExportName(); sep = " "; if (this->ExportedTargets.insert(te).second) { - this->Exports.push_back(te); + this->Exports.emplace_back(te, tei.XcFrameworkLocation); } else { std::ostringstream e; e << "given target \"" << te->GetName() << "\" more than once."; @@ -76,13 +75,14 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) } if (generatedInterfaceRequired) { - this->GenerateRequiredCMakeVersion(os, "3.0.0"); + this->SetRequiredCMakeVersion(3, 0, 0); } this->GenerateExpectedTargetsCode(os, expectedTargets); } // Create all the imported targets. - for (cmGeneratorTarget* gte : this->Exports) { + for (auto const& exp : this->Exports) { + cmGeneratorTarget* gte = exp.Target; this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); gte->Target->AppendBuildInterfaceIncludes(); @@ -176,7 +176,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) void cmExportBuildFileGenerator::GenerateImportTargetsConfig( std::ostream& os, const std::string& config, std::string const& suffix) { - for (cmGeneratorTarget* target : this->Exports) { + for (auto const& exp : this->Exports) { + cmGeneratorTarget* target = exp.Target; + // Collect import properties for this target. ImportPropertyMap properties; @@ -200,7 +202,23 @@ void cmExportBuildFileGenerator::GenerateImportTargetsConfig( // properties); // Generate code in the export file. - this->GenerateImportPropertyCode(os, config, target, properties); + std::string importedXcFrameworkLocation = exp.XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::BuildInterface); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config, + exp.Target, nullptr, exp.Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) { + importedXcFrameworkLocation = + cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', + importedXcFrameworkLocation); + } + } + this->GenerateImportPropertyCode(os, config, suffix, target, properties, + importedXcFrameworkLocation); } } } @@ -308,7 +326,7 @@ void cmExportBuildFileGenerator::HandleMissingTarget( } void cmExportBuildFileGenerator::GetTargets( - std::vector<std::string>& targets) const + std::vector<TargetExport>& targets) const { if (this->ExportSet) { for (std::unique_ptr<cmTargetExport> const& te : @@ -316,7 +334,7 @@ void cmExportBuildFileGenerator::GetTargets( if (te->NamelinkOnly) { continue; } - targets.push_back(te->TargetName); + targets.emplace_back(te->TargetName, te->XcFrameworkLocation); } return; } @@ -334,9 +352,11 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg, for (auto const& exp : exportSets) { const auto& exportSet = exp.second; - std::vector<std::string> targets; + std::vector<TargetExport> targets; exportSet->GetTargets(targets); - if (cm::contains(targets, name)) { + if (std::any_of( + targets.begin(), targets.end(), + [&name](const TargetExport& te) { return te.Name == name; })) { exportFiles.push_back(exp.first); ns = exportSet->GetNamespace(); } diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 4636196..9f11d13 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -33,15 +33,27 @@ class cmTargetExport; class cmExportBuildFileGenerator : public cmExportFileGenerator { public: + struct TargetExport + { + TargetExport(std::string name, std::string xcFrameworkLocation) + : Name(std::move(name)) + , XcFrameworkLocation(std::move(xcFrameworkLocation)) + { + } + + std::string Name; + std::string XcFrameworkLocation; + }; + cmExportBuildFileGenerator(); /** Set the list of targets to export. */ - void SetTargets(std::vector<std::string> const& targets) + void SetTargets(std::vector<TargetExport> const& targets) { this->Targets = targets; } - void GetTargets(std::vector<std::string>& targets) const; - void AppendTargets(std::vector<std::string> const& targets) + void GetTargets(std::vector<TargetExport>& targets) const; + void AppendTargets(std::vector<TargetExport> const& targets) { cm::append(this->Targets, targets); } @@ -90,6 +102,7 @@ protected: cmTargetExport* te) override; std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + cmExportSet* GetExportSet() const override { return this->ExportSet; } std::string GetCxxModulesDirectory() const override; void GenerateCxxModuleConfigInformation(std::ostream&) const override; @@ -98,9 +111,22 @@ protected: std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( cmGlobalGenerator* gg, const std::string& name); - std::vector<std::string> Targets; + struct TargetExportPrivate + { + TargetExportPrivate(cmGeneratorTarget* target, + std::string xcFrameworkLocation) + : Target(target) + , XcFrameworkLocation(std::move(xcFrameworkLocation)) + { + } + + cmGeneratorTarget* Target; + std::string XcFrameworkLocation; + }; + + std::vector<TargetExport> Targets; cmExportSet* ExportSet; - std::vector<cmGeneratorTarget*> Exports; + std::vector<TargetExportPrivate> Exports; cmLocalGenerator* LG; // The directory for C++ module information. std::string CxxModulesDirectory; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 7e44210..7d23c91 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -8,6 +8,7 @@ #include <cm/memory> #include <cm/optional> +#include <cmext/algorithm> #include <cmext/string_view> #include "cmsys/RegularExpression.hxx" @@ -24,10 +25,12 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmValue.h" #if defined(__HAIKU__) # include <FindDirectory.h> @@ -66,6 +69,11 @@ bool cmExportCommand(std::vector<std::string> const& args, std::string CxxModulesDirectory; bool Append = false; bool ExportOld = false; + + std::vector<std::vector<std::string>> PackageDependencyArgs; + bool ExportPackageDependencies = false; + + std::vector<std::vector<std::string>> TargetArgs; }; auto parser = @@ -75,7 +83,13 @@ bool cmExportCommand(std::vector<std::string> const& args, .Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory); if (args[0] == "EXPORT") { - parser.Bind("EXPORT"_s, &Arguments::ExportSetName); + parser.Bind("EXPORT"_s, &Arguments::ExportSetName) + .Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, + &Arguments::ExportPackageDependencies); + } else if (args[0] == "SETUP") { + parser.Bind("SETUP"_s, &Arguments::ExportSetName); + parser.Bind("PACKAGE_DEPENDENCY"_s, &Arguments::PackageDependencyArgs); + parser.Bind("TARGET"_s, &Arguments::TargetArgs); } else { parser.Bind("TARGETS"_s, &Arguments::Targets); parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile); @@ -91,6 +105,91 @@ bool cmExportCommand(std::vector<std::string> const& args, return false; } + if (args[0] == "SETUP") { + cmMakefile& mf = status.GetMakefile(); + cmGlobalGenerator* gg = mf.GetGlobalGenerator(); + + cmExportSetMap& setMap = gg->GetExportSets(); + auto& exportSet = setMap[arguments.ExportSetName]; + + struct PackageDependencyArguments + { + std::string Enabled; + ArgumentParser::MaybeEmpty<std::vector<std::string>> ExtraArgs; + }; + + auto packageDependencyParser = + cmArgumentParser<PackageDependencyArguments>{} + .Bind("ENABLED"_s, &PackageDependencyArguments::Enabled) + .Bind("EXTRA_ARGS"_s, &PackageDependencyArguments::ExtraArgs); + + for (auto const& packageDependencyArgs : arguments.PackageDependencyArgs) { + if (packageDependencyArgs.empty()) { + continue; + } + + PackageDependencyArguments const packageDependencyArguments = + packageDependencyParser.Parse( + cmMakeRange(packageDependencyArgs).advance(1), &unknownArgs); + + if (!unknownArgs.empty()) { + status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); + return false; + } + + auto& packageDependency = + exportSet.GetPackageDependencyForSetup(packageDependencyArgs.front()); + + if (!packageDependencyArguments.Enabled.empty()) { + if (packageDependencyArguments.Enabled == "AUTO") { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::Auto; + } else if (cmIsOff(packageDependencyArguments.Enabled)) { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::Off; + } else if (cmIsOn(packageDependencyArguments.Enabled)) { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::On; + } else { + status.SetError( + cmStrCat("Invalid enable setting for package dependency: \"", + packageDependencyArguments.Enabled, "\"")); + return false; + } + } + + cm::append(packageDependency.ExtraArguments, + packageDependencyArguments.ExtraArgs); + } + + struct TargetArguments + { + std::string XcFrameworkLocation; + }; + + auto targetParser = cmArgumentParser<TargetArguments>{}.Bind( + "XCFRAMEWORK_LOCATION"_s, &TargetArguments::XcFrameworkLocation); + + for (auto const& targetArgs : arguments.TargetArgs) { + if (targetArgs.empty()) { + continue; + } + + TargetArguments const targetArguments = + targetParser.Parse(cmMakeRange(targetArgs).advance(1), &unknownArgs); + + if (!unknownArgs.empty()) { + status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); + return false; + } + + exportSet.SetXcFrameworkLocation(targetArgs.front(), + targetArguments.XcFrameworkLocation); + } + + return true; + } + std::string fname; bool android = false; if (!arguments.AndroidMKFile.empty()) { @@ -133,7 +232,7 @@ bool cmExportCommand(std::vector<std::string> const& args, fname = dir + "/" + fname; } - std::vector<std::string> targets; + std::vector<cmExportBuildFileGenerator::TargetExport> targets; cmGlobalGenerator* gg = mf.GetGlobalGenerator(); @@ -171,7 +270,7 @@ bool cmExportCommand(std::vector<std::string> const& args, status.SetError(e.str()); return false; } - targets.push_back(currentTarget); + targets.emplace_back(currentTarget, std::string{}); } if (arguments.Append) { if (cmExportBuildFileGenerator* ebfg = @@ -224,6 +323,7 @@ bool cmExportCommand(std::vector<std::string> const& args, ebfg->SetTargets(targets); } ebfg->SetExportOld(arguments.ExportOld); + ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies); // Compute the set of configurations exported. std::vector<std::string> configurationTypes = diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index d0e69fb..e2c3edd 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportFileGenerator.h" +#include <algorithm> #include <array> #include <cassert> #include <cstring> @@ -9,12 +10,15 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/string_view> #include "cmsys/FStream.hxx" #include "cmComputeLinkInformation.h" +#include "cmExportSet.h" #include "cmFileSet.h" +#include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmLinkItem.h" @@ -30,6 +34,7 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.h" +#include "cmVersion.h" static std::string cmExportFileGeneratorEscape(std::string const& str) { @@ -93,17 +98,35 @@ bool cmExportFileGenerator::GenerateImportFile() return false; } std::ostream& os = *foutPtr; + std::stringstream mainFileWithHeadersAndFootersBuffer; // Start with the import file header. - this->GeneratePolicyHeaderCode(os); - this->GenerateImportHeaderCode(os); + this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer); // Create all the imported targets. - bool result = this->GenerateMainFile(os); + std::stringstream mainFileBuffer; + bool result = this->GenerateMainFile(mainFileBuffer); + + // Export find_dependency() calls. Must be done after GenerateMainFile(), + // because that's when target dependencies are gathered, which we need for + // the find_dependency() calls. + if (!this->AppendMode && this->GetExportSet() && + this->ExportPackageDependencies) { + this->SetRequiredCMakeVersion(3, 9, 0); + this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer); + } + + // Write cached import code. + mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf(); // End with the import file footer. - this->GenerateImportFooterCode(os); - this->GeneratePolicyFooterCode(os); + this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer); + this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer); + + // This has to be done last, after the minimum CMake version has been + // determined. + this->GeneratePolicyHeaderCode(os); + os << mainFileWithHeadersAndFootersBuffer.rdbuf(); return result; } @@ -156,17 +179,6 @@ void cmExportFileGenerator::PopulateInterfaceProperty( } } -void cmExportFileGenerator::GenerateRequiredCMakeVersion( - std::ostream& os, const char* versionString) -{ - /* clang-format off */ - os << "if(CMAKE_VERSION VERSION_LESS " << versionString << ")\n" - " message(FATAL_ERROR \"This file relies on consumers using " - "CMake " << versionString << " or greater.\")\n" - "endif()\n\n"; - /* clang-format on */ -} - bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, @@ -620,6 +632,12 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, return false; } + cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack(); + if (!pkgStack.Empty() || + tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) { + this->ExternalTargets.emplace(tgt); + } + if (tgt->IsImported()) { input = tgt->GetName(); return true; @@ -867,12 +885,14 @@ void cmExportFileGenerator::SetImportDetailProperties( // Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers // find private dependencies of shared libraries. std::size_t oldMissingTargetsSize = this->MissingTargets.size(); + auto oldExternalTargets = this->ExternalTargets; this->SetImportLinkProperty( suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps, properties, ImportLinkPropertyTargetNames::Yes); // Avoid enforcing shared library private dependencies as public package // dependencies by ignoring missing targets added for them. this->MissingTargets.resize(oldMissingTargetsSize); + this->ExternalTargets = std::move(oldExternalTargets); if (iface->Multiplicity > 0) { std::string prop = @@ -953,20 +973,29 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n" << " message(FATAL_ERROR \"CMake >= 2.8.0 required\")\n" << "endif()\n" - << "if(CMAKE_VERSION VERSION_LESS \"2.8.3\")\n" - << " message(FATAL_ERROR \"CMake >= 2.8.3 required\")\n" + << "if(CMAKE_VERSION VERSION_LESS \"" + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << "\")\n" + << " message(FATAL_ERROR \"CMake >= " + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << " required\")\n" << "endif()\n"; /* clang-format on */ // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.26 (this upper limit may be reviewed + // policy settings for up to CMake 3.27 (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.26)\n"; + << "cmake_policy(VERSION " + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << "...3.27)\n"; /* clang-format on */ } @@ -1125,8 +1154,9 @@ void cmExportFileGenerator::GenerateImportTargetCode( } void cmExportFileGenerator::GenerateImportPropertyCode( - std::ostream& os, const std::string& config, cmGeneratorTarget const* target, - ImportPropertyMap const& properties) + std::ostream& os, const std::string& config, const std::string& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + const std::string& importedXcFrameworkLocation) { // Construct the imported target name. std::string targetName = this->Namespace; @@ -1145,12 +1175,98 @@ void cmExportFileGenerator::GenerateImportPropertyCode( } os << ")\n"; os << "set_target_properties(" << targetName << " PROPERTIES\n"; + std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix); for (auto const& property : properties) { - os << " " << property.first << " " - << cmExportFileGeneratorEscape(property.second) << "\n"; + if (importedXcFrameworkLocation.empty() || + property.first != importedLocationProp) { + os << " " << property.first << " " + << cmExportFileGeneratorEscape(property.second) << "\n"; + } } - os << " )\n" - << "\n"; + os << " )\n"; + if (!importedXcFrameworkLocation.empty()) { + auto importedLocationIt = properties.find(importedLocationProp); + if (importedLocationIt != properties.end()) { + os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY " + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) + << ")\n" + " set_property(TARGET " + << targetName << " PROPERTY " << importedLocationProp << " " + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) + << ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY " + << importedLocationProp << " " + << cmExportFileGeneratorEscape(importedLocationIt->second) + << ")\nendif()\n"; + } + } + os << "\n"; +} + +void cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os) +{ + os << "include(CMakeFindDependencyMacro)\n"; + std::map<std::string, cmExportSet::PackageDependency> packageDependencies; + auto* exportSet = this->GetExportSet(); + if (exportSet) { + packageDependencies = exportSet->GetPackageDependencies(); + } + + for (cmGeneratorTarget const* gt : this->ExternalTargets) { + std::string findPackageName; + auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME"); + cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack(); + if (!exportFindPackageName.IsEmpty()) { + findPackageName = *exportFindPackageName; + } else { + if (!pkgStack.Empty()) { + cmFindPackageCall const& fpc = pkgStack.Top(); + findPackageName = fpc.Name; + } + } + if (!findPackageName.empty()) { + auto& dep = packageDependencies[findPackageName]; + if (!pkgStack.Empty()) { + dep.FindPackageIndex = pkgStack.Top().Index; + } + if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) { + dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On; + } + } + } + + std::vector<std::pair<std::string, cmExportSet::PackageDependency>> + packageDependenciesSorted(packageDependencies.begin(), + packageDependencies.end()); + std::sort( + packageDependenciesSorted.begin(), packageDependenciesSorted.end(), + [](const std::pair<std::string, cmExportSet::PackageDependency>& lhs, + const std::pair<std::string, cmExportSet::PackageDependency>& rhs) + -> bool { + if (lhs.second.SpecifiedIndex) { + if (rhs.second.SpecifiedIndex) { + return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex; + } + assert(rhs.second.FindPackageIndex); + return true; + } + assert(lhs.second.FindPackageIndex); + if (rhs.second.SpecifiedIndex) { + return false; + } + assert(rhs.second.FindPackageIndex); + return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex; + }); + + for (auto const& it : packageDependenciesSorted) { + if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) { + os << "find_dependency(" << it.first << " REQUIRED"; + for (auto const& arg : it.second.ExtraArguments) { + os << " " << cmOutputConverter::EscapeForCMake(arg); + } + os << ")\n"; + } + } + os << "\n\n"; } void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os) @@ -1214,10 +1330,16 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os) /* clang-format off */ os << "# Loop over all imported files and verify that they actually exist\n" "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n" - " foreach(_cmake_file IN LISTS \"_cmake_import_check_files_for_${_cmake_target}\")\n" - " if(NOT EXISTS \"${_cmake_file}\")\n" - " message(FATAL_ERROR \"The imported target \\\"${_cmake_target}\\\"" - " references the file\n" + " if(CMAKE_VERSION VERSION_LESS \"3.28\"\n" + " OR NOT DEFINED " + "_cmake_import_check_xcframework_for_${_cmake_target}\n" + " OR NOT IS_DIRECTORY " + "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n" + " foreach(_cmake_file IN LISTS " + "\"_cmake_import_check_files_for_${_cmake_target}\")\n" + " if(NOT EXISTS \"${_cmake_file}\")\n" + " message(FATAL_ERROR \"The imported target " + "\\\"${_cmake_target}\\\" references the file\n" " \\\"${_cmake_file}\\\"\n" "but this file does not exist. Possible reasons include:\n" "* The file was deleted, renamed, or moved to another location.\n" @@ -1226,8 +1348,9 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os) " \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n" "but not all the files it references.\n" "\")\n" - " endif()\n" - " endforeach()\n" + " endif()\n" + " endforeach()\n" + " endif()\n" " unset(_cmake_file)\n" " unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n" "endforeach()\n" @@ -1240,15 +1363,18 @@ void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os) void cmExportFileGenerator::GenerateImportedFileChecksCode( std::ostream& os, cmGeneratorTarget* target, ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations) + const std::set<std::string>& importedLocations, + const std::string& importedXcFrameworkLocation) { // Construct the imported target name. std::string targetName = cmStrCat(this->Namespace, target->GetExportName()); - os << "list(APPEND _cmake_import_check_targets " << targetName - << " )\n" - "list(APPEND _cmake_import_check_files_for_" - << targetName << " "; + os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n"; + if (!importedXcFrameworkLocation.empty()) { + os << "set(_cmake_import_check_xcframework_for_" << targetName << ' ' + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n"; + } + os << "list(APPEND _cmake_import_check_files_for_" << targetName << " "; for (std::string const& li : importedLocations) { auto pi = properties.find(li); @@ -1485,3 +1611,17 @@ void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os) this->GenerateCxxModuleConfigInformation(ap); } + +void cmExportFileGenerator::SetRequiredCMakeVersion(unsigned int major, + unsigned int minor, + unsigned int patch) +{ + if (CMake_VERSION_ENCODE(major, minor, patch) > + CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor, + this->RequiredCMakeVersionMinor, + this->RequiredCMakeVersionPatch)) { + this->RequiredCMakeVersionMajor = major; + this->RequiredCMakeVersionMinor = minor; + this->RequiredCMakeVersionPatch = patch; + } +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f396e0e..554dd4a 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -15,6 +15,7 @@ #include "cmVersion.h" #include "cmVersionConfig.h" +class cmExportSet; class cmFileSet; class cmGeneratorTarget; class cmLocalGenerator; @@ -61,6 +62,11 @@ public: error. */ bool GenerateImportFile(); + void SetExportPackageDependencies(bool exportPackageDependencies) + { + this->ExportPackageDependencies = exportPackageDependencies; + } + protected: using ImportPropertyMap = std::map<std::string, std::string>; @@ -78,16 +84,18 @@ protected: virtual void GenerateImportTargetCode(std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType targetType); - virtual void GenerateImportPropertyCode(std::ostream& os, - const std::string& config, - cmGeneratorTarget const* target, - ImportPropertyMap const& properties); + virtual void GenerateImportPropertyCode( + std::ostream& os, const std::string& config, const std::string& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + const std::string& importedXcFrameworkLocation); virtual void GenerateImportedFileChecksCode( std::ostream& os, cmGeneratorTarget* target, ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations); + const std::set<std::string>& importedLocations, + const std::string& importedXcFrameworkLocation); virtual void GenerateImportedFileCheckLoop(std::ostream& os); virtual void GenerateMissingTargetsCheckCode(std::ostream& os); + virtual void GenerateFindDependencyCalls(std::ostream& os); virtual void GenerateExpectedTargetsCode(std::ostream& os, const std::string& expectedTargets); @@ -173,9 +181,6 @@ protected: std::string& input, cmGeneratorTarget const* target, FreeTargetsReplace replace = NoReplaceFreeTargets); - virtual void GenerateRequiredCMakeVersion(std::ostream& os, - const char* versionString); - bool PopulateCxxModuleExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, cmGeneratorExpression::PreprocessContext ctx, @@ -196,6 +201,11 @@ protected: cmFileSet* fileSet, cmTargetExport* te) = 0; + virtual cmExportSet* GetExportSet() const { return nullptr; } + + void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, + unsigned int patch); + // The namespace in which the exports are placed in the generated file. std::string Namespace; @@ -216,6 +226,14 @@ protected: std::vector<std::string> MissingTargets; + std::set<cmGeneratorTarget const*> ExternalTargets; + + unsigned int RequiredCMakeVersionMajor = 2; + unsigned int RequiredCMakeVersionMinor = 8; + unsigned int RequiredCMakeVersionPatch = 3; + + bool ExportPackageDependencies = false; + private: void PopulateInterfaceProperty(const std::string&, const std::string&, cmGeneratorTarget const* target, diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index d53254d..eaa85f3 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -80,8 +80,8 @@ void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode( } void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, cmGeneratorTarget const*, - ImportPropertyMap const&) + std::ostream&, const std::string&, const std::string&, + cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) { } @@ -110,11 +110,6 @@ void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&) { } -void cmExportInstallAndroidMKGenerator::GenerateRequiredCMakeVersion( - std::ostream&, const char*) -{ -} - void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables( std::ostream&) { @@ -127,7 +122,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop( void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode( std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&, - const std::set<std::string>&) + const std::set<std::string>&, const std::string&) { } diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index 061358d..b1778ef 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -45,22 +45,22 @@ protected: void GenerateExpectedTargetsCode( std::ostream& os, const std::string& expectedTargets) override; void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, - cmGeneratorTarget const* target, - ImportPropertyMap const& properties) override; + std::ostream& os, const std::string& config, const std::string& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + const std::string& importedXcFrameworkLocation) override; void GenerateMissingTargetsCheckCode(std::ostream& os) override; + void GenerateFindDependencyCalls(std::ostream&) override {} void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties) override; void GenerateImportPrefix(std::ostream& os) override; void LoadConfigFiles(std::ostream&) override; - void GenerateRequiredCMakeVersion(std::ostream& os, - const char* versionString) override; void CleanupTemporaryVariables(std::ostream&) override; void GenerateImportedFileCheckLoop(std::ostream& os) override; void GenerateImportedFileChecksCode( std::ostream& os, cmGeneratorTarget* target, ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations) override; + const std::set<std::string>& importedLocations, + const std::string& importedXcFrameworkLocation) override; bool GenerateImportFileConfig(const std::string& config) override; }; diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index a264f5e..64e694c 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -76,9 +76,6 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) // Compute the relative import prefix for the file this->GenerateImportPrefix(os); - bool require2_8_12 = false; - bool require3_0_0 = false; - bool require3_1_0 = false; bool requiresConfigFiles = false; // Create all the imported targets. for (cmTargetExport* te : allTargets) { @@ -147,16 +144,16 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) if (this->PopulateInterfaceLinkLibrariesProperty( gt, cmGeneratorExpression::InstallInterface, properties) && !this->ExportOld) { - require2_8_12 = true; + this->SetRequiredCMakeVersion(2, 8, 12); } } if (targetType == cmStateEnums::INTERFACE_LIBRARY) { - require3_0_0 = true; + this->SetRequiredCMakeVersion(3, 0, 0); } if (gt->GetProperty("INTERFACE_SOURCES")) { // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 // can consume them. - require3_1_0 = true; + this->SetRequiredCMakeVersion(3, 1, 0); } this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt, @@ -169,14 +166,6 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->GenerateTargetFileSets(gt, os, te); } - if (require3_1_0) { - this->GenerateRequiredCMakeVersion(os, "3.1.0"); - } else if (require3_0_0) { - this->GenerateRequiredCMakeVersion(os, "3.0.0"); - } else if (require2_8_12) { - this->GenerateRequiredCMakeVersion(os, "2.8.12"); - } - this->LoadConfigFiles(os); bool result = true; @@ -386,9 +375,26 @@ void cmExportInstallFileGenerator::GenerateImportTargetsConfig( // properties); // Generate code in the export file. - this->GenerateImportPropertyCode(os, config, gtgt, properties); - this->GenerateImportedFileChecksCode(os, gtgt, properties, - importedLocations); + std::string importedXcFrameworkLocation = te->XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::InstallInterface, true); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config, + te->Target, nullptr, te->Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) && + !cmHasLiteralPrefix(importedXcFrameworkLocation, + "${_IMPORT_PREFIX}/")) { + importedXcFrameworkLocation = + cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation); + } + } + this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties, + importedXcFrameworkLocation); + this->GenerateImportedFileChecksCode( + os, gtgt, properties, importedLocations, importedXcFrameworkLocation); } } } diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index e073a31..9de0c8e 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -12,12 +12,13 @@ #include <vector> #include "cmExportFileGenerator.h" +#include "cmInstallExportGenerator.h" #include "cmStateTypes.h" +class cmExportSet; class cmFileSet; class cmGeneratorTarget; class cmGlobalGenerator; -class cmInstallExportGenerator; class cmInstallTargetGenerator; class cmTargetExport; @@ -121,6 +122,11 @@ protected: void GenerateCxxModuleConfigInformation(std::ostream&) const override; bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); + cmExportSet* GetExportSet() const override + { + return this->IEGen->GetExportSet(); + } + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. diff --git a/Source/cmExportSet.cxx b/Source/cmExportSet.cxx index 3d4ef0a..b32bb8d 100644 --- a/Source/cmExportSet.cxx +++ b/Source/cmExportSet.cxx @@ -1,6 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmExportSet.h" +#include "cmExportSet.h" // IWYU pragma: associated #include <algorithm> #include <tuple> @@ -11,7 +11,7 @@ #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" -#include "cmTargetExport.h" +#include "cmTargetExport.h" // IWYU pragma: associated cmExportSet::cmExportSet(std::string name) : Name(std::move(name)) @@ -20,6 +20,17 @@ cmExportSet::cmExportSet(std::string name) cmExportSet::~cmExportSet() = default; +cmExportSet::PackageDependency& cmExportSet::GetPackageDependencyForSetup( + const std::string& name) +{ + auto& dep = this->PackageDependencies[name]; + if (!dep.SpecifiedIndex) { + dep.SpecifiedIndex = this->NextPackageDependencyIndex; + this->NextPackageDependencyIndex++; + } + return dep; +} + bool cmExportSet::Compute(cmLocalGenerator* lg) { for (std::unique_ptr<cmTargetExport>& tgtExport : this->TargetExports) { @@ -61,6 +72,16 @@ void cmExportSet::AddInstallation(cmInstallExportGenerator const* installation) this->Installations.push_back(installation); } +void cmExportSet::SetXcFrameworkLocation(const std::string& name, + const std::string& location) +{ + for (auto& te : this->TargetExports) { + if (name == te->TargetName) { + te->XcFrameworkLocation = location; + } + } +} + cmExportSet& cmExportSetMap::operator[](const std::string& name) { auto it = this->find(name); diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h index b75a26d..f2fc4a7 100644 --- a/Source/cmExportSet.h +++ b/Source/cmExportSet.h @@ -9,6 +9,8 @@ #include <string> #include <vector> +#include <cm/optional> + class cmInstallExportGenerator; class cmLocalGenerator; class cmTargetExport; @@ -31,6 +33,9 @@ public: void AddInstallation(cmInstallExportGenerator const* installation); + void SetXcFrameworkLocation(const std::string& name, + const std::string& location); + std::string const& GetName() const { return this->Name; } std::vector<std::unique_ptr<cmTargetExport>> const& GetTargetExports() const @@ -43,10 +48,36 @@ public: return &this->Installations; } + enum class PackageDependencyExportEnabled + { + Auto, + Off, + On, + }; + + struct PackageDependency + { + PackageDependencyExportEnabled Enabled = + PackageDependencyExportEnabled::Auto; + std::vector<std::string> ExtraArguments; + cm::optional<unsigned int> SpecifiedIndex; + cm::optional<unsigned int> FindPackageIndex; + }; + + PackageDependency& GetPackageDependencyForSetup(const std::string& name); + + const std::map<std::string, PackageDependency>& GetPackageDependencies() + const + { + return this->PackageDependencies; + } + private: std::vector<std::unique_ptr<cmTargetExport>> TargetExports; std::string Name; std::vector<cmInstallExportGenerator const*> Installations; + std::map<std::string, PackageDependency> PackageDependencies; + unsigned int NextPackageDependencyIndex = 0; }; /// A name -> cmExportSet map with overloaded operator[]. diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index 8b0f309..4524ba6 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -727,7 +727,7 @@ std::string cmFileAPI::NoSupportedVersion( // The "codemodel" object kind. // Update Help/manual/cmake-file-api.7.rst when updating this constant. -static unsigned int const CodeModelV2Minor = 6; +static unsigned int const CodeModelV2Minor = 7; void cmFileAPI::BuildClientRequestCodeModel( ClientRequest& r, std::vector<RequestVersion> const& versions) diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index d069186..30a7e67 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -41,10 +41,12 @@ #include "cmInstallSubdirectoryGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmLinkLineComputer.h" // IWYU pragma: keep +#include "cmList.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" #include "cmState.h" @@ -503,6 +505,8 @@ class Target Json::Value DumpDependencies(); Json::Value DumpDependency(cmTargetDepend const& td); Json::Value DumpFolder(); + Json::Value DumpLauncher(const char* name, const char* type); + Json::Value DumpLaunchers(); public: Target(cmGeneratorTarget* gt, std::string const& config); @@ -1223,6 +1227,13 @@ Json::Value Target::Dump() target["archive"] = this->DumpArchive(); } + if (type == cmStateEnums::EXECUTABLE) { + Json::Value launchers = this->DumpLaunchers(); + if (!launchers.empty()) { + target["launchers"] = std::move(launchers); + } + } + Json::Value dependencies = this->DumpDependencies(); if (!dependencies.empty()) { target["dependencies"] = dependencies; @@ -2075,6 +2086,46 @@ Json::Value Target::DumpFolder() } return folder; } + +Json::Value Target::DumpLauncher(const char* name, const char* type) +{ + cmValue property = this->GT->GetProperty(name); + Json::Value launcher; + if (property) { + cmList commandWithArgs{ *property }; + std::string command(commandWithArgs[0]); + cmSystemTools::ConvertToUnixSlashes(command); + launcher = Json::objectValue; + launcher["command"] = RelativeIfUnder(this->TopSource, command); + launcher["type"] = type; + Json::Value args; + for (std::string const& arg : cmMakeRange(commandWithArgs).advance(1)) { + args.append(arg); + } + if (!args.empty()) { + launcher["arguments"] = std::move(args); + } + } + return launcher; +} + +Json::Value Target::DumpLaunchers() +{ + Json::Value launchers; + { + Json::Value launcher = DumpLauncher("TEST_LAUNCHER", "test"); + if (!launcher.empty()) { + launchers.append(std::move(launcher)); + } + } + if (this->GT->Makefile->IsOn("CMAKE_CROSSCOMPILING")) { + Json::Value emulator = DumpLauncher("CROSSCOMPILING_EMULATOR", "emulator"); + if (!emulator.empty()) { + launchers.append(std::move(emulator)); + } + } + return launchers; +} } Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index c65136c..c1b7b48 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -1322,13 +1322,15 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, if (oldPolicyPath != realPath) { status.GetMakefile().IssueMessage( MessageType::AUTHOR_WARNING, - cmStrCat( - cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), '\n', - "From input path:\n ", input, - "\nthe policy OLD behavior produces path:\n ", oldPolicyPath, - "\nbut the policy NEW behavior produces path:\n ", realPath, - "\nSince the policy is not set, CMake is using the OLD " - "behavior for compatibility.")); + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), + "\n" + "From input path:\n ", + input, "\nthe policy OLD behavior produces path:\n ", + oldPolicyPath, + "\nbut the policy NEW behavior produces path:\n ", + realPath, + "\nSince the policy is not set, CMake is using the OLD " + "behavior for compatibility.")); } } realPath = oldPolicyPath; diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 30458cd..9b51b1a 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -1044,6 +1044,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) PushPopRootPathStack pushPopRootPathStack(*this); SetRestoreFindDefinitions setRestoreFindDefinitions(*this, components, componentVarDefs); + cmMakefile::FindPackageStackRAII findPackageStackRAII(this->Makefile, + this->Name); // See if we have been told to delegate to FetchContent or some other // redirected config package first. We have to check all names that diff --git a/Source/cmFindPackageStack.cxx b/Source/cmFindPackageStack.cxx new file mode 100644 index 0000000..1aeb2a7 --- /dev/null +++ b/Source/cmFindPackageStack.cxx @@ -0,0 +1,7 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#define cmFindPackageStack_cxx +#include "cmFindPackageStack.h" + +#include "cmConstStack.tcc" // IWYU pragma: keep +template class cmConstStack<cmFindPackageCall, cmFindPackageStack>; diff --git a/Source/cmFindPackageStack.h b/Source/cmFindPackageStack.h new file mode 100644 index 0000000..2062fbc --- /dev/null +++ b/Source/cmFindPackageStack.h @@ -0,0 +1,33 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <memory> +#include <string> + +#include "cmConstStack.h" + +/** + * Represents one call to find_package. + */ +class cmFindPackageCall +{ +public: + std::string Name; + unsigned int Index; +}; + +/** + * Represents a stack of find_package calls with efficient value semantics. + */ +class cmFindPackageStack + : public cmConstStack<cmFindPackageCall, cmFindPackageStack> +{ + using cmConstStack::cmConstStack; + friend class cmConstStack<cmFindPackageCall, cmFindPackageStack>; +}; +#ifndef cmFindPackageStack_cxx +extern template class cmConstStack<cmFindPackageCall, cmFindPackageStack>; +#endif diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index d51dbd0..4e46df7 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -175,14 +175,15 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const cm::string_view property(this->Top()->Property); return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s || - property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s; + property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s || + property == "LINKER_TYPE"_s; } bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const { cm::string_view property(this->Top()->Property); - return property == "LINK_OPTIONS"_s; + return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s; } bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 9bd9fef..289bb24 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -868,6 +868,31 @@ cmValue cmGeneratorTarget::GetFeature(const std::string& feature, return this->LocalGenerator->GetFeature(feature, config); } +std::string cmGeneratorTarget::GetLinkerTypeProperty( + std::string const& lang, std::string const& config) const +{ + std::string propName{ "LINKER_TYPE" }; + auto linkerType = this->GetProperty(propName); + if (!linkerType.IsEmpty()) { + cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr, + nullptr); + auto ltype = + cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(), + config, this, &dagChecker, this, lang); + if (this->IsDeviceLink()) { + cmList list{ ltype }; + const auto DL_BEGIN = "<DEVICE_LINK>"_s; + const auto DL_END = "</DEVICE_LINK>"_s; + cm::erase_if(list, [&](const std::string& item) { + return item == DL_BEGIN || item == DL_END; + }); + return list.to_string(); + } + return ltype; + } + return std::string{}; +} + const char* cmGeneratorTarget::GetLinkPIEProperty( const std::string& config) const { @@ -1821,32 +1846,31 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(), &dagChecker, linkInterfaceSourcesEntries, IncludeRuntimeInterface::No, LinkInterfaceFor::Usage); - std::vector<std::string>::size_type numFilesBefore = files.size(); bool contextDependentInterfaceSources = processSources( this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); // Collect TARGET_OBJECTS of direct object link-dependencies. bool contextDependentObjects = false; - std::vector<std::string>::size_type numFilesBefore2 = files.size(); if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { EvaluatedTargetPropertyEntries linkObjectsEntries; AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); contextDependentObjects = processSources(this, linkObjectsEntries, files, uniqueSrcs, debugSources); + // Note that for imported targets or multi-config generators supporting + // cross-config builds the paths to the object files must be per-config, + // so contextDependentObjects will be true here even if object libraries + // are specified without per-config generator expressions. } // Collect this target's file sets. - std::vector<std::string>::size_type numFilesBefore3 = files.size(); EvaluatedTargetPropertyEntries fileSetEntries; AddFileSetEntries(this, config, &dagChecker, fileSetEntries); bool contextDependentFileSets = processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); // Determine if sources are context-dependent or not. - if (!contextDependentDirectSources && - !(contextDependentInterfaceSources && numFilesBefore < files.size()) && - !(contextDependentObjects && numFilesBefore2 < files.size()) && - !(contextDependentFileSets && numFilesBefore3 < files.size())) { + if (!contextDependentDirectSources && !contextDependentInterfaceSources && + !contextDependentObjects && !contextDependentFileSets) { this->SourcesAreContextDependent = Tribool::False; } else { this->SourcesAreContextDependent = Tribool::True; @@ -5528,6 +5552,50 @@ std::string cmGeneratorTarget::GetLinkerLanguage( return this->GetLinkClosure(config)->LinkerLanguage; } +std::string cmGeneratorTarget::GetLinkerTool(const std::string& config) const +{ + return this->GetLinkerTool(this->GetLinkerLanguage(config), config); +} + +std::string cmGeneratorTarget::GetLinkerTool(const std::string& lang, + const std::string& config) const +{ + auto usingLinker = + cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "", + "LINKER_"); + auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE")); + if (!format || format != "TOOL"_s) { + return this->Makefile->GetDefinition("CMAKE_LINKER"); + } + + auto linkerType = this->GetLinkerTypeProperty(lang, config); + if (linkerType.empty()) { + linkerType = "DEFAULT"; + } + usingLinker = cmStrCat(usingLinker, linkerType); + auto linkerTool = this->Makefile->GetDefinition(usingLinker); + + if (!linkerTool) { + if (this->GetGlobalGenerator()->IsVisualStudio() && + linkerType == "DEFAULT"_s) { + return std::string{}; + } + + // fall-back to generic definition + linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER"); + + if (linkerType != "DEFAULT"_s) { + this->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("LINKER_TYPE '", linkerType, + "' is unknown. Did you forgot to define '", usingLinker, + "' variable?")); + } + } + + return linkerTool; +} + std::string cmGeneratorTarget::GetPDBOutputName( const std::string& config) const { @@ -6846,7 +6914,8 @@ bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( std::string const& n, cmListFileBacktrace const& bt, - LookupLinkItemScope* scope, LookupSelf lookupSelf) const + std::string const& linkFeature, LookupLinkItemScope* scope, + LookupSelf lookupSelf) const { cm::optional<cmLinkItem> maybeItem; if (this->IsLinkLookupScope(n, scope->LG)) { @@ -6858,7 +6927,8 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( (lookupSelf == LookupSelf::No && name == this->GetName())) { return maybeItem; } - maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG); + maybeItem = + this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); return maybeItem; } @@ -6889,9 +6959,16 @@ void cmGeneratorTarget::ExpandLinkItems( cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this, headTarget->LinkerLanguage) }; + + auto linkFeature = cmLinkItem::DEFAULT; for (auto const& lib : libs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - lib, cge->GetBacktrace(), &scope, + lib, cge->GetBacktrace(), linkFeature, &scope, field == LinkInterfaceField::Libraries ? LookupSelf::No : LookupSelf::Yes)) { cmLinkItem item = std::move(*maybeItem); @@ -7637,9 +7714,16 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( LinkInterfaceField::Libraries, iface); cmList deps{ info->SharedDeps }; LookupLinkItemScope scope{ this->LocalGenerator }; + + auto linkFeature = cmLinkItem::DEFAULT; for (auto const& dep : deps) { + if (auto maybeLinkFeature = ParseLinkFeature(dep)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - dep, cmListFileBacktrace(), &scope, LookupSelf::No)) { + dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { iface.SharedDeps.emplace_back(std::move(*maybeItem)); } } @@ -8439,7 +8523,13 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( impl.HadLinkLanguageSensitiveCondition = true; } + auto linkFeature = cmLinkItem::DEFAULT; for (auto const& lib : llibs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + if (this->IsLinkLookupScope(lib, lg)) { continue; } @@ -8486,8 +8576,8 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( } // The entry is meant for this configuration. - cmLinkItem item = - this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg); + cmLinkItem item = this->ResolveLinkItem( + BT<std::string>(name, entry.Backtrace), lg, linkFeature); if (item.Target) { auto depsForTarget = synthTargetsForConfig.find(item.Target); if (depsForTarget != synthTargetsForConfig.end()) { @@ -8535,7 +8625,14 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( CMP0003_ComputeLinkType(config, debugConfigs); cmTarget::LinkLibraryVectorType const& oldllibs = this->Target->GetOriginalLinkLibraries(); + + auto linkFeature = cmLinkItem::DEFAULT; for (cmTarget::LibraryID const& oldllib : oldllibs) { + if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { std::string name = this->CheckCMP0004(oldllib.first); if (name == this->GetName() || name.empty()) { @@ -8543,7 +8640,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( } // Support OLD behavior for CMP0003. impl.WrongConfigLibraries.push_back( - this->ResolveLinkItem(BT<std::string>(name))); + this->ResolveLinkItem(BT<std::string>(name), linkFeature)); } } } @@ -8569,19 +8666,20 @@ cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( } cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name) const + BT<std::string> const& name, std::string const& linkFeature) const { - return this->ResolveLinkItem(name, this->LocalGenerator); + return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); } -cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name, - cmLocalGenerator const* lg) const +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, cmLocalGenerator const* lg, + std::string const& linkFeature) const { auto bt = name.Backtrace; TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); if (!resolved.Target) { - return cmLinkItem(resolved.String, false, bt); + return cmLinkItem(resolved.String, false, bt, linkFeature); } // Check deprecation, issue message with `bt` backtrace. @@ -8602,10 +8700,10 @@ cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name, // within the project. if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && !resolved.Target->IsExecutableWithExports()) { - return cmLinkItem(resolved.Target->GetName(), false, bt); + return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); } - return cmLinkItem(resolved.Target, false, bt); + return cmLinkItem(resolved.Target, false, bt, linkFeature); } bool cmGeneratorTarget::HasPackageReferences() const diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index c13b2f6..cfb08fa 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -206,6 +206,9 @@ public: cmValue GetFeature(const std::string& feature, const std::string& config) const; + std::string GetLinkerTypeProperty(std::string const& lang, + std::string const& config) const; + const char* GetLinkPIEProperty(const std::string& config) const; bool IsIPOEnabled(std::string const& lang, std::string const& config) const; @@ -447,9 +450,12 @@ public: TargetOrString ResolveTargetReference(std::string const& name, cmLocalGenerator const* lg) const; - cmLinkItem ResolveLinkItem(BT<std::string> const& name) const; - cmLinkItem ResolveLinkItem(BT<std::string> const& name, - cmLocalGenerator const* lg) const; + cmLinkItem ResolveLinkItem( + BT<std::string> const& name, + std::string const& linkFeature = cmLinkItem::DEFAULT) const; + cmLinkItem ResolveLinkItem( + BT<std::string> const& name, cmLocalGenerator const* lg, + std::string const& linkFeature = cmLinkItem::DEFAULT) const; bool HasPackageReferences() const; std::vector<std::string> GetPackageReferences() const; @@ -794,6 +800,10 @@ public: //! Return the preferred linker language for this target std::string GetLinkerLanguage(const std::string& config) const; + //! Return the preferred linker tool for this target + std::string GetLinkerTool(const std::string& config) const; + std::string GetLinkerTool(const std::string& lang, + const std::string& config) const; /** Does this target have a GNU implib to convert to MS format? */ bool HasImplibGNUtoMS(std::string const& config) const; @@ -1175,6 +1185,7 @@ private: }; cm::optional<cmLinkItem> LookupLinkItem(std::string const& n, cmListFileBacktrace const& bt, + std::string const& linkFeature, LookupLinkItemScope* scope, LookupSelf lookupSelf) const; diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 95e2187..940f49d 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -116,6 +116,19 @@ void cmGhsMultiTargetGenerator::Generate() void cmGhsMultiTargetGenerator::GenerateTarget() { + if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE && + !this->GeneratorTarget + ->GetLinkerTypeProperty( + this->GeneratorTarget->GetLinkerLanguage(this->ConfigName), + this->ConfigName) + .empty()) { + // Green Hill MULTI does not support this feature. + cmSystemTools::Message( + cmStrCat("'LINKER_TYPE' property, specified on target '", + this->GeneratorTarget->GetName(), + "', is not supported by this generator.")); + } + // Open the target file in copy-if-different mode. std::string fproj = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index c2b972d..e74a8b0 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -694,7 +694,22 @@ void cmGlobalGenerator::EnableLanguage( std::string includes = mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES"); cmList includesList{ includes }; - for (std::string const& setupFile : includesList) { + for (std::string setupFile : includesList) { + // Any relative path without a .cmake extension is checked for valid + // cmake modules. This logic should be consistent with CMake's include() + // command. Otherwise default to checking relative path w.r.t. source + // directory + if (!cmSystemTools::FileIsFullPath(setupFile) && + !cmHasLiteralSuffix(setupFile, ".cmake")) { + std::string mfile = mf->GetModulesFile(cmStrCat(setupFile, ".cmake")); + if (mfile.empty()) { + cmSystemTools::Error(cmStrCat( + "CMAKE_PROJECT_TOP_LEVEL_INCLUDES module:\n ", setupFile)); + mf->GetState()->SetInTopLevelIncludes(false); + return; + } + setupFile = mfile; + } std::string absSetupFile = cmSystemTools::CollapseFullPath( setupFile, mf->GetCurrentSourceDirectory()); if (!cmSystemTools::FileExists(absSetupFile)) { @@ -859,7 +874,11 @@ void cmGlobalGenerator::EnableLanguage( noCompiler << "The " << compilerName << ":\n" " " << *compilerFile << "\n" - "is not a full path and was not found in the PATH.\n" + "is not a full path and was not found in the PATH." +#ifdef _WIN32 + " Perhaps the extension is missing?" +#endif + "\n" ; /* clang-format on */ } else if (!cmSystemTools::FileExists(*compilerFile)) { @@ -1139,24 +1158,30 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension( { const std::string& lang = source.GetLanguage(); if (!lang.empty()) { - auto const it = this->LanguageToOutputExtension.find(lang); - if (it != this->LanguageToOutputExtension.end()) { - return it->second; - } - } else { - // if no language is found then check to see if it is already an - // output extension for some language. In that case it should be ignored - // and in this map, so it will not be compiled but will just be used. - std::string const& ext = source.GetExtension(); - if (!ext.empty()) { - if (this->OutputExtensions.count(ext)) { - return ext; - } + return this->GetLanguageOutputExtension(lang); + } + // if no language is found then check to see if it is already an + // output extension for some language. In that case it should be ignored + // and in this map, so it will not be compiled but will just be used. + std::string const& ext = source.GetExtension(); + if (!ext.empty()) { + if (this->OutputExtensions.count(ext)) { + return ext; } } return ""; } +std::string cmGlobalGenerator::GetLanguageOutputExtension( + std::string const& lang) const +{ + auto const it = this->LanguageToOutputExtension.find(lang); + if (it != this->LanguageToOutputExtension.end()) { + return it->second; + } + return ""; +} + std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const { // if there is an extension and it starts with . then move past the @@ -2848,6 +2873,14 @@ void cmGlobalGenerator::AddGlobalTarget_Test( gti.Name = this->GetTestTargetName(); gti.Message = "Running tests..."; gti.UsesTerminal = true; + // Unlike the 'install' target, the 'test' target does not depend on 'all' + // by default. Enable it only if CMAKE_SKIP_TEST_ALL_DEPENDENCY is + // explicitly set to OFF. + if (cmValue noall = mf->GetDefinition("CMAKE_SKIP_TEST_ALL_DEPENDENCY")) { + if (cmIsOff(noall)) { + gti.Depends.emplace_back(this->GetAllTargetName()); + } + } cmCustomCommandLine singleLine; singleLine.push_back(cmSystemTools::GetCTestCommand()); singleLine.push_back("--force-new-ctest-process"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index aa54f69..d83b669 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -349,6 +349,8 @@ public: int GetLinkerPreference(const std::string& lang) const; //! What is the object file extension for a given source file? std::string GetLanguageOutputExtension(cmSourceFile const&) const; + //! What is the object file extension for a given language? + std::string GetLanguageOutputExtension(std::string const& lang) const; //! What is the configurations directory variable called? virtual const char* GetCMakeCFGIntDir() const { return "."; } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 22fd90d..55eb9b5 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -378,6 +378,15 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild( } { + std::string ninjaDepfilePath; + bool depfileIsOutput = false; + if (!depfile.empty()) { + ninjaDepfilePath = this->ConvertToNinjaPath(depfile); + depfileIsOutput = + std::find(outputs.ExplicitOuts.begin(), outputs.ExplicitOuts.end(), + ninjaDepfilePath) != outputs.ExplicitOuts.end(); + } + cmNinjaBuild build("CUSTOM_COMMAND"); build.Comment = comment; build.Outputs = std::move(outputs.ExplicitOuts); @@ -405,7 +414,13 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild( vars["pool"] = job_pool; } if (!depfile.empty()) { - vars["depfile"] = depfile; + vars["depfile"] = ninjaDepfilePath; + // Add the depfile to the `.ninja_deps` database. Since this (generally) + // removes the file, it cannot be declared as an output or byproduct of + // the command. + if (!depfileIsOutput) { + vars["deps"] = "gcc"; + } } if (config.empty()) { this->WriteBuild(*this->GetCommonFileStream(), build); @@ -1359,10 +1374,10 @@ void cmGlobalNinjaGenerator::AppendTargetDepends( } else { cmNinjaDeps outs; - auto computeISPCOuputs = [](cmGlobalNinjaGenerator* gg, - cmGeneratorTarget const* depTarget, - cmNinjaDeps& outputDeps, - const std::string& targetConfig) { + auto computeISPCOutputs = [](cmGlobalNinjaGenerator* gg, + cmGeneratorTarget const* depTarget, + cmNinjaDeps& outputDeps, + const std::string& targetConfig) { if (depTarget->CanCompileSources()) { auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig); if (!headers.empty()) { @@ -1386,10 +1401,10 @@ void cmGlobalNinjaGenerator::AppendTargetDepends( } if (targetDep.IsCross()) { this->AppendTargetOutputs(targetDep, outs, fileConfig, depends); - computeISPCOuputs(this, targetDep, outs, fileConfig); + computeISPCOutputs(this, targetDep, outs, fileConfig); } else { this->AppendTargetOutputs(targetDep, outs, config, depends); - computeISPCOuputs(this, targetDep, outs, config); + computeISPCOutputs(this, targetDep, outs, config); } } std::sort(outs.begin(), outs.end()); @@ -3242,10 +3257,3 @@ std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTarget( return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_', cmSystemTools::UpperCase(config)); } - -std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTargetPrivate( - cmGeneratorTarget const* target, const std::string& config) const -{ - return cmStrCat(this->OrderDependsTargetForTarget(target, config), - "_private"); -} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 220d393..3443643 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -349,7 +349,7 @@ public: virtual std::string OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const; - virtual std::string OrderDependsTargetForTargetPrivate( + std::string OrderDependsTargetForTargetPrivate( cmGeneratorTarget const* target, const std::string& config) const; void AppendTargetOutputs(cmGeneratorTarget const* target, @@ -742,9 +742,6 @@ public: std::string OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const override; - std::string OrderDependsTargetForTargetPrivate( - cmGeneratorTarget const* target, const std::string& config) const override; - protected: bool OpenBuildFileStreams() override; void CloseBuildFileStreams() override; diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 541db63..c93b140 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -191,6 +191,23 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset( } } + if (this->GeneratorToolsetFortran) { + if (*this->GeneratorToolsetFortran != "ifx" && + *this->GeneratorToolsetFortran != "ifort") { + mf->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("Generator\n" + " ", + this->GetName(), + "\n" + "given toolset\n" + " fortran=", + *this->GeneratorToolsetFortran, + "\n" + "but the value is not \"ifx\" or \"ifort\".")); + this->GeneratorToolsetFortran = cm::nullopt; + } + } + if (!this->GeneratorToolsetVersion.empty() && this->GeneratorToolsetVersion != "Test Toolset Version"_s) { // If a specific minor version of the MSVC toolset is requested, verify @@ -300,6 +317,9 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset( if (const char* cudaDir = this->GetPlatformToolsetCudaCustomDir()) { mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR", cudaDir); } + if (cm::optional<std::string> fortran = this->GetPlatformToolsetFortran()) { + mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_FORTRAN", *fortran); + } if (const char* vcTargetsDir = this->GetCustomVCTargetsPath()) { mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR", vcTargetsDir); @@ -410,6 +430,10 @@ bool cmGlobalVisualStudio10Generator::ProcessGeneratorToolsetField( cmSystemTools::ConvertToUnixSlashes(this->CustomFlagTableDir); return true; } + if (key == "fortran"_s) { + this->GeneratorToolsetFortran = value; + return true; + } if (key == "version"_s) { this->GeneratorToolsetVersion = value; return true; diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 40bdd71..a2b351c 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -93,6 +93,12 @@ public: * directory */ std::string const& GetPlatformToolsetCudaVSIntegrationSubdirString() const; + /** The fortran toolset name. */ + cm::optional<std::string> GetPlatformToolsetFortran() const override + { + return this->GeneratorToolsetFortran; + } + /** Return whether we need to use No/Debug instead of false/true for GenerateDebugInformation. */ bool GetPlatformToolsetNeedsDebugEnum() const @@ -221,6 +227,7 @@ protected: std::string GeneratorToolsetCudaCustomDir; std::string GeneratorToolsetCudaNvccSubdir; std::string GeneratorToolsetCudaVSIntegrationSubdir; + cm::optional<std::string> GeneratorToolsetFortran; std::string DefaultPlatformToolset; std::string DefaultPlatformToolsetHostArchitecture; std::string DefaultAndroidToolset; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 1abdd0b..9739a09 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -94,8 +94,8 @@ const std::string& cmGlobalVisualStudio7Generator::GetIntelProjectVersion() cmSystemTools::ReadRegistryValue(vskey, intelVersion, cmSystemTools::KeyWOW64_32); unsigned int intelVersionNumber = ~0u; - sscanf(intelVersion.c_str(), "%u", &intelVersionNumber); - if (intelVersionNumber >= 11) { + if (sscanf(intelVersion.c_str(), "%u", &intelVersionNumber) != 1 || + intelVersionNumber >= 11) { // Default to latest known project file version. intelVersion = "11.0"; } else if (intelVersionNumber == 10) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 6f6109e..2056b2f 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -10,6 +10,8 @@ #include <utility> #include <vector> +#include <cm/optional> + #include <cm3p/json/value.h> #include "cmGlobalVisualStudioGenerator.h" @@ -102,6 +104,10 @@ public: } const std::string& GetIntelProjectVersion(); + virtual cm::optional<std::string> GetPlatformToolsetFortran() const + { + return cm::nullopt; + } bool FindMakeProgram(cmMakefile* mf) override; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 72dba42..c6bb3df 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -2480,6 +2480,25 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS", swiftDefs.CreateList()); } + + if (cm::optional<cmSwiftCompileMode> swiftCompileMode = + this->CurrentLocalGenerator->GetSwiftCompileMode(gtgt, configName)) { + switch (*swiftCompileMode) { + case cmSwiftCompileMode::Wholemodule: + buildSettings->AddAttribute("SWIFT_COMPILATION_MODE", + this->CreateString("wholemodule")); + break; + case cmSwiftCompileMode::Incremental: + case cmSwiftCompileMode::Singlefile: + break; + case cmSwiftCompileMode::Unknown: + this->CurrentLocalGenerator->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("Unknown Swift_COMPILATION_MODE on target '", + gtgt->GetName(), "'")); + break; + } + } } std::string extraLinkOptionsVar; @@ -2501,6 +2520,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->CurrentLocalGenerator->GetStaticLibraryFlags( extraLinkOptions, configName, llang, gtgt); } else { + this->CurrentLocalGenerator->AppendLinkerTypeFlags(extraLinkOptions, gtgt, + configName, llang); + cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS"); if (targetLinkFlags) { this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, @@ -4278,6 +4300,15 @@ void cmGlobalXCodeGenerator::AddEmbeddedResources(cmXCodeObject* target) dstSubfolderSpec, NoActionOnCopyByDefault); } +void cmGlobalXCodeGenerator::AddEmbeddedXPCServices(cmXCodeObject* target) +{ + static const auto dstSubfolderSpec = "16"; + + this->AddEmbeddedObjects( + target, "Embed XPC Services", "XCODE_EMBED_XPC_SERVICES", dstSubfolderSpec, + NoActionOnCopyByDefault, "$(CONTENTS_FOLDER_PATH)/XPCServices"); +} + bool cmGlobalXCodeGenerator::CreateGroups( std::vector<cmLocalGenerator*>& generators) { @@ -4607,6 +4638,10 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( buildSettings->AddAttribute("CODE_SIGNING_ALLOWED", this->CreateString("NO")); } + + // This code supports the OLD behavior of CMP0157. We should be able to + // remove computing the debug configuration set once the old behavior is + // removed. auto debugConfigs = this->GetCMakeInstance()->GetDebugConfigs(); std::set<std::string> debugConfigSet(debugConfigs.begin(), debugConfigs.end()); @@ -4616,9 +4651,16 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings); - if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) { - buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE", - this->CreateString("wholemodule")); + // Supports the OLD behavior of CMP0157. CMP0157 OLD behavior globally set + // wholemodule compilation for all non-debug configurations, for all + // targets. + if (this->CurrentMakefile + ->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT") + .IsEmpty()) { + if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) { + buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE", + this->CreateString("wholemodule")); + } } // Put this last so it can override existing settings @@ -4680,6 +4722,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( this->AddEmbeddedAppExtensions(t); this->AddEmbeddedExtensionKitExtensions(t); this->AddEmbeddedResources(t); + this->AddEmbeddedXPCServices(t); // Inherit project-wide values for any target-specific search paths. this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS"); this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS"); diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index da0a4ea..12a5cad 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -225,6 +225,7 @@ private: void AddEmbeddedAppExtensions(cmXCodeObject* target); void AddEmbeddedExtensionKitExtensions(cmXCodeObject* target); void AddEmbeddedResources(cmXCodeObject* target); + void AddEmbeddedXPCServices(cmXCodeObject* target); void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target, cmXCodeObject* buildSettings, const std::string& configName); diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 0fc4011..e2755da 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2030,7 +2030,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, + ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false, helper.Makefile->GetBacktrace())); return true; @@ -2054,12 +2054,14 @@ bool HandleExportMode(std::vector<std::string> const& args, bool exportOld = false; std::string filename; std::string cxx_modules_directory; + bool exportPackageDependencies = false; ica.Bind("EXPORT"_s, exp); ica.Bind("NAMESPACE"_s, name_space); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + ica.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, exportPackageDependencies); std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -2147,7 +2149,8 @@ bool HandleExportMode(std::vector<std::string> const& args, &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, - exportOld, false, helper.Makefile->GetBacktrace())); + exportOld, false, exportPackageDependencies, + helper.Makefile->GetBacktrace())); return true; } diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 71d5471..72f0ac7 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -27,7 +27,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( std::string const& component, MessageLevel message, bool exclude_from_all, std::string filename, std::string name_space, std::string cxx_modules_directory, bool exportOld, bool android, - cmListFileBacktrace backtrace) + bool exportPackageDependencies, cmListFileBacktrace backtrace) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all, false, std::move(backtrace)) , ExportSet(exportSet) @@ -36,6 +36,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( , Namespace(std::move(name_space)) , CxxModulesDirectory(std::move(cxx_modules_directory)) , ExportOld(exportOld) + , ExportPackageDependencies(exportPackageDependencies) { if (android) { #ifndef CMAKE_BOOTSTRAP @@ -119,6 +120,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) this->EFGen->AddConfiguration(c); } } + this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies); this->EFGen->GenerateImportFile(); // Perform the main install script generation. diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index f2d4a05..5f92851 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -29,7 +29,8 @@ public: bool exclude_from_all, std::string filename, std::string name_space, std::string cxx_modules_directory, bool exportOld, - bool android, cmListFileBacktrace backtrace); + bool android, bool exportPackageDependencies, + cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -70,6 +71,7 @@ protected: std::string const Namespace; std::string const CxxModulesDirectory; bool const ExportOld; + bool const ExportPackageDependencies; cmLocalGenerator* LocalGenerator = nullptr; std::string TempDir; diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx index 1abdaa6..5c44fba 100644 --- a/Source/cmJSONState.cxx +++ b/Source/cmJSONState.cxx @@ -45,7 +45,7 @@ cmJSONState::cmJSONState(const std::string& filename, Json::Value* root) void cmJSONState::AddError(std::string const& errMsg) { - this->errors.push_back(Error(errMsg)); + this->errors.emplace_back(errMsg); } void cmJSONState::AddErrorAtValue(std::string const& errMsg, @@ -65,7 +65,7 @@ void cmJSONState::AddErrorAtOffset(std::string const& errMsg, this->AddError(errMsg); } else { Location loc = LocateInDocument(offset); - this->errors.push_back(Error(loc, errMsg)); + this->errors.emplace_back(loc, errMsg); } } @@ -118,7 +118,7 @@ const Json::Value* cmJSONState::value_after(std::string const& k) void cmJSONState::push_stack(std::string const& k, const Json::Value* value) { - this->parseStack.push_back(JsonPair(k, value)); + this->parseStack.emplace_back(k, value); } void cmJSONState::pop_stack() diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx index 2dc40ff..3654176 100644 --- a/Source/cmLinkItem.cxx +++ b/Source/cmLinkItem.cxx @@ -4,20 +4,30 @@ #include <utility> // IWYU pragma: keep +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + #include "cmGeneratorTarget.h" +#include "cmStringAlgorithms.h" + +const std::string cmLinkItem::DEFAULT = "DEFAULT"; cmLinkItem::cmLinkItem() = default; -cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt) +cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt, + std::string feature) : String(std::move(n)) + , Feature(std::move(feature)) , Cross(c) , Backtrace(std::move(bt)) { } cmLinkItem::cmLinkItem(cmGeneratorTarget const* t, bool c, - cmListFileBacktrace bt) + cmListFileBacktrace bt, std::string feature) : Target(t) + , Feature(std::move(feature)) , Cross(c) , Backtrace(std::move(bt)) { @@ -73,3 +83,19 @@ cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool checkCMP0027) , CheckCMP0027(checkCMP0027) { } + +namespace { +const cm::string_view LL_BEGIN = "<LINK_LIBRARY:"_s; +const cm::string_view LL_END = "</LINK_LIBRARY:"_s; +} +cm::optional<std::string> ParseLinkFeature(std::string const& item) +{ + if (cmHasPrefix(item, LL_BEGIN) && cmHasSuffix(item, '>')) { + return item.substr(LL_BEGIN.length(), + item.find('>', LL_BEGIN.length()) - LL_BEGIN.length()); + } + if (cmHasPrefix(item, LL_END) && cmHasSuffix(item, '>')) { + return cmLinkItem::DEFAULT; + } + return cm::nullopt; +} diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h index e4444d3..1946c9b 100644 --- a/Source/cmLinkItem.h +++ b/Source/cmLinkItem.h @@ -10,6 +10,7 @@ #include <unordered_map> #include <vector> +#include <cm/optional> #include <cmext/algorithm> #include "cmListFileCache.h" @@ -25,14 +26,20 @@ class cmLinkItem std::string String; public: + // default feature: link library without decoration + static const std::string DEFAULT; + cmLinkItem(); - cmLinkItem(std::string s, bool c, cmListFileBacktrace bt); - cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt); + cmLinkItem(std::string s, bool c, cmListFileBacktrace bt, + std::string feature = DEFAULT); + cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt, + std::string feature = DEFAULT); std::string const& AsStr() const; cmGeneratorTarget const* Target = nullptr; // The source file representing the external object (used when linking // `$<TARGET_OBJECTS>`) cmSourceFile const* ObjectSource = nullptr; + std::string Feature; bool Cross = false; cmListFileBacktrace Backtrace; friend bool operator<(cmLinkItem const& l, cmLinkItem const& r); @@ -160,3 +167,6 @@ inline cmTargetLinkLibraryType CMP0003_ComputeLinkType( // The current configuration is not a debug configuration. return OPTIMIZED_LibraryType; } + +// Parse LINK_LIBRARY genex markers. +cm::optional<std::string> ParseLinkFeature(std::string const& item); diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx index 0ad846b..a63b794 100644 --- a/Source/cmLinkItemGraphVisitor.cxx +++ b/Source/cmLinkItemGraphVisitor.cxx @@ -82,7 +82,7 @@ bool cmLinkItemGraphVisitor::ItemVisited(cmLinkItem const& item) bool cmLinkItemGraphVisitor::LinkVisited(cmLinkItem const& depender, cmLinkItem const& dependee) { - auto const link = std::make_pair<>(depender.AsStr(), dependee.AsStr()); + auto const link = std::make_pair(depender.AsStr(), dependee.AsStr()); bool const linkVisited = this->VisitedLinks.find(link) != this->VisitedLinks.cend(); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 168cd41..38c49ed 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -82,7 +82,6 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER", "CMAKE_CURRENT_SOURCE_DIR", "CMAKE_CURRENT_BINARY_DIR", "CMAKE_RANLIB", - "CMAKE_LINKER", "CMAKE_MT", "CMAKE_TAPI", "CMAKE_CUDA_HOST_COMPILER", @@ -1367,7 +1366,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags( { const std::string configUpper = cmSystemTools::UpperCase(config); std::vector<BT<std::string>> flags; - if (linkLanguage != "Swift") { + if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) { std::string staticLibFlags; this->AppendFlags( staticLibFlags, @@ -1604,6 +1603,7 @@ void cmLocalGenerator::GetTargetFlags( } std::string extraLinkFlags; + this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage); this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config, linkLanguage); this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage); @@ -1651,6 +1651,39 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags( if (lang == "Fortran") { this->AppendFlags(compileFlags, this->GetTargetFortranFlags(target, config)); + } else if (lang == "Swift") { + // Only set the compile mode if CMP0157 is set + if (cm::optional<cmSwiftCompileMode> swiftCompileMode = + this->GetSwiftCompileMode(target, config)) { + std::string swiftCompileModeFlag; + switch (*swiftCompileMode) { + case cmSwiftCompileMode::Incremental: { + swiftCompileModeFlag = "-incremental"; + if (cmValue flag = + mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) { + swiftCompileModeFlag = *flag; + } + break; + } + case cmSwiftCompileMode::Wholemodule: { + swiftCompileModeFlag = "-wmo"; + if (cmValue flag = + mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) { + swiftCompileModeFlag = *flag; + } + break; + } + case cmSwiftCompileMode::Singlefile: + break; + case cmSwiftCompileMode::Unknown: { + this->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("Unknown Swift_COMPILATION_MODE on target '", + target->GetName(), "'")); + } + } + this->AppendFlags(compileFlags, swiftCompileModeFlag); + } } this->AddCMP0018Flags(compileFlags, target, lang, config); @@ -2959,6 +2992,40 @@ cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName( return msvcDebugInformationFormat; } +cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode( + cmGeneratorTarget const* target, std::string const& config) +{ + cmMakefile const* mf = this->GetMakefile(); + cmValue const swiftCompileModeDefault = + mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT"); + if (!cmNonempty(swiftCompileModeDefault)) { + return {}; + } + cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE"); + if (!swiftCompileMode) { + swiftCompileMode = swiftCompileModeDefault; + } + + std::string const expandedCompileMode = + cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target); + if (expandedCompileMode == "wholemodule") { + return cmSwiftCompileMode::Wholemodule; + } + if (expandedCompileMode == "singlefile") { + return cmSwiftCompileMode::Singlefile; + } + if (expandedCompileMode == "incremental") { + return cmSwiftCompileMode::Incremental; + } + return cmSwiftCompileMode::Unknown; +} + +bool cmLocalGenerator::IsSplitSwiftBuild() const +{ + return cmNonempty(this->GetMakefile()->GetDefinition( + "CMAKE_Swift_COMPILATION_MODE_DEFAULT")); +} + namespace { inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf, @@ -3067,8 +3134,17 @@ cmLocalGenerator::AddUnityFilesModeAuto( chunk = std::min(itemsLeft, batchSize); - std::string filename = cmStrCat(filename_base, "unity_", batch, - (lang == "C") ? "_c.c" : "_cxx.cxx"); + std::string extension; + if (lang == "C") { + extension = "_c.c"; + } else if (lang == "CXX") { + extension = "_cxx.cxx"; + } else if (lang == "OBJC") { + extension = "_m.m"; + } else if (lang == "OBJCXX") { + extension = "_mm.mm"; + } + std::string filename = cmStrCat(filename_base, "unity_", batch, extension); auto const begin = filtered_sources.begin() + batch * batchSize; auto const end = begin + chunk; unity_files.emplace_back(this->WriteUnitySource( @@ -3159,7 +3235,7 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target) cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE"); cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE"); - for (std::string lang : { "C", "CXX" }) { + for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX" }) { std::vector<UnityBatchedSource> filtered_sources; std::copy_if(unitySources.begin(), unitySources.end(), std::back_inserter(filtered_sources), @@ -3205,6 +3281,49 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target) } } +void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags, + cmGeneratorTarget* target, + const std::string& config, + const std::string& linkLanguage) +{ + switch (target->GetType()) { + case cmStateEnums::EXECUTABLE: + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + break; + default: + return; + } + + auto usingLinker = + cmStrCat("CMAKE_", linkLanguage, "_USING_", + target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_"); + + auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE")); + if (format && format != "FLAG"_s) { + return; + } + + auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config); + if (linkerType.empty()) { + linkerType = "DEFAULT"; + } + usingLinker = cmStrCat(usingLinker, linkerType); + auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker); + if (linkerTypeFlags) { + if (!linkerTypeFlags.IsEmpty()) { + auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags); + target->ResolveLinkerWrapper(linkerFlags, linkLanguage); + this->AppendFlags(flags, linkerFlags); + } + } else if (linkerType != "DEFAULT"_s) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("LINKER_TYPE '", linkerType, + "' is unknown. Did you forgot to define '", + usingLinker, "' variable?")); + } +} + void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target, const std::string& config, diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index a920cfe..3ec349b 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -67,6 +67,15 @@ enum class cmBuildStep Link }; +/** What compilation mode the swift files are in */ +enum class cmSwiftCompileMode +{ + Wholemodule, + Incremental, + Singlefile, + Unknown, +}; + /** Target and source file which have a specific output. */ struct cmSourcesWithOutput { @@ -177,6 +186,9 @@ public: void AddPchDependencies(cmGeneratorTarget* target); void AddUnityBuild(cmGeneratorTarget* target); virtual void AddXCConfigSources(cmGeneratorTarget* /* target */) {} + void AppendLinkerTypeFlags(std::string& flags, cmGeneratorTarget* target, + const std::string& config, + const std::string& linkLanguage); void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target, const std::string& config, const std::string& lang); @@ -546,6 +558,14 @@ public: const std::string& prop, const std::string& config); + // Return Swift_COMPILATION_MODE value if CMP0157 is NEW. + cm::optional<cmSwiftCompileMode> GetSwiftCompileMode( + cmGeneratorTarget const* target, std::string const& config); + + // Can we build Swift with a separate object build and link step + // (If CMP0157 is NEW, we can do a split build) + bool IsSplitSwiftBuild() const; + protected: // The default implementation converts to a Windows shortpath to // help older toolchains handle spaces and such. A generator may diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 7b02c56..d315f0f 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -32,6 +32,7 @@ #include "cmList.h" #include "cmListFileCache.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmSourceFile.h" @@ -794,6 +795,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( target->GetType() == cmStateEnums::OBJECT_LIBRARY ? ".lib" : cmSystemTools::GetFilenameLastExtension(targetNameFull); + if (cm::optional<std::string> fortran = gg->GetPlatformToolsetFortran()) { + fout << "\t\t\tUseCompiler=\"" << *fortran << "Compiler\"\n"; + } /* clang-format off */ fout << "\t\t\tTargetName=\"" << this->EscapeForXML(targetName) << "\"\n" @@ -1085,6 +1089,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( cmComputeLinkInformation& cli = *pcli; std::string linkLanguage = cli.GetLinkLanguage(); + if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) { + // Visual Studio 10 or upper is required for this feature + this->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("'LINKER_TYPE' property, specified on target '", + target->GetName(), + "', is not supported by this generator."), + target->GetBacktrace()); + } + // Compute the variable name to lookup standard libraries for this // language. std::string standardLibsVar = @@ -1161,6 +1175,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( cmComputeLinkInformation& cli = *pcli; std::string linkLanguage = cli.GetLinkLanguage(); + if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) { + // Visual Studio 10 or upper is required for this feature + this->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("'LINKER_TYPE' property, specified on target '", + target->GetName(), + "', is not supported by this generator."), + target->GetBacktrace()); + } + bool isWin32Executable = target->IsWin32Executable(configName); // Compute the variable name to lookup standard libraries for this diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 93fb8b4..936b282 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -302,6 +302,11 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const return this->Backtrace; } +cmFindPackageStack cmMakefile::GetFindPackageStack() const +{ + return this->FindPackageStack; +} + void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, cmListFileBacktrace const& bt, CommandMissingFromStack missing) const @@ -1215,8 +1220,8 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( std::move(cc), - [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, - std::unique_ptr<cmCustomCommand> tcc) { + [this, t, type](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { BacktraceGuard guard(this->Backtrace, lfbt); tcc->SetBacktrace(lfbt); detail::AddCustomCommandToTarget(lg, cmCommandOrigin::Project, t, type, @@ -1254,8 +1259,9 @@ void cmMakefile::AddCustomCommandToOutput( // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( std::move(cc), - [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, - std::unique_ptr<cmCustomCommand> tcc) { + [this, replace, callback](cmLocalGenerator& lg, + const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { BacktraceGuard guard(this->Backtrace, lfbt); tcc->SetBacktrace(lfbt); cmSourceFile* sf = detail::AddCustomCommandToOutput( @@ -1341,7 +1347,8 @@ void cmMakefile::AppendCustomCommandToOutput( if (this->ValidateCustomCommand(commandLines)) { // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( - [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) { + [this, output, depends, implicit_depends, + commandLines](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) { BacktraceGuard guard(this->Backtrace, lfbt); detail::AppendCustomCommandToOutput(lg, lfbt, output, depends, implicit_depends, commandLines); @@ -1372,8 +1379,8 @@ cmTarget* cmMakefile::AddUtilityCommand(const std::string& utilityName, // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( std::move(cc), - [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, - std::unique_ptr<cmCustomCommand> tcc) { + [this, target](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr<cmCustomCommand> tcc) { BacktraceGuard guard(this->Backtrace, lfbt); tcc->SetBacktrace(lfbt); detail::AddUtilityCommand(lg, cmCommandOrigin::Project, target, @@ -4633,12 +4640,13 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0120 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0126 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 || - id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104)) && + id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104 || + id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126)) && (!this->IsSet("CMAKE_WARN_DEPRECATED") || this->IsOn("CMAKE_WARN_DEPRECATED"))) { this->IssueMessage(MessageType::DEPRECATION_WARNING, @@ -4771,6 +4779,36 @@ cmMakefile::MacroPushPop::~MacroPushPop() this->Makefile->PopMacroScope(this->ReportError); } +cmMakefile::FindPackageStackRAII::FindPackageStackRAII(cmMakefile* mf, + std::string const& name) + : Makefile(mf) +{ + this->Makefile->FindPackageStack = + this->Makefile->FindPackageStack.Push(cmFindPackageCall{ + name, + this->Makefile->FindPackageStackNextIndex, + }); + this->Makefile->FindPackageStackNextIndex++; +} + +cmMakefile::FindPackageStackRAII::~FindPackageStackRAII() +{ + this->Makefile->FindPackageStackNextIndex = + this->Makefile->FindPackageStack.Top().Index + 1; + this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); + + if (!this->Makefile->FindPackageStack.Empty()) { + auto top = this->Makefile->FindPackageStack.Top(); + this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); + + top.Index = this->Makefile->FindPackageStackNextIndex; + this->Makefile->FindPackageStackNextIndex++; + + this->Makefile->FindPackageStack = + this->Makefile->FindPackageStack.Push(top); + } +} + cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf, std::string const& pkg) : Makefile(mf) diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 24daa72..e5edbae 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -25,6 +25,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" +#include "cmFindPackageStack.h" #include "cmFunctionBlocker.h" #include "cmListFileCache.h" #include "cmMessageType.h" // IWYU pragma: keep @@ -660,6 +661,11 @@ public: cmListFileBacktrace GetBacktrace() const; /** + * Get the current stack of find_package calls. + */ + cmFindPackageStack GetFindPackageStack() const; + + /** * Get the vector of files created by this makefile */ const std::vector<std::string>& GetOutputFiles() const @@ -1020,6 +1026,15 @@ public: // searches std::deque<std::vector<std::string>> FindPackageRootPathStack; + class FindPackageStackRAII + { + cmMakefile* Makefile; + + public: + FindPackageStackRAII(cmMakefile* mf, std::string const& pkg); + ~FindPackageStackRAII(); + }; + class DebugFindPkgRAII { cmMakefile* Makefile; @@ -1210,6 +1225,9 @@ private: std::vector<BT<GeneratorAction>> GeneratorActions; bool GeneratorActionsInvoked = false; + cmFindPackageStack FindPackageStack; + unsigned int FindPackageStackNextIndex = 0; + bool DebugFindPkg = false; bool CheckSystemVars; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 4a2b9e8..96a0d5c 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -344,6 +344,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) return; } + auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName()); + // Build list of dependencies. std::vector<std::string> depends; this->AppendLinkDepends(depends, linkLanguage); @@ -533,6 +535,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) vars.CMTargetType = cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str(); vars.Language = linkLanguage.c_str(); + vars.Linker = linker.c_str(); vars.AIXExports = aixExports.c_str(); vars.Objects = buildObjs.c_str(); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index fc3caa1..bc48a3b 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -218,6 +218,9 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink) extraFlags, this->GeneratorTarget, linkLineComputer.get(), this->GetConfigName()); + this->UseLWYU = this->LocalGenerator->AppendLWYUFlags( + extraFlags, this->GeneratorTarget, linkLanguage); + this->WriteLibraryRules(linkRuleVar, extraFlags, relink); } @@ -441,6 +444,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( return; } + auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName()); + // Build list of dependencies. std::vector<std::string> depends; this->AppendLinkDepends(depends, linkLanguage); @@ -766,6 +771,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( vars.CMTargetType = cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str(); vars.Language = linkLanguage.c_str(); + vars.Linker = linker.c_str(); vars.AIXExports = aixExports.c_str(); vars.Objects = buildObjs.c_str(); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 74b4b75..8fda774 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -153,6 +153,8 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags( this->LocalGenerator->AppendCompileOptions(flags, opts); this->LocalGenerator->SetLinkScriptShell(false); + this->LocalGenerator->AppendLinkerTypeFlags( + flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); this->LocalGenerator->AppendPositionIndependentLinkerFlags( flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); this->LocalGenerator->AppendDependencyInfoLinkerFlags( diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 48c30b6..99ea009 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -294,6 +294,9 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule( .c_str(); vars.Language = "CUDA"; + std::string linker = + this->GetGeneratorTarget()->GetLinkerTool("CUDA", config); + vars.Linker = linker.c_str(); // build response file name std::string responseFlag = this->GetMakefile()->GetSafeDefinition( @@ -400,6 +403,9 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules( vars.Fatbinary = "$FATBIN"; vars.RegisterFile = "$REGISTER"; vars.LinkFlags = "$LINK_FLAGS"; + std::string linker = + this->GetGeneratorTarget()->GetLinkerTool("CUDA", config); + vars.Linker = linker.c_str(); std::string flags = this->GetFlags("CUDA", config); vars.Flags = flags.c_str(); @@ -441,6 +447,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str(); + std::string linker = this->GetGeneratorTarget()->GetLinkerTool(config); + vars.Linker = linker.c_str(); std::string lang = this->TargetLinkLanguage(config); vars.Language = lang.c_str(); vars.AIXExports = "$AIX_EXPORTS"; @@ -1201,7 +1209,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); } - if (this->TargetLinkLanguage(config) == "Swift") { + // If we can't split the Swift build model (CMP0157 is OLD or unset), fall + // back on the old one-step "build/link" logic. + if (!this->GetLocalGenerator()->IsSplitSwiftBuild() && + this->TargetLinkLanguage(config) == "Swift") { vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string { cmGeneratorTarget::Names targetNames = this->GetGeneratorTarget()->GetLibraryNames(config); @@ -1214,12 +1225,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmOutputConverter::SHELL); vars["SWIFT_SOURCES"] = [this, config]() -> std::string { - std::vector<cmSourceFile const*> sources; + std::vector<cmSourceFile const*> sourceFiles; std::stringstream oss; - this->GetGeneratorTarget()->GetObjectSources(sources, config); + this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); cmLocalGenerator const* LocalGen = this->GetLocalGenerator(); - for (const auto& source : sources) { + for (const auto& source : sourceFiles) { const std::string sourcePath = source->GetLanguage() == "Swift" ? this->GetCompiledSourceNinjaPath(source) : this->GetObjectFilePath(source, config); @@ -1237,10 +1248,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( vars["FLAGS"] = this->GetFlags("Swift", config); vars["INCLUDES"] = this->GetIncludes("Swift", config); this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); - } - // Compute specific libraries to link with. - if (this->TargetLinkLanguage(config) == "Swift") { + // Compute specific libraries to link with. std::vector<cmSourceFile const*> sources; gt->GetObjectSources(sources, config); for (const auto& source : sources) { diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 4025918..2cb1284 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -14,6 +14,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -31,6 +32,7 @@ #include "cmGlobalCommonGenerator.h" #include "cmGlobalNinjaGenerator.h" #include "cmList.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" @@ -47,6 +49,7 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetDepend.h" #include "cmValue.h" #include "cmake.h" @@ -145,7 +148,7 @@ std::string cmNinjaTargetGenerator::LanguageScanRule( bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( std::string const& lang) const { - return lang == "Fortran"; + return lang == "Fortran" || lang == "Swift"; } bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const @@ -1136,9 +1139,19 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, config); + std::vector<cmSourceFile const*> swiftSources; + for (cmSourceFile const* sf : objectSources) { - this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig); + if (this->GetLocalGenerator()->IsSplitSwiftBuild() && + sf->GetLanguage() == "Swift") { + swiftSources.push_back(sf); + } else { + this->WriteObjectBuildStatement(sf, config, fileConfig, + firstForConfig); + } } + WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig, + firstForConfig); } { @@ -1234,9 +1247,11 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) { return *name; } - return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), - '/', config, '/', - target->GetName(), ".swiftdeps")); + return this->GetLocalGenerator()->ConvertToOutputFormat( + this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), '/', + config, '/', target->GetName(), + ".swiftdeps")), + cmOutputConverter::SHELL); }(); std::string mapFilePath = @@ -1254,8 +1269,10 @@ void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( // Add flag this->LocalGenerator->AppendFlags(flags, "-output-file-map"); - this->LocalGenerator->AppendFlagEscape(flags, - ConvertToNinjaPath(mapFilePath)); + this->LocalGenerator->AppendFlagEscape( + flags, + this->GetLocalGenerator()->ConvertToOutputFormat( + ConvertToNinjaPath(mapFilePath), cmOutputConverter::SHELL)); } namespace { @@ -1431,10 +1448,13 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } + this->SetMsvcTargetPdbVariable(vars, config); + if (firstForConfig) { this->ExportObjectCompileCommand( language, sourceFilePath, objectDir, objectFileName, objectFileDir, - vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], + vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config); } objBuild.Outputs.push_back(objectFileName); @@ -1625,8 +1645,6 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } - this->SetMsvcTargetPdbVariable(vars, config); - objBuild.RspFile = cmStrCat(objectFileName, ".rsp"); if (language == "ISPC") { @@ -1777,10 +1795,13 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile; } + this->SetMsvcTargetPdbVariable(vars, config); + if (firstForConfig) { this->ExportObjectCompileCommand( language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"], - vars["DEFINES"], vars["INCLUDES"], config); + vars["DEFINES"], vars["INCLUDES"], vars["TARGET_COMPILE_PDB"], + vars["TARGET_PDB"], config); } bmiBuild.Outputs.push_back(bmiFileName); @@ -1851,14 +1872,220 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), vars); - this->SetMsvcTargetPdbVariable(vars, config); - bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), bmiBuild, commandLineLengthLimit); } +void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement( + std::vector<cmSourceFile const*> const& sources, std::string const& config, + std::string const& fileConfig, bool firstForConfig) +{ + // Swift sources are compiled as a module, not individually like with C/C++. + // Flags, header search paths, and definitions are passed to the entire + // module build, but we still need to emit compile-commands for each source + // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS. + // In whole-module mode, with a single thread, the Swift compiler will + // only emit a single object file, but if more than one thread is specified, + // or building in other modes, the compiler will emit multiple object files. + // When building a single-output, we do not provide an output-file-map (OFM), + // and instead pass `-o` to tell the compiler where to write the object. + // When building multiple outputs, we provide an OFM to tell the compiler + // where to put each object. + // + // + // Per-Target (module): + // - Flags + // - Definitions + // - Include paths + // - (single-output) output object filename + // - Swiftmodule + // + // Per-File: + // - compile-command + // - (multi-output) OFM data + // - (multi-output) output object filename + // + // Note: Due to the differences in the build models, we are only able to + // build the object build-graph if we know what mode the target is built in. + // For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to + // fall back on the old "linker" build. Otherwise, this should be + // indistinguishable from the old behavior. + // + // FIXME(#25490): Add response file support to Swift object build step + // FIXME(#25491): Include all files in module in compile_commands.json + + if (sources.empty()) { + return; + } + + cmSwiftCompileMode compileMode; + if (cm::optional<cmSwiftCompileMode> optionalCompileMode = + this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget, + config)) { + compileMode = *optionalCompileMode; + } else { + // CMP0157 is not NEW, bailing early! + return; + } + + auto getTargetPropertyOrDefault = + [](cmGeneratorTarget const& target, std::string const& property, + std::string defaultValue) -> std::string { + if (cmValue value = target.GetProperty(property)) { + return *value; + } + return defaultValue; + }; + + std::string const language = "Swift"; + std::string const objectDir = this->ConvertToNinjaPath( + cmStrCat(this->GeneratorTarget->GetSupportDirectory(), + this->GetGlobalGenerator()->ConfigDirectory(config))); + + cmGeneratorTarget const& target = *this->GeneratorTarget; + cmNinjaBuild objBuild( + this->LanguageCompilerRule(language, config, WithScanning::No)); + cmNinjaVars& vars = objBuild.Variables; + + std::string const moduleName = + getTargetPropertyOrDefault(target, "Swift_MODULE_NAME", target.GetName()); + std::string const moduleDirectory = getTargetPropertyOrDefault( + target, "Swift_MODULE_DIRECTORY", + target.LocalGenerator->GetCurrentBinaryDirectory()); + std::string const moduleFilename = getTargetPropertyOrDefault( + target, "Swift_MODULE", cmStrCat(moduleName, ".swiftmodule")); + std::string const moduleFilepath = + this->ConvertToNinjaPath(cmStrCat(moduleDirectory, '/', moduleFilename)); + + bool const isSingleOutput = [this, compileMode]() -> bool { + bool isMultiThread = false; + if (cmValue numThreadStr = + this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) { + unsigned long numThreads; + cmStrToULong(*numThreadStr, &numThreads); + // numThreads == 1 is multi-threaded according to swiftc + isMultiThread = numThreads > 0; + } + return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule; + }(); + + // Swift modules only make sense to emit from things that can be imported. + // Executables that don't export symbols can't be imported, so don't try to + // emit a swiftmodule for them. It will break. + if (target.GetType() != cmStateEnums::EXECUTABLE || + target.IsExecutableWithExports()) { + std::string const emitModuleFlag = "-emit-module"; + std::string const modulePathFlag = "-emit-module-path"; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleFilepath }); + objBuild.Outputs.push_back(moduleFilepath); + + std::string const moduleNameFlag = "-module-name"; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], cmStrCat(moduleNameFlag, ' ', moduleName)); + } + + if (target.GetType() != cmStateEnums::EXECUTABLE) { + std::string const libraryLinkNameFlag = "-module-link-name"; + std::string const libraryLinkName = + this->GetGeneratorTarget()->GetLibraryNames(config).Base; + this->LocalGenerator->AppendFlags( + vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName)); + } + + // Without `-emit-library` or `-emit-executable`, targets with a single + // source file parse as a Swift script instead of like normal source. For + // non-executable targets, append this to ensure that they are parsed like a + // normal source. + if (target.GetType() != cmStateEnums::EXECUTABLE) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library"); + } + + this->LocalGenerator->AppendFlags(vars["FLAGS"], + this->GetFlags(language, config)); + vars["DEFINES"] = this->GetDefines(language, config); + vars["INCLUDES"] = this->GetIncludes(language, config); + + // target-level object filename + std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat( + objectDir, '/', moduleName, + this->GetGlobalGenerator()->GetLanguageOutputExtension(language))); + + if (isSingleOutput) { + this->LocalGenerator->AppendFlags(vars["FLAGS"], + cmStrCat("-o ", targetObjectFilename)); + objBuild.Outputs.push_back(targetObjectFilename); + this->Configs[config].Objects.push_back(targetObjectFilename); + } + + for (cmSourceFile const* sf : sources) { + // Add dependency to object build on each source file + std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf); + objBuild.ExplicitDeps.push_back(sourceFilePath); + + if (isSingleOutput) { + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, targetObjectFilename, + cmSystemTools::GetFilenamePath(targetObjectFilename), vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], + /*compile pdb*/ "", /*target pdb*/ "", config); + } + } else { + // Object outputs + std::string const objectFilepath = + this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config)); + this->EnsureParentDirectoryExists(objectFilepath); + objBuild.Outputs.push_back(objectFilepath); + this->Configs[config].Objects.push_back(objectFilepath); + + // Add OFM data + this->EmitSwiftDependencyInfo(sf, config); + + // Emit compile commands + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFilepath, + cmSystemTools::GetFilenamePath(objectFilepath), vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], + /*compile pdb*/ "", + /*target pdb*/ "", config); + } + } + } + + if (!isSingleOutput) { + this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]); + } + + for (cmTargetDepend const& dep : + this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) { + if (!dep->IsLanguageUsed("Swift", config)) { + continue; + } + // Add dependencies on the emitted swiftmodule file from swift targets we + // depend on + std::string const depModuleName = + getTargetPropertyOrDefault(*dep, "Swift_MODULE_NAME", dep->GetName()); + std::string const depModuleDir = getTargetPropertyOrDefault( + *dep, "Swift_MODULE_DIRECTORY", + dep->LocalGenerator->GetCurrentBinaryDirectory()); + std::string const depModuleFilename = getTargetPropertyOrDefault( + *dep, "Swift_MODULE", cmStrCat(depModuleName, ".swiftmodule")); + std::string const depModuleFilepath = + this->ConvertToNinjaPath(cmStrCat(depModuleDir, '/', depModuleFilename)); + objBuild.ImplicitDeps.push_back(depModuleFilepath); + } + + objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); + + // Write object build + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + objBuild); +} + void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, const std::string& config) { @@ -1992,6 +2219,7 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand( std::string const& objectDir, std::string const& objectFileName, std::string const& objectFileDir, std::string const& flags, std::string const& defines, std::string const& includes, + std::string const& targetCompilePdb, std::string const& targetPdb, std::string const& outputConfig) { if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { @@ -2042,6 +2270,8 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand( compileObjectVars.Flags = fullFlags.c_str(); compileObjectVars.Defines = defines.c_str(); compileObjectVars.Includes = includes.c_str(); + compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str(); + compileObjectVars.TargetPDB = targetPdb.c_str(); // Rule for compiling object file. std::string cudaCompileMode; diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 3f56113..7a68599 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -171,6 +171,9 @@ protected: const std::string& config, const std::string& fileConfig, bool firstForConfig); + void WriteSwiftObjectBuildStatement( + std::vector<cmSourceFile const*> const& sources, const std::string& config, + const std::string& fileConfig, bool firstForConfig); void WriteObjectBuildStatement(cmSourceFile const* source, const std::string& config, const std::string& fileConfig, @@ -189,6 +192,7 @@ protected: std::string const& objectDir, std::string const& objectFileName, std::string const& objectFileDir, std::string const& flags, std::string const& defines, std::string const& includes, + std::string const& targetCompilePdb, std::string const& targetPdb, std::string const& outputConfig); void AdditionalCleanFiles(const std::string& config); diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 8838de4..f038c6b 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -473,6 +473,17 @@ class cmMakefile; SELECT(POLICY, CMP0155, \ "C++ sources in targets with at least C++20 are scanned for " \ "imports when supported.", \ + 3, 28, 0, cmPolicies::WARN) \ + SELECT( \ + POLICY, CMP0156, \ + "De-duplicate libraries on link lines based on linker capabilities.", 3, \ + 29, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0157, \ + "Swift compilation mode selected by an abstraction.", 3, 29, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0158, \ + "add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when " \ + "cross-compiling.", \ 3, 28, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) @@ -513,7 +524,9 @@ class cmMakefile; F(CMP0131) \ F(CMP0142) \ F(CMP0154) \ - F(CMP0155) + F(CMP0155) \ + F(CMP0156) \ + F(CMP0157) #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \ F(CMP0116) \ diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 3aef299..53166c1 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -11,6 +11,7 @@ #include "cmsys/RegularExpression.hxx" #include "cmExecutionStatus.h" +#include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -371,29 +372,55 @@ static bool IncludeByVariable(cmExecutionStatus& status, if (!include) { return true; } + cmList includeFiles{ *include }; + + bool failed = false; + for (auto filePath : includeFiles) { + // Any relative path without a .cmake extension is checked for valid cmake + // modules. This logic should be consistent with CMake's include() command. + // Otherwise default to checking relative path w.r.t. source directory + if (!cmSystemTools::FileIsFullPath(filePath) && + !cmHasLiteralSuffix(filePath, ".cmake")) { + std::string mfile = mf.GetModulesFile(cmStrCat(filePath, ".cmake")); + if (mfile.empty()) { + status.SetError( + cmStrCat("could not find requested module:\n ", filePath)); + failed = true; + continue; + } + filePath = mfile; + } + std::string includeFile = cmSystemTools::CollapseFullPath( + filePath, mf.GetCurrentSourceDirectory()); + if (!cmSystemTools::FileExists(includeFile)) { + status.SetError( + cmStrCat("could not find requested file:\n ", filePath)); + failed = true; + continue; + } + if (cmSystemTools::FileIsDirectory(includeFile)) { + status.SetError( + cmStrCat("requested file is a directory:\n ", filePath)); + failed = true; + continue; + } - std::string includeFile = - cmSystemTools::CollapseFullPath(*include, mf.GetCurrentSourceDirectory()); - if (!cmSystemTools::FileExists(includeFile)) { - status.SetError(cmStrCat("could not find requested file:\n ", *include)); - return false; - } - if (cmSystemTools::FileIsDirectory(includeFile)) { - status.SetError(cmStrCat("requested file is a directory:\n ", *include)); - return false; - } + const bool readit = mf.ReadDependentFile(filePath); + if (readit) { + // If the included file ran successfully, continue to the next file + continue; + } - const bool readit = mf.ReadDependentFile(*include); - if (readit) { - return true; - } + if (cmSystemTools::GetFatalErrorOccurred()) { + failed = true; + continue; + } - if (cmSystemTools::GetFatalErrorOccurred()) { - return true; + status.SetError(cmStrCat("could not load requested file:\n ", filePath)); + failed = true; } - - status.SetError(cmStrCat("could not load requested file:\n ", *include)); - return false; + // At this point all files were processed + return !failed; } static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name, diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index adbdba8..0a394b5 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -76,13 +76,6 @@ static void MergeOptions(std::vector<std::string>& baseOpts, unsigned int const cmQtAutoGen::ParallelMax = 64; -#ifdef _WIN32 -// Actually 32767 (see -// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but we -// allow for a small margin -size_t const cmQtAutoGen::CommandLineLengthMax = 32000; -#endif - cm::string_view cmQtAutoGen::GeneratorName(GenT genType) { switch (genType) { diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index d111422..5a23ae9 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -64,11 +64,6 @@ public: /// @brief Maximum number of parallel threads/processes in a generator static unsigned int const ParallelMax; -#ifdef _WIN32 - /// @brief Maximum number of characters on command line - static size_t const CommandLineLengthMax; -#endif - /// @brief Returns the generator name static cm::string_view GeneratorName(GenT genType); /// @brief Returns the generator name in upper case diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 50b0ebe..8213274 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -485,6 +485,38 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } } +#ifdef _WIN32 + { + const auto& value = + this->GenTarget->GetProperty("AUTOGEN_COMMAND_LINE_LENGTH_MAX"); + if (value.IsSet()) { + using maxCommandLineLengthType = + decltype(this->AutogenTarget.MaxCommandLineLength); + unsigned long propInt = 0; + if (cmStrToULong(value, &propInt) && propInt > 0 && + propInt <= std::numeric_limits<maxCommandLineLengthType>::max()) { + this->AutogenTarget.MaxCommandLineLength = + static_cast<maxCommandLineLengthType>(propInt); + } else { + // Warn the project author that AUTOGEN_PARALLEL is not valid. + this->Makefile->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("AUTOGEN_COMMAND_LINE_LENGTH_MAX=\"", *value, + "\" for target \"", this->GenTarget->GetName(), + "\" is not valid. Using no limit for " + "AUTOGEN_COMMAND_LINE_LENGTH_MAX")); + this->AutogenTarget.MaxCommandLineLength = + std::numeric_limits<maxCommandLineLengthType>::max(); + } + } else { + // Actually 32767 (see + // https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but + // we allow for a small margin + this->AutogenTarget.MaxCommandLineLength = 32000; + } + } +#endif + // Autogen target info and settings files { // Info file @@ -1692,6 +1724,10 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() // General info.SetBool("MULTI_CONFIG", this->MultiConfig); info.SetUInt("PARALLEL", this->AutogenTarget.Parallel); +#ifdef _WIN32 + info.SetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX", + this->AutogenTarget.MaxCommandLineLength); +#endif info.SetUInt("VERBOSITY", this->Verbosity); // Directories diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index a44d33f..3f7ab9f 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cstddef> +#include <limits> #include <memory> #include <set> #include <string> @@ -194,6 +195,8 @@ private: bool GlobalTarget = false; // Settings unsigned int Parallel = 1; + unsigned int MaxCommandLineLength = + std::numeric_limits<unsigned int>::max(); // Configuration files std::string InfoFile; ConfigString SettingsFile; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index ece657d..a49125e 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <atomic> #include <cstddef> +#include <limits> #include <map> #include <mutex> #include <set> @@ -172,6 +173,8 @@ public: bool MultiConfig = false; IntegerVersion QtVersion = { 4, 0 }; unsigned int ThreadCount = 0; + unsigned int MaxCommandLineLength = + std::numeric_limits<unsigned int>::max(); // - Directories std::string AutogenBuildDir; std::string AutogenIncludeDir; @@ -190,7 +193,7 @@ public: { public: // -- Parse Cache - std::atomic<bool> ParseCacheChanged = ATOMIC_VAR_INIT(false); + std::atomic<bool> ParseCacheChanged{ false }; cmFileTime ParseCacheTime; ParseCacheT ParseCache; @@ -333,6 +336,13 @@ public: std::vector<std::string> const& command, std::string const& output) const; + /* + * Check if command line exceeds maximum length supported by OS + * (if on Windows) and switch to using a response file instead. + */ + void MaybeWriteResponseFile(std::string const& outputFile, + std::vector<std::string>& cmd) const; + /** @brief Run an external process. Use only during Process() call! */ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector<std::string> const& command, @@ -498,10 +508,6 @@ public: protected: ParseCacheT::FileHandleT CacheEntry; - - private: - void MaybeWriteMocResponseFile(std::string const& outputFile, - std::vector<std::string>& cmd) const; }; /** uic compiles a file. */ @@ -583,7 +589,7 @@ private: std::string SettingsStringMoc_; std::string SettingsStringUic_; // -- Worker thread pool - std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); + std::atomic<bool> JobError_{ false }; cmWorkerPool WorkerPool_; // -- Concurrent processing mutable std::mutex CMakeLibMutex_; @@ -795,6 +801,51 @@ void cmQtAutoMocUicT::JobT::LogCommandError( this->Gen()->Log().ErrorCommand(genType, message, command, output); } +/* + * Check if command line exceeds maximum length supported by OS + * (if on Windows) and switch to using a response file instead. + */ +void cmQtAutoMocUicT::JobT::MaybeWriteResponseFile( + std::string const& outputFile, std::vector<std::string>& cmd) const +{ +#ifdef _WIN32 + // Ensure cmd is less than CommandLineLengthMax characters + size_t commandLineLength = cmd.size(); // account for separating spaces + for (std::string const& str : cmd) { + commandLineLength += str.length(); + } + if (commandLineLength >= this->BaseConst().MaxCommandLineLength) { + // Command line exceeds maximum size allowed by OS + // => create response file + std::string const responseFile = cmStrCat(outputFile, ".rsp"); + + cmsys::ofstream fout(responseFile.c_str()); + if (!fout) { + this->LogError( + GenT::MOC, + cmStrCat("AUTOMOC was unable to create a response file at\n ", + this->MessagePath(responseFile))); + return; + } + + auto it = cmd.begin(); + while (++it != cmd.end()) { + fout << *it << "\n"; + } + fout.close(); + + // Keep all but executable + cmd.resize(1); + + // Specify response file + cmd.emplace_back(cmStrCat('@', responseFile)); + } +#else + static_cast<void>(outputFile); + static_cast<void>(cmd); +#endif +} + bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector<std::string> const& command, @@ -836,6 +887,8 @@ void cmQtAutoMocUicT::JobMocPredefsT::Process() cm::append(cmd, this->MocConst().OptionsDefinitions); // Add includes cm::append(cmd, this->MocConst().OptionsIncludes); + // Check if response file is necessary + MaybeWriteResponseFile(this->MocConst().PredefsFileAbs, cmd); // Execute command if (!this->RunProcess(GenT::MOC, result, cmd, reason.get())) { this->LogCommandError(GenT::MOC, @@ -2034,7 +2087,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() // Add source file cmd.push_back(sourceFile); - MaybeWriteMocResponseFile(outputFile, cmd); + MaybeWriteResponseFile(outputFile, cmd); } // Execute moc command @@ -2080,51 +2133,6 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() } } -/* - * Check if command line exceeds maximum length supported by OS - * (if on Windows) and switch to using a response file instead. - */ -void cmQtAutoMocUicT::JobCompileMocT::MaybeWriteMocResponseFile( - std::string const& outputFile, std::vector<std::string>& cmd) const -{ -#ifdef _WIN32 - // Ensure cmd is less than CommandLineLengthMax characters - size_t commandLineLength = cmd.size(); // account for separating spaces - for (std::string const& str : cmd) { - commandLineLength += str.length(); - } - if (commandLineLength >= CommandLineLengthMax) { - // Command line exceeds maximum size allowed by OS - // => create response file - std::string const responseFile = cmStrCat(outputFile, ".rsp"); - - cmsys::ofstream fout(responseFile.c_str()); - if (!fout) { - this->LogError( - GenT::MOC, - cmStrCat("AUTOMOC was unable to create a response file at\n ", - this->MessagePath(responseFile))); - return; - } - - auto it = cmd.begin(); - while (++it != cmd.end()) { - fout << *it << "\n"; - } - fout.close(); - - // Keep all but executable - cmd.resize(1); - - // Specify response file - cmd.emplace_back(cmStrCat('@', responseFile)); - } -#else - static_cast<void>(outputFile); - static_cast<void>(cmd); -#endif -} - void cmQtAutoMocUicT::JobCompileUicT::Process() { std::string const& sourceFile = this->Mapping->SourceFile->FileName; @@ -2377,6 +2385,10 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info) !info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor, true) || !info.GetUInt("PARALLEL", this->BaseConst_.ThreadCount, false) || +#ifdef _WIN32 + !info.GetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX", + this->BaseConst_.MaxCommandLineLength, false) || +#endif !info.GetString("BUILD_DIR", this->BaseConst_.AutogenBuildDir, true) || !info.GetStringConfig("INCLUDE_DIR", this->BaseConst_.AutogenIncludeDir, true) || diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index 638bb42..a8c81d0 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx @@ -27,6 +27,19 @@ std::string cmRulePlaceholderExpander::ExpandVariable( return this->ReplaceValues->LinkFlags; } } + if (this->ReplaceValues->Linker) { + if (variable == "CMAKE_LINKER") { + auto result = this->OutputConverter->ConvertToOutputForExisting( + this->ReplaceValues->Linker); + if (this->ReplaceValues->Launcher) { + // Add launcher as part of expansion so that it always appears + // immediately before the command itself, regardless of whether the + // overall rule template contains other content at the front. + result = cmStrCat(this->ReplaceValues->Launcher, " ", result); + } + return result; + } + } if (this->ReplaceValues->Manifests) { if (variable == "MANIFESTS") { return this->ReplaceValues->Manifests; @@ -325,17 +338,7 @@ std::string cmRulePlaceholderExpander::ExpandVariable( auto mapIt = this->VariableMappings.find(variable); if (mapIt != this->VariableMappings.end()) { if (variable.find("_FLAG") == std::string::npos) { - std::string ret = - this->OutputConverter->ConvertToOutputForExisting(mapIt->second); - - if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") { - // Add launcher as part of expansion so that it always appears - // immediately before the command itself, regardless of whether the - // overall rule template contains other content at the front. - ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret); - } - - return ret; + return this->OutputConverter->ConvertToOutputForExisting(mapIt->second); } return mapIt->second; } diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 5d1f199..225abd4 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -53,6 +53,7 @@ public: const char* SONameFlag = nullptr; const char* TargetSOName = nullptr; const char* TargetInstallNameDir = nullptr; + const char* Linker = nullptr; const char* LinkFlags = nullptr; const char* Manifests = nullptr; const char* LanguageCompileFlags = nullptr; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index abbf29e..832bf57 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -20,6 +20,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmFileSet.h" +#include "cmFindPackageStack.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -438,6 +439,7 @@ TargetProperty const StaticTargetProperties[] = { // ---- Swift { "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources }, { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources }, + { "Swift_COMPILATION_MODE"_s, IC::CanCompileSources }, // ---- moc { "AUTOMOC"_s, IC::CanCompileSources }, { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources }, @@ -456,6 +458,7 @@ TargetProperty const StaticTargetProperties[] = { { "AUTORCC_EXECUTABLE"_s, IC::CanCompileSources }, // Linking properties + { "LINKER_TYPE"_s, IC::CanCompileSources }, { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports }, { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget }, { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources }, @@ -549,6 +552,7 @@ TargetProperty const StaticTargetProperties[] = { { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources }, { "ANDROID_SKIP_ANT_STEP"_s, IC::CanCompileSources }, // -- Autogen + { "AUTOGEN_COMMAND_LINE_LENGTH_MAX"_s, IC::CanCompileSources }, { "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources }, { "AUTOGEN_PARALLEL"_s, IC::CanCompileSources }, { "AUTOGEN_USE_SYSTEM_INCLUDE"_s, IC::CanCompileSources }, @@ -584,11 +588,13 @@ TargetProperty const StaticTargetProperties[] = { // Usage requirement properties { "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources }, { "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig }, + { "EXPORT_FIND_PACKAGE_NAME"_s, IC::NormalTarget }, // Metadata { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget }, { "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources }, { "FOLDER"_s }, + { "TEST_LAUNCHER"_s, IC::ExecutableTarget }, // Xcode properties { "XCODE_GENERATE_SCHEME"_s, IC::NeedsXcode }, @@ -660,6 +666,7 @@ public: TLLCommands; std::map<std::string, cmFileSet> FileSets; cmListFileBacktrace Backtrace; + cmFindPackageStack FindPackageStack; UsageRequirementProperty IncludeDirectories; UsageRequirementProperty CompileOptions; @@ -960,6 +967,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, // Save the backtrace of target construction. this->impl->Backtrace = this->impl->Makefile->GetBacktrace(); + if (this->impl->IsImported()) { + this->impl->FindPackageStack = this->impl->Makefile->GetFindPackageStack(); + } if (this->IsNormal()) { // Initialize the INCLUDE_DIRECTORIES property based on the current value @@ -1247,6 +1257,11 @@ cmListFileBacktrace const& cmTarget::GetBacktrace() const return this->impl->Backtrace; } +cmFindPackageStack const& cmTarget::GetFindPackageStack() const +{ + return this->impl->FindPackageStack; +} + bool cmTarget::IsExecutableWithExports() const { return (this->GetType() == cmStateEnums::EXECUTABLE && diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 584856a..385dfe7 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -24,6 +24,7 @@ class cmCustomCommand; class cmFileSet; +class cmFindPackageStack; class cmGlobalGenerator; class cmInstallTargetGenerator; class cmMakefile; @@ -239,6 +240,9 @@ public: //! Get a backtrace from the creation of the target. cmListFileBacktrace const& GetBacktrace() const; + //! Get a find_package call stack from the creation of the target. + cmFindPackageStack const& GetFindPackageStack() const; + void InsertInclude(BT<std::string> const& entry, bool before = false); void InsertCompileOption(BT<std::string> const& entry, bool before = false); void InsertCompileDefinition(BT<std::string> const& entry); diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index 1cef888..caeb54d 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -4,6 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <map> #include <string> class cmFileSet; @@ -37,4 +38,5 @@ public: ///@} bool NamelinkOnly = false; + std::string XcFrameworkLocation; }; diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index b0d9c2d..7c9969c 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx @@ -9,6 +9,7 @@ cmTest::cmTest(cmMakefile* mf) : Backtrace(mf->GetBacktrace()) + , PolicyStatusCMP0158(mf->GetPolicyStatus(cmPolicies::CMP0158)) { this->Makefile = mf; this->OldStyle = true; diff --git a/Source/cmTest.h b/Source/cmTest.h index 8b50b87..480966a 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -9,6 +9,7 @@ #include <vector> #include "cmListFileCache.h" +#include "cmPolicies.h" #include "cmPropertyMap.h" #include "cmValue.h" @@ -60,6 +61,12 @@ public: bool GetOldStyle() const { return this->OldStyle; } void SetOldStyle(bool b) { this->OldStyle = b; } + /** Get/Set if CMP0158 policy is NEW */ + bool GetCMP0158IsNew() const + { + return this->PolicyStatusCMP0158 == cmPolicies::NEW; + } + /** Set/Get whether lists in command lines should be expanded. */ bool GetCommandExpandLists() const; void SetCommandExpandLists(bool b); @@ -74,4 +81,5 @@ private: cmMakefile* Makefile; cmListFileBacktrace Backtrace; + cmPolicies::PolicyStatus PolicyStatusCMP0158; }; diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index ca1226a..3194d8f 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -167,10 +167,24 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, if (target && target->GetType() == cmStateEnums::EXECUTABLE) { // Use the target file on disk. exe = target->GetFullPath(config); + auto useEmulator = !this->GetTest()->GetCMP0158IsNew() || + this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING"); + + // Prepend with the test launcher if specified. + cmValue launcher = target->GetProperty("TEST_LAUNCHER"); + if (cmNonempty(launcher)) { + cmList launcherWithArgs{ *launcher }; + std::string launcherExe(launcherWithArgs[0]); + cmSystemTools::ConvertToUnixSlashes(launcherExe); + os << cmOutputConverter::EscapeForCMake(launcherExe) << " "; + for (std::string const& arg : cmMakeRange(launcherWithArgs).advance(1)) { + os << cmOutputConverter::EscapeForCMake(arg) << " "; + } + } // Prepend with the emulator when cross compiling if required. cmValue emulator = target->GetProperty("CROSSCOMPILING_EMULATOR"); - if (cmNonempty(emulator)) { + if (cmNonempty(emulator) && useEmulator) { cmList emulatorWithArgs{ *emulator }; std::string emulatorExe(emulatorWithArgs[0]); cmSystemTools::ConvertToUnixSlashes(emulatorExe); diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx index 914172b..ffc4de9 100644 --- a/Source/cmTransformDepfile.cxx +++ b/Source/cmTransformDepfile.cxx @@ -16,6 +16,9 @@ #include "cmGccDepfileReaderTypes.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" namespace { @@ -121,6 +124,10 @@ bool cmTransformDepfile(cmDepfileFormat format, const cmLocalGenerator& lg, return false; } content = *std::move(result); + } else { + lg.GetMakefile()->IssueMessage( + MessageType::WARNING, + cmStrCat("Expected depfile does not exist.\n ", infile)); } cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outfile)); diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx index 34e6a70..168f1a6 100644 --- a/Source/cmUVHandlePtr.cxx +++ b/Source/cmUVHandlePtr.cxx @@ -6,6 +6,9 @@ #include <cassert> #include <cstdlib> #include <mutex> +#include <utility> + +#include <cm/memory> #include <cm3p/uv.h> @@ -44,7 +47,7 @@ void uv_loop_ptr::reset() this->loop.reset(); } -uv_loop_ptr::operator uv_loop_t*() +uv_loop_ptr::operator uv_loop_t*() const { return this->loop.get(); } @@ -54,6 +57,11 @@ uv_loop_t* uv_loop_ptr::operator->() const noexcept return this->loop.get(); } +uv_loop_t& uv_loop_ptr::operator*() const +{ + return *this->loop; +} + uv_loop_t* uv_loop_ptr::get() const { return this->loop.get(); @@ -97,13 +105,19 @@ void uv_handle_ptr_base_<T>::allocate(void* data) } template <typename T> +uv_handle_ptr_base_<T>::operator bool() const +{ + return this->handle.get(); +} + +template <typename T> void uv_handle_ptr_base_<T>::reset() { this->handle.reset(); } template <typename T> -uv_handle_ptr_base_<T>::operator uv_handle_t*() +uv_handle_ptr_base_<T>::operator uv_handle_t*() const { return reinterpret_cast<uv_handle_t*>(this->handle.get()); } @@ -235,6 +249,12 @@ int uv_timer_ptr::start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat) return uv_timer_start(*this, cb, timeout, repeat); } +void uv_timer_ptr::stop() +{ + assert(this->handle); + uv_timer_stop(*this); +} + #ifndef CMAKE_BOOTSTRAP uv_tty_ptr::operator uv_stream_t*() const { @@ -248,12 +268,32 @@ int uv_tty_ptr::init(uv_loop_t& loop, int fd, int readable, void* data) } #endif +int uv_idle_ptr::init(uv_loop_t& loop, void* data) +{ + this->allocate(data); + return uv_idle_init(&loop, *this); +} + +int uv_idle_ptr::start(uv_idle_cb cb) +{ + assert(this->handle); + return uv_idle_start(*this, cb); +} + +void uv_idle_ptr::stop() +{ + assert(this->handle); + uv_idle_stop(*this); +} + template class uv_handle_ptr_base_<uv_handle_t>; #define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME) \ template class uv_handle_ptr_base_<uv_##NAME##_t>; \ template class uv_handle_ptr_<uv_##NAME##_t>; +UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(idle) + UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(signal) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe) @@ -269,4 +309,38 @@ UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty) #endif + +namespace { +struct write_req : public uv_write_t +{ + std::weak_ptr<std::function<void(int)>> cb_; + write_req(std::weak_ptr<std::function<void(int)>> wcb) + : cb_(std::move(wcb)) + { + } +}; + +void write_req_cb(uv_write_t* req, int status) +{ + // Ownership has been transferred from the event loop. + std::unique_ptr<write_req> self(static_cast<write_req*>(req)); + + // Notify the original uv_write caller if it is still interested. + if (auto cb = self->cb_.lock()) { + (*cb)(status); + } +} +} + +int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, + std::weak_ptr<std::function<void(int)>> cb) +{ + auto req = cm::make_unique<write_req>(std::move(cb)); + int status = uv_write(req.get(), handle, bufs, nbufs, write_req_cb); + if (status == 0) { + // Ownership has been transferred to the event loop. + static_cast<void>(req.release()); + } + return status; +} } diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h index 027d690..b8b3491 100644 --- a/Source/cmUVHandlePtr.h +++ b/Source/cmUVHandlePtr.h @@ -5,6 +5,7 @@ #include <cstddef> #include <cstdint> +#include <functional> #include <memory> #include <type_traits> @@ -61,10 +62,11 @@ public: * Allow less verbose calling of uv_loop_* functions * @return reinterpreted handle */ - operator uv_loop_t*(); + operator uv_loop_t*() const; uv_loop_t* get() const; uv_loop_t* operator->() const noexcept; + uv_loop_t& operator*() const; }; /*** @@ -130,6 +132,16 @@ public: uv_handle_ptr_base_(std::nullptr_t) {} ~uv_handle_ptr_base_() { this->reset(); } +#if defined(__SUNPRO_CC) + // The Oracle Studio compiler recognizes 'explicit operator bool()' in + // 'if(foo)' but not 'if(foo && ...)'. The purpose of 'explicit' here + // is to avoid accidental conversion in non-boolean contexts. Just + // leave it out on this compiler so we can compile valid code. + operator bool() const; +#else + explicit operator bool() const; +#endif + /** * Properly close the handle if needed and sets the inner handle to nullptr */ @@ -139,7 +151,7 @@ public: * Allow less verbose calling of uv_handle_* functions * @return reinterpreted handle */ - operator uv_handle_t*(); + operator uv_handle_t*() const; T* get() const; T* operator->() const noexcept; @@ -194,6 +206,17 @@ public: void send(); }; +struct uv_idle_ptr : public uv_handle_ptr_<uv_idle_t> +{ + CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, <uv_idle_t>); + + int init(uv_loop_t& loop, void* data = nullptr); + + int start(uv_idle_cb cb); + + void stop(); +}; + struct uv_signal_ptr : public uv_handle_ptr_<uv_signal_t> { CM_INHERIT_CTOR(uv_signal_ptr, uv_handle_ptr_, <uv_signal_t>); @@ -229,6 +252,8 @@ struct uv_timer_ptr : public uv_handle_ptr_<uv_timer_t> int init(uv_loop_t& loop, void* data = nullptr); int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat); + + void stop(); }; struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t> @@ -253,6 +278,8 @@ extern template class uv_handle_ptr_base_<uv_handle_t>; UV_HANDLE_PTR_INSTANTIATE_EXTERN(async) +UV_HANDLE_PTR_INSTANTIATE_EXTERN(idle) + UV_HANDLE_PTR_INSTANTIATE_EXTERN(signal) UV_HANDLE_PTR_INSTANTIATE_EXTERN(pipe) @@ -268,4 +295,27 @@ UV_HANDLE_PTR_INSTANTIATE_EXTERN(tty) # undef UV_HANDLE_PTR_INSTANTIATE_EXTERN #endif + +/** + * Wraps uv_write to add synchronous cancellation. + * + * libuv provides no way to synchronously cancel a write request. + * Closing a write handle will cancel its pending write request, but its + * callback will still be called asynchronously later with UV_ECANCELED. + * + * This wrapper provides a solution by handing ownership of the uv_write_t + * request object to the event loop and taking it back in the callback. + * Use this in combination with uv_loop_ptr to ensure the event loop + * runs to completion and cleans up all resources. + * + * The caller may optionally provide a callback it owns with std::shared_ptr. + * If the caller's lifetime ends before the write request completes, the + * callback can be safely deleted and will not be called. + * + * The bufs array does not need to live beyond this call, but the memory + * referenced by the uv_buf_t values must remain alive until the callback + * is made or the stream is closed. + */ +int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, + std::weak_ptr<std::function<void(int)>> cb); } diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx index 655e52a..4c72261 100644 --- a/Source/cmUVProcessChain.cxx +++ b/Source/cmUVProcessChain.cxx @@ -38,7 +38,8 @@ struct cmUVProcessChain::InternalData bool Valid = false; - cm::uv_loop_ptr Loop; + cm::uv_loop_ptr BuiltinLoop; + uv_loop_t* Loop; StreamData InputStreamData; StreamData OutputStreamData; @@ -74,6 +75,19 @@ cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand( return *this; } +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinLoop() +{ + this->Loop = nullptr; + return *this; +} + +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalLoop( + uv_loop_t& loop) +{ + this->Loop = &loop; + return *this; +} + cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio) { switch (stdio) { @@ -135,6 +149,11 @@ cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetWorkingDirectory( return *this; } +uv_loop_t* cmUVProcessChainBuilder::GetLoop() const +{ + return this->Loop; +} + cmUVProcessChain cmUVProcessChainBuilder::Start() const { cmUVProcessChain chain; @@ -158,6 +177,13 @@ bool cmUVProcessChain::InternalData::Prepare( { this->Builder = builder; + if (this->Builder->Loop) { + this->Loop = this->Builder->Loop; + } else { + this->BuiltinLoop.init(); + this->Loop = this->BuiltinLoop; + } + auto const& input = this->Builder->Stdio[cmUVProcessChainBuilder::Stream_INPUT]; auto& inputData = this->InputStreamData; @@ -353,7 +379,6 @@ void cmUVProcessChain::InternalData::Finish() cmUVProcessChain::cmUVProcessChain() : Data(cm::make_unique<InternalData>()) { - this->Data->Loop.init(); } cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h index 0f37e7d..139588b 100644 --- a/Source/cmUVProcessChain.h +++ b/Source/cmUVProcessChain.h @@ -30,12 +30,16 @@ public: cmUVProcessChainBuilder& AddCommand( const std::vector<std::string>& arguments); + cmUVProcessChainBuilder& SetBuiltinLoop(); + cmUVProcessChainBuilder& SetExternalLoop(uv_loop_t& loop); cmUVProcessChainBuilder& SetNoStream(Stream stdio); cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio); cmUVProcessChainBuilder& SetMergedBuiltinStreams(); cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd); cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir); + uv_loop_t* GetLoop() const; + cmUVProcessChain Start() const; private: @@ -63,6 +67,7 @@ private: std::vector<ProcessConfiguration> Processes; std::string WorkingDirectory; bool MergedBuiltinStreams = false; + uv_loop_t* Loop = nullptr; }; class cmUVProcessChain diff --git a/Source/cmUVSignalHackRAII.h b/Source/cmUVSignalHackRAII.h deleted file mode 100644 index 60e4ca8..0000000 --- a/Source/cmUVSignalHackRAII.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once -#include "cmConfigure.h" // IWYU pragma: keep - -#include <cm3p/uv.h> - -#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \ - UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19 -# define CMAKE_UV_SIGNAL_HACK -# include "cmUVHandlePtr.h" -/* - libuv does not use SA_RESTART on its signal handler, but C++ streams - depend on it for reliable i/o operations. This RAII helper convinces - libuv to install its handler, and then revises the handler to add the - SA_RESTART flag. We use a distinct uv loop that never runs to avoid - ever really getting a callback. libuv may fill the hack loop's signal - pipe and then stop writing, but that won't break any real loops. - */ -class cmUVSignalHackRAII -{ - uv_loop_t HackLoop; - cm::uv_signal_ptr HackSignal; - static void HackCB(uv_signal_t*, int) {} - -public: - cmUVSignalHackRAII() - { - uv_loop_init(&this->HackLoop); - this->HackSignal.init(this->HackLoop); - this->HackSignal.start(HackCB, SIGCHLD); - struct sigaction hack_sa; - sigaction(SIGCHLD, nullptr, &hack_sa); - if (!(hack_sa.sa_flags & SA_RESTART)) { - hack_sa.sa_flags |= SA_RESTART; - sigaction(SIGCHLD, &hack_sa, nullptr); - } - } - ~cmUVSignalHackRAII() - { - this->HackSignal.stop(); - uv_loop_close(&this->HackLoop); - } -}; -#endif diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 4860d9a..1bbd934 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2996,6 +2996,16 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( e1.WritePlatformConfigTag( "IntDir", cond, R"($(Platform)\$(Configuration)\$(ProjectName)\)"); } else { + if (ttype == cmStateEnums::SHARED_LIBRARY || + ttype == cmStateEnums::MODULE_LIBRARY || + ttype == cmStateEnums::EXECUTABLE) { + auto linker = this->GeneratorTarget->GetLinkerTool(config); + if (!linker.empty()) { + ConvertToWindowsSlash(linker); + e1.WritePlatformConfigTag("LinkToolExe", cond, linker); + } + } + std::string intermediateDir = cmStrCat( this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/', config, '/'); diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 6188134..9dd2e6c 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -137,14 +137,18 @@ bool cmVisualStudioGeneratorOptions::IsManaged() const bool cmVisualStudioGeneratorOptions::UsingUnicode() const { // Look for a _UNICODE definition. - return std::any_of(this->Defines.begin(), this->Defines.end(), - [](std::string const& di) { return di == "_UNICODE"_s; }); + return std::any_of( + this->Defines.begin(), this->Defines.end(), [](std::string const& di) { + return di == "_UNICODE"_s || cmHasLiteralPrefix(di, "_UNICODE="); + }); } bool cmVisualStudioGeneratorOptions::UsingSBCS() const { // Look for a _SBCS definition. - return std::any_of(this->Defines.begin(), this->Defines.end(), - [](std::string const& di) { return di == "_SBCS"_s; }); + return std::any_of( + this->Defines.begin(), this->Defines.end(), [](std::string const& di) { + return di == "_SBCS"_s || cmHasLiteralPrefix(di, "_SBCS="); + }); } void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx index 27cdbba..dd8f459 100644 --- a/Source/cmWorkerPool.cxx +++ b/Source/cmWorkerPool.cxx @@ -18,7 +18,6 @@ #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmUVHandlePtr.h" -#include "cmUVSignalHackRAII.h" // IWYU pragma: keep /** * @brief libuv pipe buffer class @@ -516,9 +515,6 @@ public: static void UVSlotEnd(uv_async_t* handle); // -- UV loop -#ifdef CMAKE_UV_SIGNAL_HACK - std::unique_ptr<cmUVSignalHackRAII> UVHackRAII; -#endif std::unique_ptr<uv_loop_t> UVLoop; cm::uv_async_ptr UVRequestBegin; cm::uv_async_ptr UVRequestEnd; @@ -563,9 +559,6 @@ cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool) { // Initialize libuv loop uv_disable_stdio_inheritance(); -#ifdef CMAKE_UV_SIGNAL_HACK - UVHackRAII = cm::make_unique<cmUVSignalHackRAII>(); -#endif this->UVLoop = cm::make_unique<uv_loop_t>(); uv_loop_init(this->UVLoop.get()); } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index f54196b..32064b4 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1576,14 +1576,15 @@ void cmake::SetArgs(const std::vector<std::string>& args) if (!expandedPreset->ArchitectureStrategy || expandedPreset->ArchitectureStrategy == cmCMakePresetsGraph::ArchToolsetStrategy::Set) { - if (!this->GeneratorPlatformSet) { + if (!this->GeneratorPlatformSet && + !expandedPreset->Architecture.empty()) { this->SetGeneratorPlatform(expandedPreset->Architecture); } } if (!expandedPreset->ToolsetStrategy || expandedPreset->ToolsetStrategy == cmCMakePresetsGraph::ArchToolsetStrategy::Set) { - if (!this->GeneratorToolsetSet) { + if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) { this->SetGeneratorToolset(expandedPreset->Toolset); } } @@ -2395,8 +2396,15 @@ int cmake::ActualConfigure() cmSystemTools::RemoveADirectory(redirectsDir); if (!cmSystemTools::MakeDirectory(redirectsDir)) { cmSystemTools::Error( - "Unable to (re)create the private pkgRedirects directory:\n" + - redirectsDir); + cmStrCat("Unable to (re)create the private pkgRedirects directory:\n ", + redirectsDir, + "\n" + "This may be caused by not having read/write access to " + "the build directory.\n" + "Try specifying a location with read/write access like:\n" + " cmake -B build\n" + "If using a CMake presets file, ensure that preset parameter\n" + "'binaryDir' expands to a writable directory.\n")); return -1; } this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir, @@ -2508,6 +2516,16 @@ int cmake::ActualConfigure() "Name of generator toolset.", cmStateEnums::INTERNAL); } + if (!this->State->GetInitializedCacheValue("CMAKE_TEST_LAUNCHER")) { + cm::optional<std::string> testLauncher = + cmSystemTools::GetEnvVar("CMAKE_TEST_LAUNCHER"); + if (testLauncher && !testLauncher->empty()) { + std::string message = "Test launcher to run tests executable."; + this->AddCacheEntry("CMAKE_TEST_LAUNCHER", *testLauncher, message, + cmStateEnums::STRING); + } + } + if (!this->State->GetInitializedCacheValue( "CMAKE_CROSSCOMPILING_EMULATOR")) { cm::optional<std::string> emulator = diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 43a945f..93b0086 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -203,11 +203,16 @@ bool cmTarFilesFrom(std::string const& file, std::vector<std::string>& files) void cmCatFile(const std::string& fileToAppend) { #ifdef _WIN32 + _setmode(fileno(stdin), _O_BINARY); _setmode(fileno(stdout), _O_BINARY); #endif - cmsys::ifstream source(fileToAppend.c_str(), - (std::ios::binary | std::ios::in)); - std::cout << source.rdbuf(); + std::streambuf* buf = std::cin.rdbuf(); + cmsys::ifstream source; + if (fileToAppend != "-") { + source.open(fileToAppend.c_str(), (std::ios::binary | std::ios::in)); + buf = source.rdbuf(); + } + std::cout << buf; } bool cmRemoveDirectory(const std::string& dir, bool recursive = true) @@ -1147,7 +1152,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, int return_value = 0; bool doing_options = true; for (auto const& arg : cmMakeRange(args).advance(2)) { - if (doing_options && cmHasLiteralPrefix(arg, "-")) { + if (arg == "-") { + doing_options = false; + // Destroy console buffers to drop cout/cerr encoding transform. + consoleBuf.reset(); + cmCatFile(arg); + } else if (doing_options && cmHasLiteralPrefix(arg, "-")) { if (arg == "--") { doing_options = false; } else { diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index 2b7f2cc..562d5e6 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -298,14 +298,6 @@ endif() set(KWSYS_HEADER_INSTALL_DIR) set(KWSYS_LIBRARY_INSTALL_DIR) -# Generated source files will need this header. -string(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" - KWSYS_IN_SOURCE_BUILD) -if(NOT KWSYS_IN_SOURCE_BUILD) - configure_file(${PROJECT_SOURCE_DIR}/kwsysPrivate.h - ${PROJECT_BINARY_DIR}/kwsysPrivate.h COPYONLY IMMEDIATE) -endif() - # Select plugin module file name convention. if(NOT KWSYS_DynamicLoader_PREFIX) set(KWSYS_DynamicLoader_PREFIX ${CMAKE_SHARED_MODULE_PREFIX}) diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 3bb7869..53b55f6 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -412,18 +412,6 @@ inline void Realpath(const std::string& path, std::string& resolved_path, } #endif -#if !defined(_WIN32) && defined(__COMO__) -// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE. -extern "C" { -extern FILE* popen(__const char* __command, __const char* __modes) __THROW; -extern int pclose(FILE* __stream) __THROW; -extern char* realpath(__const char* __restrict __name, - char* __restrict __resolved) __THROW; -extern char* strdup(__const char* __s) __THROW; -extern int putenv(char* __string) __THROW; -} -#endif - namespace KWSYS_NAMESPACE { double SystemTools::GetTime() @@ -777,12 +765,16 @@ const char* SystemTools::GetEnv(const std::string& key) bool SystemTools::GetEnv(const char* key, std::string& result) { #if defined(_WIN32) - const std::wstring wkey = Encoding::ToWide(key); - const wchar_t* wv = _wgetenv(wkey.c_str()); - if (wv) { - result = Encoding::ToNarrow(wv); - return true; + auto wide_key = Encoding::ToWide(key); + auto result_size = GetEnvironmentVariableW(wide_key.data(), nullptr, 0); + if (result_size <= 0) { + return false; } + std::wstring wide_result; + wide_result.resize(result_size - 1); + GetEnvironmentVariableW(wide_key.data(), &wide_result[0], result_size); + result = Encoding::ToNarrow(wide_result); + return true; #else const char* v = getenv(key); if (v) { @@ -2802,14 +2794,14 @@ Status SystemTools::RemoveFile(std::string const& source) Status SystemTools::RemoveADirectory(std::string const& source) { - // Add write permission to the directory so we can modify its - // content to remove files and directories from it. + // Add read and write permission to the directory so we can read + // and modify its content to remove files and directories from it. mode_t mode = 0; if (SystemTools::GetPermissions(source, mode)) { #if defined(_WIN32) && !defined(__CYGWIN__) - mode |= S_IWRITE; + mode |= S_IREAD | S_IWRITE; #else - mode |= S_IWUSR; + mode |= S_IRUSR | S_IWUSR; #endif SystemTools::SetPermissions(source, mode); } diff --git a/Tests/Architecture/bar.c b/Tests/Architecture/bar.c index 37946c7..18ddb78 100644 --- a/Tests/Architecture/bar.c +++ b/Tests/Architecture/bar.c @@ -1,5 +1,5 @@ extern int foo(void); -int main() +int main(void) { return foo(); } diff --git a/Tests/BuildDepends/Project/link_depends_no_shared_exe.c b/Tests/BuildDepends/Project/link_depends_no_shared_exe.c index cfaf051..8830776 100644 --- a/Tests/BuildDepends/Project/link_depends_no_shared_exe.c +++ b/Tests/BuildDepends/Project/link_depends_no_shared_exe.c @@ -3,7 +3,7 @@ __declspec(dllimport) #endif int link_depends_no_shared_lib(void); -int main() +int main(void) { return link_depends_no_shared_lib() + link_depends_no_shared_exe_value; } diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.c b/Tests/CMakeCommands/target_compile_definitions/consumer.c index bb65e01..08554f4 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.c +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.c @@ -39,6 +39,6 @@ # error Expected LANG_IS_C_OR_CXX #endif -void consumer_c() +void consumer_c(void) { } diff --git a/Tests/CMakeCommands/target_compile_features/main.c b/Tests/CMakeCommands/target_compile_features/main.c index 76e98c4..60f0f9e 100644 --- a/Tests/CMakeCommands/target_compile_features/main.c +++ b/Tests/CMakeCommands/target_compile_features/main.c @@ -6,7 +6,7 @@ int foo(int* restrict a, int* restrict b) return 0; } -int main() +int main(void) { return 0; } diff --git a/Tests/CMakeCommands/target_compile_features/restrict_user.c b/Tests/CMakeCommands/target_compile_features/restrict_user.c index 76c956f..42e3efb 100644 --- a/Tests/CMakeCommands/target_compile_features/restrict_user.c +++ b/Tests/CMakeCommands/target_compile_features/restrict_user.c @@ -8,7 +8,7 @@ int bar(int* restrict a, int* restrict b) return foo(a, b); } -int main() +int main(void) { return 0; } diff --git a/Tests/CMakeCommands/target_compile_options/consumer.c b/Tests/CMakeCommands/target_compile_options/consumer.c index f9b6654..0984166 100644 --- a/Tests/CMakeCommands/target_compile_options/consumer.c +++ b/Tests/CMakeCommands/target_compile_options/consumer.c @@ -35,6 +35,6 @@ # endif #endif -void consumer_c() +void consumer_c(void) { } diff --git a/Tests/CMakeCommands/target_include_directories/consumer.c b/Tests/CMakeCommands/target_include_directories/consumer.c index 7fd694b..1975050 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.c +++ b/Tests/CMakeCommands/target_include_directories/consumer.c @@ -15,7 +15,7 @@ # endif #endif -int consumer_c() +int consumer_c(void) { return 0; } diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index b44c8dd..2c1d22b 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -25,6 +25,8 @@ set(CMakeLib_TESTS testXMLParser.cxx testXMLSafe.cxx testFindPackageCommand.cxx + testUVHandlePtr.cxx + testUVJobServerClient.cxx testUVProcessChain.cxx testUVRAII.cxx testUVStreambuf.cxx @@ -42,6 +44,7 @@ if(CMake_ENABLE_DEBUGGER) testDebuggerVariables.cxx testDebuggerVariablesHelper.cxx testDebuggerVariablesManager.cxx + testDebuggerThread.cxx ) endif() if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) diff --git a/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt b/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt index e47b9db..f3de7e1 100644 --- a/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt +++ b/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt @@ -1,7 +1,7 @@ # A dummy checker implementation that does not write the requested output file # so it triggers an error for every checker. -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in" "int main(){return 0;}\n") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in" "int main(void){return 0;}\n") configure_file( "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in" diff --git a/Tests/CMakeLib/testDebuggerThread.cxx b/Tests/CMakeLib/testDebuggerThread.cxx new file mode 100644 index 0000000..0ea95b6 --- /dev/null +++ b/Tests/CMakeLib/testDebuggerThread.cxx @@ -0,0 +1,33 @@ +#include <functional> +#include <memory> +#include <string> +#include <vector> + +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/types.h> + +#include "cmDebuggerThread.h" +#include "cmListFileCache.h" + +#include "testCommon.h" + +static bool testStackFrameFunctionName() +{ + auto thread = std::make_shared<cmDebugger::cmDebuggerThread>(0, "name"); + const auto* functionName = "function_name"; + auto arguments = std::vector<cmListFileArgument>{}; + cmListFileFunction func(functionName, 10, 20, arguments); + thread->PushStackFrame(nullptr, "CMakeLists.txt", func); + + auto stackTrace = GetStackTraceResponse(thread); + + ASSERT_TRUE(stackTrace.stackFrames[0].name == functionName); + return true; +} + +int testDebuggerThread(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testStackFrameFunctionName, + }); +} diff --git a/Tests/CMakeLib/testUVHandlePtr.cxx b/Tests/CMakeLib/testUVHandlePtr.cxx new file mode 100644 index 0000000..17f672d --- /dev/null +++ b/Tests/CMakeLib/testUVHandlePtr.cxx @@ -0,0 +1,153 @@ +#include <functional> +#include <iostream> +#include <memory> + +#include <cm3p/uv.h> + +#include "cmGetPipes.h" +#include "cmUVHandlePtr.h" + +static bool testBool() +{ + cm::uv_async_ptr async; + cm::uv_handle_ptr handle; + cm::uv_idle_ptr idle; + cm::uv_pipe_ptr pipe; + cm::uv_process_ptr process; + cm::uv_signal_ptr signal; + cm::uv_stream_ptr stream; + cm::uv_timer_ptr timer; + cm::uv_tty_ptr tty; + return !async && !handle && !idle && !pipe && !process && !signal && + !stream && !timer && !tty; +} + +static bool testIdle() +{ + bool idled = false; + + cm::uv_loop_ptr loop; + loop.init(); + + auto cb = [](uv_idle_t* handle) { + auto idledPtr = static_cast<bool*>(handle->data); + *idledPtr = true; + uv_idle_stop(handle); + }; + + cm::uv_idle_ptr idle; + idle.init(*loop, &idled); + idle.start(cb); + uv_run(loop, UV_RUN_DEFAULT); + + if (!idled) { + std::cerr << "uv_idle_ptr did not trigger callback" << std::endl; + return false; + } + + idled = false; + + idle.start(cb); + idle.stop(); + uv_run(loop, UV_RUN_DEFAULT); + + if (idled) { + std::cerr << "uv_idle_ptr::stop did not stop callback" << std::endl; + return false; + } + + return true; +} + +static bool testTimer() +{ + bool timed = false; + + cm::uv_loop_ptr loop; + loop.init(); + + auto cb = [](uv_timer_t* handle) { + auto timedPtr = static_cast<bool*>(handle->data); + *timedPtr = true; + uv_timer_stop(handle); + }; + + cm::uv_timer_ptr timer; + timer.init(*loop, &timed); + timer.start(cb, 10, 0); + uv_run(loop, UV_RUN_DEFAULT); + + if (!timed) { + std::cerr << "uv_timer_ptr did not trigger callback" << std::endl; + return false; + } + + timed = false; + timer.start(cb, 10, 0); + timer.stop(); + uv_run(loop, UV_RUN_DEFAULT); + + if (timed) { + std::cerr << "uv_timer_ptr::stop did not stop callback" << std::endl; + return false; + } + + return true; +} + +static bool testWriteCallback() +{ + int pipe[] = { -1, -1 }; + if (cmGetPipes(pipe) < 0) { + std::cout << "cmGetPipes() returned an error" << std::endl; + return false; + } + + cm::uv_loop_ptr loop; + loop.init(); + + cm::uv_pipe_ptr pipeRead; + pipeRead.init(*loop, 0); + uv_pipe_open(pipeRead, pipe[0]); + + cm::uv_pipe_ptr pipeWrite; + pipeWrite.init(*loop, 0); + uv_pipe_open(pipeWrite, pipe[1]); + + char c = '.'; + uv_buf_t buf = uv_buf_init(&c, sizeof(c)); + int status = -1; + auto cb = std::make_shared<std::function<void(int)>>( + [&status](int s) { status = s; }); + + // Test getting a callback after the write is done. + cm::uv_write(pipeWrite, &buf, 1, cb); + uv_run(loop, UV_RUN_DEFAULT); + if (status != 0) { + std::cout << "cm::uv_write non-zero status: " << status << std::endl; + return false; + } + + // Test deleting the callback before it is made. + status = -1; + cm::uv_write(pipeWrite, &buf, 1, cb); + cb.reset(); + uv_run(loop, UV_RUN_DEFAULT); + if (status != -1) { + std::cout << "cm::uv_write callback incorrectly called with status: " + << status << std::endl; + return false; + } + + return true; +} + +int testUVHandlePtr(int, char** const) +{ + bool passed = true; + passed = testBool() && passed; + passed = testIdle() && passed; + passed = testTimer() && passed; + passed = testWriteCallback() && passed; + return passed ? 0 : -1; +} diff --git a/Tests/CMakeLib/testUVJobServerClient.cxx b/Tests/CMakeLib/testUVJobServerClient.cxx new file mode 100644 index 0000000..13f0f40 --- /dev/null +++ b/Tests/CMakeLib/testUVJobServerClient.cxx @@ -0,0 +1,179 @@ +#include <cassert> +#include <cstddef> +#include <deque> +#include <iostream> +#include <vector> + +#include <cm/optional> + +#include <cm3p/uv.h> + +#ifndef _WIN32 +# include <unistd.h> +#endif + +#include "cmGetPipes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVJobServerClient.h" + +namespace { + +const std::size_t kTOTAL_JOBS = 10; +const std::size_t kTOTAL_TOKENS = 3; + +struct Job +{ + cm::uv_timer_ptr Timer; +}; + +struct JobRunner +{ + cm::uv_loop_ptr Loop; + cm::optional<cmUVJobServerClient> JSC; + std::vector<Job> Jobs; + std::size_t NextJobIndex = 0; + + std::size_t ActiveJobs = 0; + + std::deque<std::size_t> Queue; + + bool Okay = true; + + JobRunner() + : Jobs(kTOTAL_JOBS) + { + this->Loop.init(nullptr); + this->JSC = cmUVJobServerClient::Connect( + *this->Loop, [this]() { this->StartQueuedJob(); }, nullptr); + if (!this->JSC) { + std::cerr << "Failed to connect to job server.\n"; + this->Okay = false; + } + } + + ~JobRunner() {} + + bool Run() + { + if (this->Okay) { + this->QueueNextJobs(); + uv_run(this->Loop, UV_RUN_DEFAULT); + std::cerr << "HeldTokens: " << this->JSC->GetHeldTokens() << '\n'; + std::cerr << "NeedTokens: " << this->JSC->GetNeedTokens() << '\n'; + } +#ifdef _WIN32 + // FIXME: Windows job server client not yet implemented. + return true; +#else + return this->Okay; +#endif + } + + void QueueNextJobs() + { + std::cerr << "QueueNextJobs()\n"; + std::size_t queued = 0; + while (queued < 2 && this->NextJobIndex < this->Jobs.size()) { + this->QueueJob(this->NextJobIndex); + ++this->NextJobIndex; + ++queued; + } + std::cerr << "QueueNextJobs done\n"; + } + + void StartQueuedJob() + { + std::cerr << "StartQueuedJob()\n"; + assert(!this->Queue.empty()); + + std::size_t index = this->Queue.front(); + this->Queue.pop_front(); + this->StartJob(index); + + std::cerr << "StartQueuedJob done\n"; + } + + void StartJob(std::size_t index) + { + cm::uv_timer_ptr& job = this->Jobs[index].Timer; + job.init(*this->Loop, this); + uv_timer_start( + job, + [](uv_timer_t* handle) { + uv_timer_stop(handle); + auto self = static_cast<JobRunner*>(handle->data); + self->FinishJob(); + }, + /*timeout_ms=*/10 * (1 + (index % 3)), /*repeat_ms=*/0); + ++this->ActiveJobs; + std::cerr << " StartJob(" << index + << "): Active jobs: " << this->ActiveJobs << '\n'; + + if (this->ActiveJobs > kTOTAL_TOKENS) { + std::cerr << "Started more than " << kTOTAL_TOKENS << " jobs at once!\n"; + this->Okay = false; + return; + } + } + + void QueueJob(std::size_t index) + { + this->JSC->RequestToken(); + this->Queue.push_back(index); + std::cerr << " QueueJob(" << index + << "): Queue length: " << this->Queue.size() << '\n'; + } + + void FinishJob() + { + --this->ActiveJobs; + std::cerr << "FinishJob: Active jobs: " << this->ActiveJobs << '\n'; + + this->JSC->ReleaseToken(); + this->QueueNextJobs(); + } +}; + +bool testJobServer() +{ +#ifdef _WIN32 + // FIXME: Windows job server client not yet implemented. +#else + // Create a job server pipe. + int jobServerPipe[2]; + if (cmGetPipes(jobServerPipe) < 0) { + std::cerr << "Failed to create job server pipe\n"; + return false; + } + + // Write N-1 tokens to the pipe. + std::vector<char> jobServerInit(kTOTAL_TOKENS - 1, '.'); + if (write(jobServerPipe[1], jobServerInit.data(), jobServerInit.size()) != + kTOTAL_TOKENS - 1) { + std::cerr << "Failed to initialize job server pipe\n"; + return false; + } + + // Establish the job server client context. + // Add a bogus server spec to verify we use the last spec. + cmSystemTools::PutEnv(cmStrCat("MAKEFLAGS=--flags-before" + " --jobserver-auth=bogus" + " --flags-between" + " --jobserver-fds=", + jobServerPipe[0], ',', jobServerPipe[1], + " --flags-after")); +#endif + + JobRunner jobRunner; + return jobRunner.Run(); +} +} + +int testUVJobServerClient(int, char** const) +{ + bool passed = true; + passed = testJobServer() && passed; + return passed ? 0 : -1; +} diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx index aab084b..6014889 100644 --- a/Tests/CMakeLib/testUVProcessChain.cxx +++ b/Tests/CMakeLib/testUVProcessChain.cxx @@ -297,7 +297,12 @@ bool testUVProcessChainBuiltin(const char* helperCommand) .AddCommand({ helperCommand, "capitalize" }) .AddCommand({ helperCommand, "dedup" }) .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) - .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR) + .SetBuiltinLoop(); + if (builder.GetLoop()) { + std::cout << "GetLoop() should return null" << std::endl; + return false; + } if (!checkExecution(builder, chain)) { return false; @@ -400,6 +405,10 @@ bool testUVProcessChainExternal(const char* helperCommand) .AddCommand({ helperCommand, "dedup" }) .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1]) .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]); + if (builder.GetLoop()) { + std::cout << "GetLoop() should return null" << std::endl; + return false; + } if (!checkExecution(builder, chain)) { return false; @@ -668,6 +677,43 @@ bool testUVProcessChainWait0(const char* helperCommand) return true; } +bool testUVProcessChainExternalLoop(const char* helperCommand) +{ + cm::uv_loop_ptr loop; + loop.init(); + + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "echo" }) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetExternalLoop(*loop); + if (builder.GetLoop() != loop) { + std::cout << "GetLoop() should return external loop" << std::endl; + return false; + } + + auto chain = builder.Start(); + + if (&chain.GetLoop() != loop) { + std::cout << "GetLoop() should return external loop" << std::endl; + return false; + } + + if (!chain.Wait()) { + std::cout << "Wait() timed out" << std::endl; + return false; + } + + cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream()); + std::string output = getInput(stream); + if (output != "HELLO world!") { + std::cout << "Output was \"" << output << "\", expected \"HELLO world!\"" + << std::endl; + return false; + } + + return true; +} + int testUVProcessChain(int argc, char** const argv) { if (argc < 2) { @@ -720,5 +766,10 @@ int testUVProcessChain(int argc, char** const argv) return -1; } + if (!testUVProcessChainExternalLoop(argv[1])) { + std::cout << "While executing testUVProcessChainExternalLoop().\n"; + return -1; + } + return 0; } diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx index 0bdd44c..9e79d5c 100644 --- a/Tests/CMakeLib/testUVRAII.cxx +++ b/Tests/CMakeLib/testUVRAII.cxx @@ -37,7 +37,7 @@ static bool testAsyncShutdown() return false; } - if (signal.get()) { + if (signal) { std::cerr << "Loop exited with signal not being cleaned up" << std::endl; return false; } @@ -125,13 +125,13 @@ static bool testCrossAssignment() pipe.init(Loop, 0); cm::uv_stream_ptr stream = std::move(pipe); - if (pipe.get()) { + if (pipe) { std::cerr << "Move should be sure to invalidate the previous ptr" << std::endl; return false; } cm::uv_handle_ptr handle = std::move(stream); - if (stream.get()) { + if (stream) { std::cerr << "Move should be sure to invalidate the previous ptr" << std::endl; return false; @@ -162,6 +162,7 @@ static bool testAllMoves() uv_async_ptr _13; uv_signal_ptr _14; uv_handle_ptr _15; + uv_idle_ptr _16; }; allTypes a; diff --git a/Tests/COnly/conly.c b/Tests/COnly/conly.c index 2ae8a1a..c5f18ad 100644 --- a/Tests/COnly/conly.c +++ b/Tests/COnly/conly.c @@ -4,7 +4,7 @@ #include "libc1.h" #include "libc2.h" -int main() +int main(void) { int class = 0; if (LibC1Func() != 2.0) { diff --git a/Tests/COnly/libc1.c b/Tests/COnly/libc1.c index b01e1e1..e1f7e27 100644 --- a/Tests/COnly/libc1.c +++ b/Tests/COnly/libc1.c @@ -1,4 +1,4 @@ -float LibC1Func() +float LibC1Func(void) { return 2.0; } diff --git a/Tests/COnly/libc2.c b/Tests/COnly/libc2.c index 0fd8956..28d46f3 100644 --- a/Tests/COnly/libc2.c +++ b/Tests/COnly/libc2.c @@ -1,6 +1,6 @@ #include "libc2.h" -float LibC2Func() +float LibC2Func(void) { return 1.0; } diff --git a/Tests/CPackInnoSetupGenerator/main.c b/Tests/CPackInnoSetupGenerator/main.c index 413899c..9165c97 100644 --- a/Tests/CPackInnoSetupGenerator/main.c +++ b/Tests/CPackInnoSetupGenerator/main.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { printf("Hello, World!\n"); return 42; diff --git a/Tests/CTestTestVerboseOutput/nop.c b/Tests/CTestTestVerboseOutput/nop.c index f8b643a..8488f4e 100644 --- a/Tests/CTestTestVerboseOutput/nop.c +++ b/Tests/CTestTestVerboseOutput/nop.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/CommandLength/test.c b/Tests/CommandLength/test.c index f8b643a..8488f4e 100644 --- a/Tests/CommandLength/test.c +++ b/Tests/CommandLength/test.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/CompileDefinitions/runtest.c b/Tests/CompileDefinitions/runtest.c index c6dac4d..37317b8 100644 --- a/Tests/CompileDefinitions/runtest.c +++ b/Tests/CompileDefinitions/runtest.c @@ -6,7 +6,7 @@ # error "BUILD_CONFIG_NAME not defined!" #endif -int main() +int main(void) { char build_config_name[] = BUILD_CONFIG_NAME; char* c; diff --git a/Tests/CompileFeatures/c_variadic_macros.c b/Tests/CompileFeatures/c_variadic_macros.c index 4da111e..7a21902 100644 --- a/Tests/CompileFeatures/c_variadic_macros.c +++ b/Tests/CompileFeatures/c_variadic_macros.c @@ -9,7 +9,7 @@ int someFunc(int i1, char c, int i2) #define FUNC_WRAPPER(...) someFunc(__VA_ARGS__) -void otherFunc() +void otherFunc(void) { FUNC_WRAPPER(42, 'a', 7); } diff --git a/Tests/CompileFeatures/default_dialect.c b/Tests/CompileFeatures/default_dialect.c index c696c83..cae107b 100644 --- a/Tests/CompileFeatures/default_dialect.c +++ b/Tests/CompileFeatures/default_dialect.c @@ -26,7 +26,7 @@ # endif #endif -int main() +int main(void) { return 0; } diff --git a/Tests/CompileFeatures/genex_test.c b/Tests/CompileFeatures/genex_test.c index de408ce..8ccad35 100644 --- a/Tests/CompileFeatures/genex_test.c +++ b/Tests/CompileFeatures/genex_test.c @@ -38,6 +38,6 @@ # endif #endif -int main() +int main(void) { } diff --git a/Tests/Complex/Executable/Sub1/NameConflictTest.c b/Tests/Complex/Executable/Sub1/NameConflictTest.c index 8720386..740c236 100644 --- a/Tests/Complex/Executable/Sub1/NameConflictTest.c +++ b/Tests/Complex/Executable/Sub1/NameConflictTest.c @@ -1,4 +1,4 @@ -int NameConflictTest1() +int NameConflictTest1(void) { return 0; } diff --git a/Tests/Complex/Executable/Sub2/NameConflictTest.c b/Tests/Complex/Executable/Sub2/NameConflictTest.c index 4a32572..cee9f6f 100644 --- a/Tests/Complex/Executable/Sub2/NameConflictTest.c +++ b/Tests/Complex/Executable/Sub2/NameConflictTest.c @@ -1,4 +1,4 @@ -int NameConflictTest2() +int NameConflictTest2(void) { return 0; } diff --git a/Tests/Complex/Library/TestLink.c b/Tests/Complex/Library/TestLink.c index 25dee08..f4bc255 100644 --- a/Tests/Complex/Library/TestLink.c +++ b/Tests/Complex/Library/TestLink.c @@ -1,4 +1,4 @@ -int TestLinkGetType() +int TestLinkGetType(void) { #ifdef CMakeTestLinkShared_EXPORTS return 0; diff --git a/Tests/Complex/Library/testConly.c b/Tests/Complex/Library/testConly.c index eb933a2..05ecc18 100644 --- a/Tests/Complex/Library/testConly.c +++ b/Tests/Complex/Library/testConly.c @@ -2,7 +2,7 @@ #include <stdio.h> -int CsharedFunction() +int CsharedFunction(void) { #ifndef TEST_C_FLAGS printf("TEST_C_FLAGS failed\n"); diff --git a/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c b/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c index 8720386..740c236 100644 --- a/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c +++ b/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c @@ -1,4 +1,4 @@ -int NameConflictTest1() +int NameConflictTest1(void) { return 0; } diff --git a/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c b/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c index 4a32572..cee9f6f 100644 --- a/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c +++ b/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c @@ -1,4 +1,4 @@ -int NameConflictTest2() +int NameConflictTest2(void) { return 0; } diff --git a/Tests/ComplexOneConfig/Library/TestLink.c b/Tests/ComplexOneConfig/Library/TestLink.c index 25dee08..f4bc255 100644 --- a/Tests/ComplexOneConfig/Library/TestLink.c +++ b/Tests/ComplexOneConfig/Library/TestLink.c @@ -1,4 +1,4 @@ -int TestLinkGetType() +int TestLinkGetType(void) { #ifdef CMakeTestLinkShared_EXPORTS return 0; diff --git a/Tests/ComplexOneConfig/Library/testConly.c b/Tests/ComplexOneConfig/Library/testConly.c index eb933a2..05ecc18 100644 --- a/Tests/ComplexOneConfig/Library/testConly.c +++ b/Tests/ComplexOneConfig/Library/testConly.c @@ -2,7 +2,7 @@ #include <stdio.h> -int CsharedFunction() +int CsharedFunction(void) { #ifndef TEST_C_FLAGS printf("TEST_C_FLAGS failed\n"); diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt index 38475f8..770afb3 100644 --- a/Tests/ConfigSources/CMakeLists.txt +++ b/Tests/ConfigSources/CMakeLists.txt @@ -154,7 +154,15 @@ else() endif() add_library(OneConfigOnly OBJECT "$<$<CONFIG:${one_config}>:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>") set_property(TARGET OneConfigOnly PROPERTY LINKER_LANGUAGE CXX) +add_executable(ConfigSourcesUseOne main_one_config.cpp) +target_compile_definitions(ConfigSourcesUseOne PRIVATE "$<$<CONFIG:${one_config}>:CFG_ONE>") +target_link_libraries(ConfigSourcesUseOne PRIVATE "$<$<CONFIG:${one_config}>:OneConfigOnly>") +add_library(OneConfigOnlyIface INTERFACE) +target_sources(OneConfigOnlyIface INTERFACE "$<$<CONFIG:${one_config}>:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>") +add_executable(ConfigSourcesUseOneIface main_one_config.cpp) +target_compile_definitions(ConfigSourcesUseOneIface PRIVATE "$<$<CONFIG:${one_config}>:CFG_ONE>") +target_link_libraries(ConfigSourcesUseOneIface PRIVATE "$<$<CONFIG:${one_config}>:OneConfigOnlyIface>") # --------------------------------------------------------------------------- # Makes sure that each configuration uses the correct generated file. diff --git a/Tests/ConfigSources/main_one_config.cpp b/Tests/ConfigSources/main_one_config.cpp new file mode 100644 index 0000000..318944b --- /dev/null +++ b/Tests/ConfigSources/main_one_config.cpp @@ -0,0 +1,8 @@ +#include "iface.h" +int main() +{ +#ifdef CFG_ONE + iface_src(); +#endif + return 0; +} diff --git a/Tests/Cuda/WithC/main.c b/Tests/Cuda/WithC/main.c index 5f3c781..ed64427 100644 --- a/Tests/Cuda/WithC/main.c +++ b/Tests/Cuda/WithC/main.c @@ -4,7 +4,7 @@ extern int use_cuda(void); # include <windows.h> #endif -int main() +int main(void) { #ifdef _WIN32 /* Use an API that requires CMake's "standard" C libraries. */ diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt index 82366df..9f49498 100644 --- a/Tests/CudaOnly/CMakeLists.txt +++ b/Tests/CudaOnly/CMakeLists.txt @@ -16,6 +16,7 @@ set(CudaOnly.Toolkit_BUILD_OPTIONS -DHAS_CUPTI:BOOL=${CMake_TEST_CUDA_CUPTI}) add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit) add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang) add_cuda_test_macro(CudaOnly.ToolkitMultipleDirs CudaOnlyToolkitMultipleDirs) +add_cuda_test_macro(CudaOnly.TryCompileTargetStatic CudaOnlyTryCompileTargetStatic) add_cuda_test_macro(CudaOnly.WithDefs CudaOnlyWithDefs) add_cuda_test_macro(CudaOnly.CircularLinkLine CudaOnlyCircularLinkLine) add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols) diff --git a/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt b/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt new file mode 100644 index 0000000..340c1c9 --- /dev/null +++ b/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.25) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +project(TryCompileTargetStatic LANGUAGES CUDA) + +add_executable(CudaOnlyTryCompileTargetStatic main.cu) diff --git a/Tests/CudaOnly/TryCompileTargetStatic/main.cu b/Tests/CudaOnly/TryCompileTargetStatic/main.cu new file mode 100644 index 0000000..766b775 --- /dev/null +++ b/Tests/CudaOnly/TryCompileTargetStatic/main.cu @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/Tests/CustomCommand/gen_redirect_in.c b/Tests/CustomCommand/gen_redirect_in.c index 6af364b..c71123a 100644 --- a/Tests/CustomCommand/gen_redirect_in.c +++ b/Tests/CustomCommand/gen_redirect_in.c @@ -1,6 +1,6 @@ #if 1 -int gen_redirect() +int gen_redirect(void) { return 3; } diff --git a/Tests/CustomCommandWorkingDirectory/customTarget.c b/Tests/CustomCommandWorkingDirectory/customTarget.c index 8dbf0d4..9735287 100644 --- a/Tests/CustomCommandWorkingDirectory/customTarget.c +++ b/Tests/CustomCommandWorkingDirectory/customTarget.c @@ -1,4 +1,4 @@ -int customTarget() +int customTarget(void) { return 0; } diff --git a/Tests/Dependency/1/OneSrc.c b/Tests/Dependency/1/OneSrc.c index 9801c25..7f527f4 100644 --- a/Tests/Dependency/1/OneSrc.c +++ b/Tests/Dependency/1/OneSrc.c @@ -1,3 +1,3 @@ -void OneFunction() +void OneFunction(void) { } diff --git a/Tests/Dependency/Case1/a.c b/Tests/Dependency/Case1/a.c index 262f523..bf1c773 100644 --- a/Tests/Dependency/Case1/a.c +++ b/Tests/Dependency/Case1/a.c @@ -1,4 +1,4 @@ -int a() +int a(void) { return 5; } diff --git a/Tests/Dependency/Case1/b.c b/Tests/Dependency/Case1/b.c index deda685..f4ef707 100644 --- a/Tests/Dependency/Case1/b.c +++ b/Tests/Dependency/Case1/b.c @@ -1,6 +1,6 @@ -extern int a(); +extern int a(void); -int b() +int b(void) { return a() + 17; } diff --git a/Tests/Dependency/Case1/b2.c b/Tests/Dependency/Case1/b2.c index f341da7..108e76a 100644 --- a/Tests/Dependency/Case1/b2.c +++ b/Tests/Dependency/Case1/b2.c @@ -1,4 +1,4 @@ -int b2() +int b2(void) { return 3; } diff --git a/Tests/Dependency/Case1/c.c b/Tests/Dependency/Case1/c.c index a3ec162..ddda855 100644 --- a/Tests/Dependency/Case1/c.c +++ b/Tests/Dependency/Case1/c.c @@ -1,6 +1,6 @@ -extern int b(); +extern int b(void); -int c() +int c(void) { return b() + 42; } diff --git a/Tests/Dependency/Case1/c2.c b/Tests/Dependency/Case1/c2.c index 317bb0f..826c746 100644 --- a/Tests/Dependency/Case1/c2.c +++ b/Tests/Dependency/Case1/c2.c @@ -1,6 +1,6 @@ -extern int b2(); +extern int b2(void); -int c2() +int c2(void) { return b2() + 1; } diff --git a/Tests/Dependency/Case1/d.c b/Tests/Dependency/Case1/d.c index f67aef7..fdeb2bf 100644 --- a/Tests/Dependency/Case1/d.c +++ b/Tests/Dependency/Case1/d.c @@ -1,6 +1,6 @@ -extern int c2(); +extern int c2(void); -int d() +int d(void) { return c2() + 2; } diff --git a/Tests/Dependency/Case1/main.c b/Tests/Dependency/Case1/main.c index 07191cc..6847f7d 100644 --- a/Tests/Dependency/Case1/main.c +++ b/Tests/Dependency/Case1/main.c @@ -2,7 +2,7 @@ extern int b(); extern int c(); extern int d(); -int main() +int main(void) { c(); b(); diff --git a/Tests/Dependency/Case4/bar.c b/Tests/Dependency/Case4/bar.c index 08092f9..18ddb78 100644 --- a/Tests/Dependency/Case4/bar.c +++ b/Tests/Dependency/Case4/bar.c @@ -1,5 +1,5 @@ -extern int foo(); -int main() +extern int foo(void); +int main(void) { return foo(); } diff --git a/Tests/Dependency/Case4/foo.c b/Tests/Dependency/Case4/foo.c index e05eb7e..c83d856 100644 --- a/Tests/Dependency/Case4/foo.c +++ b/Tests/Dependency/Case4/foo.c @@ -1,4 +1,4 @@ -int foo() +int foo(void) { return 0; } diff --git a/Tests/Dependency/Eight/EightSrc.c b/Tests/Dependency/Eight/EightSrc.c index 7bfa481..16605ce 100644 --- a/Tests/Dependency/Eight/EightSrc.c +++ b/Tests/Dependency/Eight/EightSrc.c @@ -1,6 +1,6 @@ -void SevenFunction(); +void SevenFunction(void); -void EightFunction() +void EightFunction(void) { SevenFunction(); } diff --git a/Tests/Dependency/Exec/ExecMain.c b/Tests/Dependency/Exec/ExecMain.c index 9572afd..793bc18 100644 --- a/Tests/Dependency/Exec/ExecMain.c +++ b/Tests/Dependency/Exec/ExecMain.c @@ -5,7 +5,7 @@ void NoDepCFunction(); void SixAFunction(); void SixBFunction(); -int main() +int main(void) { SixAFunction(); SixBFunction(); diff --git a/Tests/Dependency/Exec2/ExecMain.c b/Tests/Dependency/Exec2/ExecMain.c index 385cce1..085f30f 100644 --- a/Tests/Dependency/Exec2/ExecMain.c +++ b/Tests/Dependency/Exec2/ExecMain.c @@ -3,7 +3,7 @@ void FiveFunction(); void EightFunction(); -int main() +int main(void) { FiveFunction(); EightFunction(); diff --git a/Tests/Dependency/Exec3/ExecMain.c b/Tests/Dependency/Exec3/ExecMain.c index 385cce1..085f30f 100644 --- a/Tests/Dependency/Exec3/ExecMain.c +++ b/Tests/Dependency/Exec3/ExecMain.c @@ -3,7 +3,7 @@ void FiveFunction(); void EightFunction(); -int main() +int main(void) { FiveFunction(); EightFunction(); diff --git a/Tests/Dependency/Exec4/ExecMain.c b/Tests/Dependency/Exec4/ExecMain.c index 0cfcce9..48552d3 100644 --- a/Tests/Dependency/Exec4/ExecMain.c +++ b/Tests/Dependency/Exec4/ExecMain.c @@ -3,7 +3,7 @@ void FiveFunction(); void TwoFunction(); -int main() +int main(void) { FiveFunction(); TwoFunction(); diff --git a/Tests/Dependency/Five/FiveSrc.c b/Tests/Dependency/Five/FiveSrc.c index 33d8ad7..b35b05b 100644 --- a/Tests/Dependency/Five/FiveSrc.c +++ b/Tests/Dependency/Five/FiveSrc.c @@ -1,6 +1,6 @@ -void TwoFunction(); +void TwoFunction(void); -void FiveFunction() +void FiveFunction(void) { TwoFunction(); } diff --git a/Tests/Dependency/Four/FourSrc.c b/Tests/Dependency/Four/FourSrc.c index 4ea996d..b91c5fd 100644 --- a/Tests/Dependency/Four/FourSrc.c +++ b/Tests/Dependency/Four/FourSrc.c @@ -1,9 +1,9 @@ #include <two-test.h> /* Requires TwoCustom to be built first. */ -void NoDepAFunction(); -void OneFunction(); -void TwoFunction(); +void NoDepAFunction(void); +void OneFunction(void); +void TwoFunction(void); -void FourFunction() +void FourFunction(void) { static int count = 0; if (count == 0) { diff --git a/Tests/Dependency/NoDepA/NoDepASrc.c b/Tests/Dependency/NoDepA/NoDepASrc.c index 8c4072b..e972df2 100644 --- a/Tests/Dependency/NoDepA/NoDepASrc.c +++ b/Tests/Dependency/NoDepA/NoDepASrc.c @@ -1,3 +1,3 @@ -void NoDepAFunction() +void NoDepAFunction(void) { } diff --git a/Tests/Dependency/NoDepB/NoDepBSrc.c b/Tests/Dependency/NoDepB/NoDepBSrc.c index ddc71c5..81dc5ed 100644 --- a/Tests/Dependency/NoDepB/NoDepBSrc.c +++ b/Tests/Dependency/NoDepB/NoDepBSrc.c @@ -1,6 +1,6 @@ -void NoDepAFunction(); +void NoDepAFunction(void); -void NoDepBFunction() +void NoDepBFunction(void) { NoDepAFunction(); } diff --git a/Tests/Dependency/NoDepC/NoDepCSrc.c b/Tests/Dependency/NoDepC/NoDepCSrc.c index b478c59..d90007e 100644 --- a/Tests/Dependency/NoDepC/NoDepCSrc.c +++ b/Tests/Dependency/NoDepC/NoDepCSrc.c @@ -1,6 +1,6 @@ -void NoDepAFunction(); +void NoDepAFunction(void); -void NoDepCFunction() +void NoDepCFunction(void) { NoDepAFunction(); } diff --git a/Tests/Dependency/Seven/SevenSrc.c b/Tests/Dependency/Seven/SevenSrc.c index e1f3329..9c74ec8 100644 --- a/Tests/Dependency/Seven/SevenSrc.c +++ b/Tests/Dependency/Seven/SevenSrc.c @@ -1,6 +1,6 @@ -void TwoFunction(); +void TwoFunction(void); -void SevenFunction() +void SevenFunction(void) { TwoFunction(); } diff --git a/Tests/Dependency/Six/SixASrc.c b/Tests/Dependency/Six/SixASrc.c index 7ea3711..ddd2d7d 100644 --- a/Tests/Dependency/Six/SixASrc.c +++ b/Tests/Dependency/Six/SixASrc.c @@ -1,7 +1,7 @@ -void FiveFunction(); -void TwoFunction(); +void FiveFunction(void); +void TwoFunction(void); -void SixAFunction() +void SixAFunction(void) { FiveFunction(); TwoFunction(); diff --git a/Tests/Dependency/Six/SixBSrc.c b/Tests/Dependency/Six/SixBSrc.c index 92f9607..42f2de6 100644 --- a/Tests/Dependency/Six/SixBSrc.c +++ b/Tests/Dependency/Six/SixBSrc.c @@ -1,8 +1,8 @@ -void TwoFunction(); -void FiveFunction(); -void FourFunction(); +void TwoFunction(void); +void FiveFunction(void); +void FourFunction(void); -void SixBFunction() +void SixBFunction(void) { TwoFunction(); FiveFunction(); diff --git a/Tests/Dependency/Three/ThreeSrc.c b/Tests/Dependency/Three/ThreeSrc.c index 3e814f3..85c51fc 100644 --- a/Tests/Dependency/Three/ThreeSrc.c +++ b/Tests/Dependency/Three/ThreeSrc.c @@ -1,7 +1,7 @@ -void OneFunction(); -void FourFunction(); +void OneFunction(void); +void FourFunction(void); -void ThreeFunction() +void ThreeFunction(void) { static int count = 0; if (count == 0) { diff --git a/Tests/Dependency/Two/TwoCustomSrc.c b/Tests/Dependency/Two/TwoCustomSrc.c index ac31dcf..432dca1 100644 --- a/Tests/Dependency/Two/TwoCustomSrc.c +++ b/Tests/Dependency/Two/TwoCustomSrc.c @@ -1,10 +1,10 @@ -extern void NoFunction(); +extern void NoFunction(void); /* Provide a function that is supposed to be found in the Three library. If Two links to TwoCustom then TwoCustom will come before Three and this symbol will be used. Since NoFunction is not defined, that will cause a link failure. */ -void ThreeFunction() +void ThreeFunction(void) { NoFunction(); } diff --git a/Tests/Dependency/Two/TwoSrc.c b/Tests/Dependency/Two/TwoSrc.c index dbdf524..dadac22 100644 --- a/Tests/Dependency/Two/TwoSrc.c +++ b/Tests/Dependency/Two/TwoSrc.c @@ -1,6 +1,6 @@ #include <two-test.h> -void TwoFunction() +void TwoFunction(void) { static int count = 0; if (count == 0) { diff --git a/Tests/DoubleProject/silly.c b/Tests/DoubleProject/silly.c index f8b643a..8488f4e 100644 --- a/Tests/DoubleProject/silly.c +++ b/Tests/DoubleProject/silly.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 67f2fcb..56b8aaa 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -4,6 +4,8 @@ if(POLICY CMP0129) endif() project(Export C CXX) +find_package(Foo REQUIRED CONFIG NO_DEFAULT_PATH) + # Pretend that RelWithDebInfo should link to debug libraries to test # the DEBUG_CONFIGURATIONS property. set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug RelWithDebInfo) @@ -110,6 +112,7 @@ add_library(testLib9ObjIface OBJECT testLib9ObjIface.c) target_compile_definitions(testLib9ObjIface INTERFACE testLib9ObjIface_USED) add_library(testLib9 STATIC testLib9.c) target_link_libraries(testLib9 INTERFACE testLib9ObjIface PUBLIC testLib9ObjPub PRIVATE testLib9ObjPriv) +target_link_libraries(testLib9 PUBLIC Foo::Foo) cmake_policy(POP) # Test using the target_link_libraries command to set the @@ -624,7 +627,7 @@ install( LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) -install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp) +install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp EXPORT_PACKAGE_DEPENDENCIES) # Install testLib5.dll outside the export. if(WIN32) diff --git a/Tests/ExportImport/Export/testExe2.c b/Tests/ExportImport/Export/testExe2.c index 958e4d2..8e6ee44 100644 --- a/Tests/ExportImport/Export/testExe2.c +++ b/Tests/ExportImport/Export/testExe2.c @@ -9,7 +9,7 @@ testExe2_EXPORT int testExe2Func(void) return 123; } -int main() +int main(void) { return 0; } diff --git a/Tests/ExportImport/External/FooConfig.cmake b/Tests/ExportImport/External/FooConfig.cmake new file mode 100644 index 0000000..48b6289 --- /dev/null +++ b/Tests/ExportImport/External/FooConfig.cmake @@ -0,0 +1,3 @@ +if(NOT TARGET Foo::Foo) + add_library(Foo::Foo INTERFACE IMPORTED) +endif() diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 2c5662d..2a57633 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -500,7 +500,7 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA OUTPUT_VARIABLE OUTPUT ) if(NOT BLD_ERROR_VARIABLE) - message(SEND_ERROR "BLD_ERROR_VARIABLE try_compile failed, but it was expected to succeed.") + message(SEND_ERROR "BLD_ERROR_VARIABLE try_compile failed, but it was expected to succeed. ${OUTPUT}") endif() if(NOT CMAKE_CROSSCOMPILING) @@ -518,6 +518,91 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA endif() endif() endif() + + # Testing try_compile with ALIAS targets. + # These assume that previous test were successful, or at least the failures will be at the linking stage + # with symbol not found errors + + # First make sure that if the test run without appropriate alias targets, they should error out + try_compile(FAILING_LIBRARY_ALIAS_ERROR_VARIABLE + "${CMAKE_CURRENT_BINARY_DIR}/test_failing_library_alias" + "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" + LINK_LIBRARIES not_existing_library + OUTPUT_VARIABLE OUTPUT + NO_CACHE + ) + if(FAILING_LIBRARY_ALIAS_ERROR_VARIABLE) + message(SEND_ERROR "FAILING_LIBRARY_ALIAS_ERROR_VARIABLE try_compile succeeded, but it was expected to fail ${OUTPUT}.") + endif() + + # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable +# set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +# try_compile(FAILING_EXE_ALIAS_ERROR_VARIABLE +# "${CMAKE_CURRENT_BINARY_DIR}/test_failing_exe_alias" +# "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c" +# LINK_LIBRARIES not_existing_executable +# OUTPUT_VARIABLE OUTPUT +# NO_CACHE +# ) +# unset(CMAKE_TRY_COMPILE_TARGET_TYPE) +# if(FAILING_EXE_ALIAS_ERROR_VARIABLE) +# message(SEND_ERROR "FAILING_EXE_ALIAS_ERROR_VARIABLE try_compile succeeded, but it was expected to fail ${OUTPUT}.") +# endif() + + # Do the actual try_compile tests for ALIAS targets + add_library(exp_systemlib_alias ALIAS exp_systemlib) + try_compile(EXP_LIBRARY_ALIAS_ERROR_VARIABLE + "${CMAKE_CURRENT_BINARY_DIR}/test_library_alias" + "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" + LINK_LIBRARIES exp_systemlib_alias + OUTPUT_VARIABLE OUTPUT + NO_CACHE + ) + if(NOT EXP_LIBRARY_ALIAS_ERROR_VARIABLE) + message(SEND_ERROR "EXP_LIBRARY_ALIAS_ERROR_VARIABLE try_compile failed with library aliased target, but it was expected to succeed ${OUTPUT}.") + endif() + + # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable +# set(CMAKE_TRY_COMPILE_TARGET_TYPE MODULE) +# add_executable(exp_exe_alias ALIAS exp_testExe2) +# try_compile(EXP_EXE_ALIAS_ERROR_VARIABLE +# "${CMAKE_CURRENT_BINARY_DIR}/test_exe_alias" +# "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c" +# LINK_LIBRARIES exp_exe_alias +# OUTPUT_VARIABLE OUTPUT +# NO_CACHE +# ) +# unset(CMAKE_TRY_COMPILE_TARGET_TYPE) +# if(NOT EXP_EXE_ALIAS_ERROR_VARIABLE) +# message(SEND_ERROR "EXP_EXE_ALIAS_ERROR_VARIABLE try_compile failed with executable aliased target, but it was expected to succeed ${OUTPUT}.") +# endif() + + add_library(bld_systemlib_alias ALIAS bld_systemlib) + try_compile(BLD_LIBRARY_ALIAS_ERROR_VARIABLE + "${CMAKE_CURRENT_BINARY_DIR}/test_library_alias" + "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" + LINK_LIBRARIES bld_systemlib_alias + OUTPUT_VARIABLE OUTPUT + NO_CACHE + ) + if(NOT BLD_LIBRARY_ALIAS_ERROR_VARIABLE) + message(SEND_ERROR "BLD_LIBRARY_ALIAS_ERROR_VARIABLE try_compile failed with library aliased target, but it was expected to succeed. ${OUTPUT}") + endif() + + # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable +# set(CMAKE_TRY_COMPILE_TARGET_TYPE MODULE) +# add_executable(bld_exe_alias ALIAS bld_testExe2) +# try_compile(BLD_EXE_ALIAS_ERROR_VARIABLE +# "${CMAKE_CURRENT_BINARY_DIR}/test_exe_alias" +# "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c" +# LINK_LIBRARIES bld_exe_alias +# OUTPUT_VARIABLE OUTPUT +# NO_CACHE +# ) +# unset(CMAKE_TRY_COMPILE_TARGET_TYPE) +# if(NOT BLD_EXE_ALIAS_ERROR_VARIABLE) +# message(SEND_ERROR "BLD_EXE_ALIAS_ERROR_VARIABLE try_compile failed with executable aliased target, but it was expected to succeed. ${OUTPUT}") +# endif() endif() #--------------------------------------------------------------------------------- diff --git a/Tests/ExportImport/Import/A/deps_iface.c b/Tests/ExportImport/Import/A/deps_iface.c index afb1af0..fd2c47f 100644 --- a/Tests/ExportImport/Import/A/deps_iface.c +++ b/Tests/ExportImport/Import/A/deps_iface.c @@ -26,7 +26,7 @@ extern int testLibDepends(void); -int main() +int main(void) { return testLibDepends(); } diff --git a/Tests/ExportImport/Import/A/imp_mod1.c b/Tests/ExportImport/Import/A/imp_mod1.c index 9385d55..138962e 100644 --- a/Tests/ExportImport/Import/A/imp_mod1.c +++ b/Tests/ExportImport/Import/A/imp_mod1.c @@ -7,7 +7,7 @@ testExe2_IMPORT int testExe2Func(void); testExe2_IMPORT int testExe2lib(void); -int imp_mod1() +int imp_mod1(void) { return testExe2Func() + testExe2lib(); } diff --git a/Tests/ExportImport/Import/A/imp_testExe1.c b/Tests/ExportImport/Import/A/imp_testExe1.c index d3b0e9e..e409b1c 100644 --- a/Tests/ExportImport/Import/A/imp_testExe1.c +++ b/Tests/ExportImport/Import/A/imp_testExe1.c @@ -21,7 +21,7 @@ extern int testStaticLibPlugin(void); #endif extern int testLib4libcfg(void); -int main() +int main(void) { return (testLib2() + generated_by_testExe1() + testLib3() + testLib4() + testLib5() + testLib6() + testLib7() + testLibCycleA1() + diff --git a/Tests/ExportImport/Import/A/imp_testExeAbs1.c b/Tests/ExportImport/Import/A/imp_testExeAbs1.c index fd05242..07d33a5 100644 --- a/Tests/ExportImport/Import/A/imp_testExeAbs1.c +++ b/Tests/ExportImport/Import/A/imp_testExeAbs1.c @@ -7,7 +7,7 @@ #ifndef testLibAbs1b # error "testLibAbs1b not defined" #endif -int main() +int main(void) { return 0 + testLibAbs1(); } diff --git a/Tests/ExportImport/Import/A/imp_testLib8.c b/Tests/ExportImport/Import/A/imp_testLib8.c index 2749b17..ef97dbe 100644 --- a/Tests/ExportImport/Import/A/imp_testLib8.c +++ b/Tests/ExportImport/Import/A/imp_testLib8.c @@ -2,7 +2,7 @@ int testLib8A(void); int testLib8B(void); -int main() +int main(void) { return (testLib8A() + testLib8B()); } diff --git a/Tests/ExportImport/Import/A/imp_testLib9.c b/Tests/ExportImport/Import/A/imp_testLib9.c index e014857..2a8d8d5 100644 --- a/Tests/ExportImport/Import/A/imp_testLib9.c +++ b/Tests/ExportImport/Import/A/imp_testLib9.c @@ -10,7 +10,7 @@ int testLib9(void); -int main() +int main(void) { return testLib9(); } diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt index e6dcd65..83c87a8 100644 --- a/Tests/ExportImport/Import/CMakeLists.txt +++ b/Tests/ExportImport/Import/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required (VERSION 2.7.20090711) cmake_policy(SET CMP0025 NEW) +cmake_policy(SET CMP0028 NEW) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/ExportImport/Import/imp_testTransExe1.c b/Tests/ExportImport/Import/imp_testTransExe1.c index 360a112..579c992 100644 --- a/Tests/ExportImport/Import/imp_testTransExe1.c +++ b/Tests/ExportImport/Import/imp_testTransExe1.c @@ -1,6 +1,6 @@ extern int imp_lib1(void); -int main() +int main(void) { return imp_lib1(); } diff --git a/Tests/ExportImport/InitialCache.cmake.in b/Tests/ExportImport/InitialCache.cmake.in index 44cd179..55aef86 100644 --- a/Tests/ExportImport/InitialCache.cmake.in +++ b/Tests/ExportImport/InitialCache.cmake.in @@ -15,3 +15,4 @@ set(CMAKE_INSTALL_PREFIX "@ExportImport_BINARY_DIR@/Root" CACHE STRING "Installa set(CMAKE_SKIP_RPATH ON CACHE BOOL "No RPATH") set(CMAKE_GNUtoMS "@ExportImport_GNUtoMS@" CACHE BOOL "CMAKE_GNUtoMS") set(CMake_TEST_CUDA "@CMake_TEST_CUDA@" CACHE BOOL "CMake_TEST_CUDA") +set(Foo_DIR "@CMAKE_CURRENT_SOURCE_DIR@/External" CACHE PATH "Foo cmake package directory") diff --git a/Tests/ExportImport/main.c b/Tests/ExportImport/main.c index f8b643a..8488f4e 100644 --- a/Tests/ExportImport/main.c +++ b/Tests/ExportImport/main.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/FindALSA/Test/main.c b/Tests/FindALSA/Test/main.c index d3303d0..53831a9 100644 --- a/Tests/FindALSA/Test/main.c +++ b/Tests/FindALSA/Test/main.c @@ -2,7 +2,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { printf("Found ALSA version %s, expected version %s\n", snd_asoundlib_version(), CMAKE_EXPECTED_ALSA_VERSION); diff --git a/Tests/FindBLAS/Test/main.c b/Tests/FindBLAS/Test/main.c index 4fc9fe4..4ce1efb 100644 --- a/Tests/FindBLAS/Test/main.c +++ b/Tests/FindBLAS/Test/main.c @@ -13,7 +13,7 @@ typedef int64_t blas_int; // declare what parts of the blas C-API we need void dswap_(blas_int* N, double* X, blas_int* incX, double* Y, blas_int* incY); -int main() +int main(void) { double x[4] = { 1, 2, 3, 4 }; double y[4] = { 8, 7, 7, 6 }; diff --git a/Tests/FindBZip2/Test/main.c b/Tests/FindBZip2/Test/main.c index 8e24c94..b3cf34b 100644 --- a/Tests/FindBZip2/Test/main.c +++ b/Tests/FindBZip2/Test/main.c @@ -2,7 +2,7 @@ #include <stdio.h> #include <stdlib.h> -int main() +int main(void) { int chunksize = 1024; FILE* file = fopen("test.bzip2", "wb"); diff --git a/Tests/FindCURL/Test/main.c b/Tests/FindCURL/Test/main.c index 263775f..82075f1 100644 --- a/Tests/FindCURL/Test/main.c +++ b/Tests/FindCURL/Test/main.c @@ -2,7 +2,7 @@ #include <stdio.h> #include <stdlib.h> -int main() +int main(void) { struct curl_slist* slist; diff --git a/Tests/FindCups/Test/main.c b/Tests/FindCups/Test/main.c index b69d621..86952db 100644 --- a/Tests/FindCups/Test/main.c +++ b/Tests/FindCups/Test/main.c @@ -1,6 +1,6 @@ #include <cups/cups.h> -int main() +int main(void) { int num_options = 0; cups_option_t* options = NULL; diff --git a/Tests/FindDevIL/Test/main.c b/Tests/FindDevIL/Test/main.c index 4a07087..dfb2f63 100644 --- a/Tests/FindDevIL/Test/main.c +++ b/Tests/FindDevIL/Test/main.c @@ -1,6 +1,6 @@ #include <IL/il.h> -int main() +int main(void) { // Test 1 requires to link to the library. ilInit(); diff --git a/Tests/FindDevIL/Test/main_ilu.c b/Tests/FindDevIL/Test/main_ilu.c index a9e7819..ac7237a 100644 --- a/Tests/FindDevIL/Test/main_ilu.c +++ b/Tests/FindDevIL/Test/main_ilu.c @@ -1,6 +1,6 @@ #include <IL/ilu.h> -int main() +int main(void) { // IL Utilities requires only initialization. // Unlike main DevIL there are no shutdown function. diff --git a/Tests/FindEXPAT/Test/main.c b/Tests/FindEXPAT/Test/main.c index 94ee3ef..703aaf9 100644 --- a/Tests/FindEXPAT/Test/main.c +++ b/Tests/FindEXPAT/Test/main.c @@ -3,7 +3,7 @@ #include <stdlib.h> #include <string.h> -int main() +int main(void) { XML_Expat_Version expat_version; char expat_version_string[16]; diff --git a/Tests/FindFontconfig/Test/main.c b/Tests/FindFontconfig/Test/main.c index c5b5963..96b0af1 100644 --- a/Tests/FindFontconfig/Test/main.c +++ b/Tests/FindFontconfig/Test/main.c @@ -3,7 +3,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { FcInit(); printf("Found Fontconfig.\n"); diff --git a/Tests/FindFreetype/Test/main.c b/Tests/FindFreetype/Test/main.c index bb838a5..315a6cb 100644 --- a/Tests/FindFreetype/Test/main.c +++ b/Tests/FindFreetype/Test/main.c @@ -3,7 +3,7 @@ #include FT_FREETYPE_H #include <string.h> -int main() +int main(void) { FT_Library library; FT_Error error; diff --git a/Tests/FindGDAL/Test/main.c b/Tests/FindGDAL/Test/main.c index 7b31a13..2e19843 100644 --- a/Tests/FindGDAL/Test/main.c +++ b/Tests/FindGDAL/Test/main.c @@ -2,7 +2,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { printf("Found GDAL version %s, expected version %s\n", GDAL_RELEASE_NAME, CMAKE_EXPECTED_GDAL_VERSION); diff --git a/Tests/FindGIF/Test/main.c b/Tests/FindGIF/Test/main.c index 656a99c..fa2c224 100644 --- a/Tests/FindGIF/Test/main.c +++ b/Tests/FindGIF/Test/main.c @@ -8,7 +8,7 @@ # define GIFLIB_MAJOR 4 #endif -int main() +int main(void) { // because of the API changes we have to test different functions depending // on the version of GIFLIB diff --git a/Tests/FindGLUT/Test/main.c b/Tests/FindGLUT/Test/main.c index 1c8569c..02ac34f 100644 --- a/Tests/FindGLUT/Test/main.c +++ b/Tests/FindGLUT/Test/main.c @@ -1,7 +1,7 @@ #include <GL/glut.h> #include <stdio.h> -int main() +int main(void) { /* The following should call exit(1) and print freeglut ERROR: Function <glutCreateWindow> called diff --git a/Tests/FindGnuTLS/Test/main.c b/Tests/FindGnuTLS/Test/main.c index 1105358..c379cc2 100644 --- a/Tests/FindGnuTLS/Test/main.c +++ b/Tests/FindGnuTLS/Test/main.c @@ -3,7 +3,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { // test the linker gnutls_session_t session; diff --git a/Tests/FindImageMagick/Test/main_magick_wand.c b/Tests/FindImageMagick/Test/main_magick_wand.c index fa6d170..52d179d 100644 --- a/Tests/FindImageMagick/Test/main_magick_wand.c +++ b/Tests/FindImageMagick/Test/main_magick_wand.c @@ -1,6 +1,6 @@ #include <wand/MagickWand.h> -int main() +int main(void) { MagickWand* wand = NewMagickWand(); wand = DestroyMagickWand(wand); diff --git a/Tests/FindJPEG/Test/main.c b/Tests/FindJPEG/Test/main.c index 5a67faa..0116cb6 100644 --- a/Tests/FindJPEG/Test/main.c +++ b/Tests/FindJPEG/Test/main.c @@ -4,7 +4,7 @@ #include <jpeglib.h> // clang-format on -int main() +int main(void) { /* Without any JPEG file to open, test that the call fails as expected. This tests that linking worked. */ diff --git a/Tests/FindJasper/Test/main.c b/Tests/FindJasper/Test/main.c index 771344d..242ff7d 100644 --- a/Tests/FindJasper/Test/main.c +++ b/Tests/FindJasper/Test/main.c @@ -4,7 +4,7 @@ #include <jasper/jasper.h> // clang-format on -int main() +int main(void) { /* Without any JPEG file to open, test that the call fails as expected. This tests that linking worked. */ diff --git a/Tests/FindLAPACK/Test/main.c b/Tests/FindLAPACK/Test/main.c index dd33fb3..3c7ad9f 100644 --- a/Tests/FindLAPACK/Test/main.c +++ b/Tests/FindLAPACK/Test/main.c @@ -14,7 +14,7 @@ typedef int64_t blas_int; void dgesv_(blas_int*, blas_int*, double*, blas_int*, blas_int*, double*, blas_int*, blas_int*); -int main() +int main(void) { double A[8] = { 0, 1, 2, 3, 4, 5, 6, 7, diff --git a/Tests/FindLibLZMA/Test/main.c b/Tests/FindLibLZMA/Test/main.c index 06e8065..0b3de31 100644 --- a/Tests/FindLibLZMA/Test/main.c +++ b/Tests/FindLibLZMA/Test/main.c @@ -4,7 +4,7 @@ static const uint8_t test_string[9] = "123456789"; -int main() +int main(void) { static const uint32_t test_vector = 0xCBF43926; diff --git a/Tests/FindLibRHash/Test/main.c b/Tests/FindLibRHash/Test/main.c index 201dced..4cc6394 100644 --- a/Tests/FindLibRHash/Test/main.c +++ b/Tests/FindLibRHash/Test/main.c @@ -1,6 +1,6 @@ #include <rhash.h> -int main() +int main(void) { rhash_library_init(); return 0; diff --git a/Tests/FindLibUV/Test/main.c b/Tests/FindLibUV/Test/main.c index cbd0db3..a14adbb 100644 --- a/Tests/FindLibUV/Test/main.c +++ b/Tests/FindLibUV/Test/main.c @@ -1,6 +1,6 @@ #include <uv.h> -int main() +int main(void) { uv_loop_close(uv_default_loop()); return 0; diff --git a/Tests/FindLibXml2/Test/main.c b/Tests/FindLibXml2/Test/main.c index 264f07d..4ded2f7 100644 --- a/Tests/FindLibXml2/Test/main.c +++ b/Tests/FindLibXml2/Test/main.c @@ -2,7 +2,7 @@ #include <libxml/tree.h> #include <string.h> -int main() +int main(void) { xmlDoc* doc; diff --git a/Tests/FindLibXslt/Test/libexslt.c b/Tests/FindLibXslt/Test/libexslt.c index ea6eb3d..5916024 100644 --- a/Tests/FindLibXslt/Test/libexslt.c +++ b/Tests/FindLibXslt/Test/libexslt.c @@ -2,7 +2,7 @@ #include <libxslt/xslt.h> #include <libxslt/xsltInternals.h> -int main() +int main(void) { xsltInit(); diff --git a/Tests/FindLibXslt/Test/libxslt.c b/Tests/FindLibXslt/Test/libxslt.c index 5b3d766..4a149c1 100644 --- a/Tests/FindLibXslt/Test/libxslt.c +++ b/Tests/FindLibXslt/Test/libxslt.c @@ -4,7 +4,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { xsltInit(); diff --git a/Tests/FindLibinput/Test/main.c b/Tests/FindLibinput/Test/main.c index 3919962..a6b1aa4 100644 --- a/Tests/FindLibinput/Test/main.c +++ b/Tests/FindLibinput/Test/main.c @@ -1,7 +1,7 @@ #include <libinput.h> #include <stdio.h> -int main() +int main(void) { struct libinput_interface interface; interface.open_restricted = 0; diff --git a/Tests/FindODBC/Test/main.c b/Tests/FindODBC/Test/main.c index 34f279c..6c4318b 100644 --- a/Tests/FindODBC/Test/main.c +++ b/Tests/FindODBC/Test/main.c @@ -3,7 +3,7 @@ #endif #include <sql.h> -int main() +int main(void) { SQLHENV env; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); diff --git a/Tests/FindOpenACC/CTest/main.c b/Tests/FindOpenACC/CTest/main.c index 53b6cae..6c005c1 100644 --- a/Tests/FindOpenACC/CTest/main.c +++ b/Tests/FindOpenACC/CTest/main.c @@ -8,7 +8,7 @@ void vecaddgpu(float* r, float* a, float* b, int n) r[i] = a[i] + b[i]; } -int main() +int main(void) { int n = 100000; /* vector length */ float* a; /* input vector 1 */ diff --git a/Tests/FindOpenCL/Test/main.c b/Tests/FindOpenCL/Test/main.c index 2fe949b..d3d0cfb 100644 --- a/Tests/FindOpenCL/Test/main.c +++ b/Tests/FindOpenCL/Test/main.c @@ -4,7 +4,7 @@ # include <CL/cl.h> #endif -int main() +int main(void) { cl_uint platformIdCount; diff --git a/Tests/FindOpenGL/Test/main.c b/Tests/FindOpenGL/Test/main.c index e1f25c6..88a4a8a 100644 --- a/Tests/FindOpenGL/Test/main.c +++ b/Tests/FindOpenGL/Test/main.c @@ -9,7 +9,7 @@ #include <stdio.h> -int main() +int main(void) { /* Reference a GL symbol without requiring a context at runtime. */ printf("&glGetString = %p\n", &glGetString); diff --git a/Tests/FindOpenGL/Test/main_gles2.c b/Tests/FindOpenGL/Test/main_gles2.c index 52f5936..355795b 100644 --- a/Tests/FindOpenGL/Test/main_gles2.c +++ b/Tests/FindOpenGL/Test/main_gles2.c @@ -9,7 +9,7 @@ #include <stdio.h> -int main() +int main(void) { /* Reference a GL symbol without requiring a context at runtime. */ printf("&glGetString = %p\n", &glGetString); diff --git a/Tests/FindOpenGL/Test/main_gles3.c b/Tests/FindOpenGL/Test/main_gles3.c index 875f73c..383954f7 100644 --- a/Tests/FindOpenGL/Test/main_gles3.c +++ b/Tests/FindOpenGL/Test/main_gles3.c @@ -9,7 +9,7 @@ #include <stdio.h> -int main() +int main(void) { /* Reference a GL symbol without requiring a context at runtime. */ printf("&glGetString = %p\n", &glGetString); diff --git a/Tests/FindOpenMP/Test/main.c b/Tests/FindOpenMP/Test/main.c index 4f0e874..9fb67e4 100644 --- a/Tests/FindOpenMP/Test/main.c +++ b/Tests/FindOpenMP/Test/main.c @@ -1,5 +1,5 @@ #include <omp.h> -int main() +int main(void) { #ifndef _OPENMP breaks_on_purpose diff --git a/Tests/FindOpenMP/Test/scaltest.c b/Tests/FindOpenMP/Test/scaltest.c index 4678b87..be48827 100644 --- a/Tests/FindOpenMP/Test/scaltest.c +++ b/Tests/FindOpenMP/Test/scaltest.c @@ -6,7 +6,7 @@ extern "C" #endif int scalprod(int n, double* x, double* y, double* res); -int main() +int main(void) { double a[5] = { 1., 2., 3., 4., 5. }; double b[5] = { 2., 3., 4., 5., 6. }; diff --git a/Tests/FindPNG/Test/main.c b/Tests/FindPNG/Test/main.c index b33b28e..05b55c0 100644 --- a/Tests/FindPNG/Test/main.c +++ b/Tests/FindPNG/Test/main.c @@ -2,7 +2,7 @@ #include <png.h> #include <string.h> -int main() +int main(void) { png_uint_32 png_version; char png_version_string[16]; diff --git a/Tests/FindPackageTest/Exporter/dummy.c b/Tests/FindPackageTest/Exporter/dummy.c index f8b643a..8488f4e 100644 --- a/Tests/FindPackageTest/Exporter/dummy.c +++ b/Tests/FindPackageTest/Exporter/dummy.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/FindPostgreSQL/Test/main.c b/Tests/FindPostgreSQL/Test/main.c index a63377a..b5fcf64 100644 --- a/Tests/FindPostgreSQL/Test/main.c +++ b/Tests/FindPostgreSQL/Test/main.c @@ -2,7 +2,7 @@ #include <stdio.h> #include <string.h> -int main() +int main(void) { int version = PQlibVersion(); char version_string[100]; diff --git a/Tests/FindPython/display_time.c b/Tests/FindPython/display_time.c index 0e78434..568d510 100644 --- a/Tests/FindPython/display_time.c +++ b/Tests/FindPython/display_time.c @@ -6,7 +6,7 @@ #include "display_time.h" -void display_time() +void display_time(void) { #if defined(PYTHON3) wchar_t* program = Py_DecodeLocale("display_time", NULL); diff --git a/Tests/FindPython/main.c b/Tests/FindPython/main.c index 0acba29..0119ce0 100644 --- a/Tests/FindPython/main.c +++ b/Tests/FindPython/main.c @@ -1,7 +1,7 @@ #include "display_time.h" -int main() +int main(void) { display_time(); } diff --git a/Tests/FindSDL/Test/main.c b/Tests/FindSDL/Test/main.c index 057289c..3b774f5 100644 --- a/Tests/FindSDL/Test/main.c +++ b/Tests/FindSDL/Test/main.c @@ -1,6 +1,6 @@ #include <SDL.h> -int main() +int main(void) { // Test 1 requires headers only. SDL_version compiled; diff --git a/Tests/FindSQLite3/Test/main.c b/Tests/FindSQLite3/Test/main.c index fb17c67..f812034 100644 --- a/Tests/FindSQLite3/Test/main.c +++ b/Tests/FindSQLite3/Test/main.c @@ -1,7 +1,7 @@ #include <sqlite3.h> #include <string.h> -int main() +int main(void) { char sqlite3_version[] = SQLITE_VERSION; diff --git a/Tests/FindTIFF/Test/main.c b/Tests/FindTIFF/Test/main.c index 9182652..bce4a3e 100644 --- a/Tests/FindTIFF/Test/main.c +++ b/Tests/FindTIFF/Test/main.c @@ -1,7 +1,7 @@ #include <assert.h> #include <tiffio.h> -int main() +int main(void) { /* Without any TIFF file to open, test that the call fails as expected. This tests that linking worked. */ diff --git a/Tests/FindVulkan/Test/main-SPIRV-Tools.c b/Tests/FindVulkan/Test/main-SPIRV-Tools.c index 097198d..c38f38d 100644 --- a/Tests/FindVulkan/Test/main-SPIRV-Tools.c +++ b/Tests/FindVulkan/Test/main-SPIRV-Tools.c @@ -2,7 +2,7 @@ #include <spirv-tools/libspirv.h> #include <stdio.h> -int main() +int main(void) { const char* spv_version = spvSoftwareVersionString(); const char* spv_details = spvSoftwareVersionDetailsString(); diff --git a/Tests/FindVulkan/Test/main.c b/Tests/FindVulkan/Test/main.c index 1bff651..78eaa4d 100644 --- a/Tests/FindVulkan/Test/main.c +++ b/Tests/FindVulkan/Test/main.c @@ -1,6 +1,6 @@ #include <vulkan/vulkan.h> -int main() +int main(void) { VkInstanceCreateInfo instanceCreateInfo = { 0 }; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; diff --git a/Tests/Fortran/mainc.c b/Tests/Fortran/mainc.c index 9efafc5..607b9fd 100644 --- a/Tests/Fortran/mainc.c +++ b/Tests/Fortran/mainc.c @@ -1,5 +1,5 @@ extern int myc(void); -int main() +int main(void) { return myc(); } diff --git a/Tests/Fortran/maincxx.c b/Tests/Fortran/maincxx.c index d35ea7e..3056d96 100644 --- a/Tests/Fortran/maincxx.c +++ b/Tests/Fortran/maincxx.c @@ -1,6 +1,6 @@ extern int myc(void); extern int mycxx(void); -int main() +int main(void) { return myc() + mycxx(); } diff --git a/Tests/FortranModules/Issue25252-iface-sources/lib.c b/Tests/FortranModules/Issue25252-iface-sources/lib.c index 6ccdb8d..894540c 100644 --- a/Tests/FortranModules/Issue25252-iface-sources/lib.c +++ b/Tests/FortranModules/Issue25252-iface-sources/lib.c @@ -1,4 +1,4 @@ -int f() +int f(void) { return 0; } diff --git a/Tests/GeneratorExpression/objlib1.c b/Tests/GeneratorExpression/objlib1.c index 98a95a4..b33aa48 100644 --- a/Tests/GeneratorExpression/objlib1.c +++ b/Tests/GeneratorExpression/objlib1.c @@ -1,4 +1,4 @@ -void objlib1() +void objlib1(void) { } diff --git a/Tests/GeneratorExpression/objlib2.c b/Tests/GeneratorExpression/objlib2.c index b2c1050..5543f75 100644 --- a/Tests/GeneratorExpression/objlib2.c +++ b/Tests/GeneratorExpression/objlib2.c @@ -1,4 +1,4 @@ -void objlib2() +void objlib2(void) { } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c index 5d857dd..ef922a3 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c @@ -1,4 +1,4 @@ -int test_b() +int test_b(void) { return 2; } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c index 66ee6f3..1df9613 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c @@ -1,4 +1,4 @@ -int test_f() +int test_f(void) { return 1; } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c index 83589ba..8bcb13a 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c @@ -1,4 +1,4 @@ -int test_c() +int test_c(void) { return 1; } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c index 82f9a52..24e2d75 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c @@ -1,4 +1,4 @@ -int test_d() +int test_d(void) { return 1; } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c index feba80e..5dd1214 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c @@ -1,4 +1,4 @@ -int test_a() +int test_a(void) { return 1; } diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c index 943c19d..658f5ab 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c @@ -1,4 +1,4 @@ -int test_e() +int test_e(void) { return 1; } diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c index d1bce33..9282163 100644 --- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c +++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c @@ -1,7 +1,7 @@ #include "INTEGRITY.h" #include "boottable.h" -void main() +void main(void) { Exit(0); } diff --git a/Tests/IncludeDirectories/StandardIncludeDirectories/main.c b/Tests/IncludeDirectories/StandardIncludeDirectories/main.c index edfe9ce..29a2ee1 100644 --- a/Tests/IncludeDirectories/StandardIncludeDirectories/main.c +++ b/Tests/IncludeDirectories/StandardIncludeDirectories/main.c @@ -1,5 +1,5 @@ #include "StdIncDir.h" -int main() +int main(void) { return 0; } diff --git a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c index f8b643a..8488f4e 100644 --- a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c +++ b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/JavaExportImport/main.c b/Tests/JavaExportImport/main.c index f8b643a..8488f4e 100644 --- a/Tests/JavaExportImport/main.c +++ b/Tests/JavaExportImport/main.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/LibName/bar.c b/Tests/LibName/bar.c index c6c1e66..b1c5cf2 100644 --- a/Tests/LibName/bar.c +++ b/Tests/LibName/bar.c @@ -2,6 +2,6 @@ __declspec(dllexport) #endif - extern void foo() + extern void foo(void) { } diff --git a/Tests/LibName/foo.c b/Tests/LibName/foo.c index 52e8d89..1bdb58b 100644 --- a/Tests/LibName/foo.c +++ b/Tests/LibName/foo.c @@ -1,11 +1,11 @@ #ifdef _WIN32 __declspec(dllimport) #endif - extern void foo(); + extern void foo(void); #ifdef _WIN32 __declspec(dllexport) #endif - void bar() + void bar(void) { foo(); } diff --git a/Tests/LibName/foobar.c b/Tests/LibName/foobar.c index 2f28d30..fa7d3e9 100644 --- a/Tests/LibName/foobar.c +++ b/Tests/LibName/foobar.c @@ -3,7 +3,7 @@ __declspec(dllimport) #endif extern void bar(); -int main() +int main(void) { bar(); return 0; diff --git a/Tests/LinkLanguage/LinkLanguage.c b/Tests/LinkLanguage/LinkLanguage.c index 37946c7..18ddb78 100644 --- a/Tests/LinkLanguage/LinkLanguage.c +++ b/Tests/LinkLanguage/LinkLanguage.c @@ -1,5 +1,5 @@ extern int foo(void); -int main() +int main(void) { return foo(); } diff --git a/Tests/LinkLine/Exec.c b/Tests/LinkLine/Exec.c index 807a7a8..11b5650 100644 --- a/Tests/LinkLine/Exec.c +++ b/Tests/LinkLine/Exec.c @@ -1,7 +1,7 @@ void OneFunc(); void TwoFunc(); -int main() +int main(void) { OneFunc(); TwoFunc(); diff --git a/Tests/LinkLine/One.c b/Tests/LinkLine/One.c index 856d0d1..4f3ad63 100644 --- a/Tests/LinkLine/One.c +++ b/Tests/LinkLine/One.c @@ -1,6 +1,6 @@ -void TwoFunc(); +void TwoFunc(void); -void OneFunc() +void OneFunc(void) { static int i = 0; ++i; diff --git a/Tests/LinkLine/Two.c b/Tests/LinkLine/Two.c index 5fc212e..ac84367 100644 --- a/Tests/LinkLine/Two.c +++ b/Tests/LinkLine/Two.c @@ -1,6 +1,6 @@ -void OneFunc(); +void OneFunc(void); -void TwoFunc() +void TwoFunc(void) { static int i = 0; ++i; diff --git a/Tests/LinkLineOrder/Exec1.c b/Tests/LinkLineOrder/Exec1.c index 9bbf0f6..e47841d 100644 --- a/Tests/LinkLineOrder/Exec1.c +++ b/Tests/LinkLineOrder/Exec1.c @@ -1,7 +1,7 @@ /* Directly depends on One */ void OneFunc(); -int main() +int main(void) { OneFunc(); return 0; diff --git a/Tests/LinkLineOrder/Exec2.c b/Tests/LinkLineOrder/Exec2.c index 91b8575..d60c94e 100644 --- a/Tests/LinkLineOrder/Exec2.c +++ b/Tests/LinkLineOrder/Exec2.c @@ -1,7 +1,7 @@ /* Directly depends on Two */ void TwoFunc(); -int main() +int main(void) { TwoFunc(); return 0; diff --git a/Tests/LinkLineOrder/NoDepA.c b/Tests/LinkLineOrder/NoDepA.c index 76f97bc..72333bc 100644 --- a/Tests/LinkLineOrder/NoDepA.c +++ b/Tests/LinkLineOrder/NoDepA.c @@ -1,7 +1,7 @@ /* depends on NoDepB */ -void NoDepB_func(); +void NoDepB_func(void); -void NoDepA_func() +void NoDepA_func(void) { NoDepB_func(); } diff --git a/Tests/LinkLineOrder/NoDepB.c b/Tests/LinkLineOrder/NoDepB.c index fa89ae9..a5c30e5 100644 --- a/Tests/LinkLineOrder/NoDepB.c +++ b/Tests/LinkLineOrder/NoDepB.c @@ -1,4 +1,4 @@ /* No dependencies */ -void NoDepB_func() +void NoDepB_func(void) { } diff --git a/Tests/LinkLineOrder/NoDepC.c b/Tests/LinkLineOrder/NoDepC.c index f05d962..cafb3fd 100644 --- a/Tests/LinkLineOrder/NoDepC.c +++ b/Tests/LinkLineOrder/NoDepC.c @@ -1,7 +1,7 @@ /* depends on NoDepA */ -void NoDepA_func(); +void NoDepA_func(void); -void NoDepC_func() +void NoDepC_func(void) { NoDepA_func(); } diff --git a/Tests/LinkLineOrder/NoDepE.c b/Tests/LinkLineOrder/NoDepE.c index c47bb85..6e33c7b 100644 --- a/Tests/LinkLineOrder/NoDepE.c +++ b/Tests/LinkLineOrder/NoDepE.c @@ -1,7 +1,7 @@ /* depends on NoDepF */ -void NoDepF_func(); +void NoDepF_func(void); -void NoDepE_func() +void NoDepE_func(void) { static int firstcall = 1; if (firstcall) { diff --git a/Tests/LinkLineOrder/NoDepF.c b/Tests/LinkLineOrder/NoDepF.c index a814310..f4afcb5 100644 --- a/Tests/LinkLineOrder/NoDepF.c +++ b/Tests/LinkLineOrder/NoDepF.c @@ -1,7 +1,7 @@ /* depends on NoDepE */ -void NoDepE_func(); +void NoDepE_func(void); -void NoDepF_func() +void NoDepF_func(void) { static int firstcall = 1; if (firstcall) { diff --git a/Tests/LinkLineOrder/NoDepX.c b/Tests/LinkLineOrder/NoDepX.c index c895dd1..c8de222 100644 --- a/Tests/LinkLineOrder/NoDepX.c +++ b/Tests/LinkLineOrder/NoDepX.c @@ -1,7 +1,7 @@ /* depends on NoDepY*/ -void NoDepY_func(); +void NoDepY_func(void); -void NoDepX_func() +void NoDepX_func(void) { NoDepY_func(); } diff --git a/Tests/LinkLineOrder/NoDepY.c b/Tests/LinkLineOrder/NoDepY.c index 1e6a4ae..dc492d5 100644 --- a/Tests/LinkLineOrder/NoDepY.c +++ b/Tests/LinkLineOrder/NoDepY.c @@ -1,4 +1,4 @@ /* No dependencies */ -void NoDepY_func() +void NoDepY_func(void) { } diff --git a/Tests/LinkLineOrder/NoDepZ.c b/Tests/LinkLineOrder/NoDepZ.c index 045e570..c866667 100644 --- a/Tests/LinkLineOrder/NoDepZ.c +++ b/Tests/LinkLineOrder/NoDepZ.c @@ -1,7 +1,7 @@ /* depends on NoDepX */ -void NoDepX_func(); +void NoDepX_func(void); -void NoDepZ_func() +void NoDepZ_func(void) { NoDepX_func(); } diff --git a/Tests/LinkLineOrder/One.c b/Tests/LinkLineOrder/One.c index b23b1ec..b95fa6d 100644 --- a/Tests/LinkLineOrder/One.c +++ b/Tests/LinkLineOrder/One.c @@ -1,9 +1,9 @@ /* depends on NoDepC and NoDepE (and hence on NoDepA, NoDepB and */ /* NoDepF) */ -void NoDepC_func(); -void NoDepE_func(); +void NoDepC_func(void); +void NoDepE_func(void); -void OneFunc() +void OneFunc(void) { NoDepC_func(); NoDepE_func(); diff --git a/Tests/LinkLineOrder/Two.c b/Tests/LinkLineOrder/Two.c index 6bffaa8..9955d67 100644 --- a/Tests/LinkLineOrder/Two.c +++ b/Tests/LinkLineOrder/Two.c @@ -1,7 +1,7 @@ -void OneFunc(); -void NoDepZ_func(); +void OneFunc(void); +void NoDepZ_func(void); -void TwoFunc() +void TwoFunc(void) { OneFunc(); NoDepZ_func(); diff --git a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c index 7176ebe..ba13727 100644 --- a/Tests/LoadCommand/CMakeCommands/cmTestCommand.c +++ b/Tests/LoadCommand/CMakeCommands/cmTestCommand.c @@ -16,14 +16,14 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) { char* file; char* str; - char* srcs; + const char* srcs; const char* cstr; char buffer[1024]; void* source_file; char* args[2]; - char* ccArgs[4]; - char* ccDep[1]; - char* ccOut[1]; + const char* ccArgs[4]; + const char* ccDep[1]; + const char* ccOut[1]; cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf; cmVTKWrapTclData* cdata = @@ -148,7 +148,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) if (info->CAPI->GetTotalArgumentSize(2, args) != 13) { return 0; } - info->CAPI->ExecuteCommand(mf, "SET", 2, args); + + ccArgs[0] = "TEST_EXEC"; + ccArgs[1] = "TRUE"; + info->CAPI->ExecuteCommand(mf, "SET", 2, ccArgs); /* make sure we can find the source file */ if (!info->CAPI->GetSource(mf, argv[1])) { diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c index 7176ebe..e3b332c 100644 --- a/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c +++ b/Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c @@ -16,14 +16,14 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) { char* file; char* str; - char* srcs; - const char* cstr; + const char* srcs; + char* cstr; char buffer[1024]; void* source_file; char* args[2]; - char* ccArgs[4]; - char* ccDep[1]; - char* ccOut[1]; + const char* ccArgs[4]; + const char* ccDep[1]; + const char* ccOut[1]; cmLoadedCommandInfo* info = (cmLoadedCommandInfo*)inf; cmVTKWrapTclData* cdata = @@ -148,7 +148,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[]) if (info->CAPI->GetTotalArgumentSize(2, args) != 13) { return 0; } - info->CAPI->ExecuteCommand(mf, "SET", 2, args); + + ccArgs[0] = "TEST_EXEC"; + ccArgs[1] = "TRUE"; + info->CAPI->ExecuteCommand(mf, "SET", 2, ccArgs); /* make sure we can find the source file */ if (!info->CAPI->GetSource(mf, argv[1])) { diff --git a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt index 2a8a152..4cd200a 100644 --- a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt +++ b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt @@ -21,14 +21,16 @@ foreach(t MultiThreaded SingleThreaded) endforeach() endforeach() endforeach() -if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang") - # LLVMFlang does not actually define these, so inject them +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 18.0) + # LLVMFlang < 18.0 does not define these, so inject them. set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "-D_MT") set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL "-D_MT;-D_DLL") set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug "-D_MT;-D_DEBUG") set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "-D_MT;-D_DEBUG;-D_DLL") endif() -string(APPEND CMAKE_Fortran_FLAGS " -w") +if(NOT CMAKE_Fortran_SIMULATE_ID STREQUAL "MSVC") + string(APPEND CMAKE_Fortran_FLAGS " -w") +endif() function(verify_combinations threads lang src) set(verify_tc_config_ Release) diff --git a/Tests/Module/CheckIPOSupported-C/bar.c b/Tests/Module/CheckIPOSupported-C/bar.c index 680f213..728d8fb 100644 --- a/Tests/Module/CheckIPOSupported-C/bar.c +++ b/Tests/Module/CheckIPOSupported-C/bar.c @@ -1,4 +1,4 @@ -int bar() +int bar(void) { return 0x42; } diff --git a/Tests/Module/CheckIPOSupported-C/foo.c b/Tests/Module/CheckIPOSupported-C/foo.c index 1e56597..6a64a99 100644 --- a/Tests/Module/CheckIPOSupported-C/foo.c +++ b/Tests/Module/CheckIPOSupported-C/foo.c @@ -1,4 +1,4 @@ -int foo() +int foo(void) { return 0x42; } diff --git a/Tests/Module/CheckIPOSupported-C/main.c b/Tests/Module/CheckIPOSupported-C/main.c index 28ab26f..7d8ad9a 100644 --- a/Tests/Module/CheckIPOSupported-C/main.c +++ b/Tests/Module/CheckIPOSupported-C/main.c @@ -1,7 +1,7 @@ int foo(); int bar(); -int main() +int main(void) { if (foo() != bar()) { return 1; diff --git a/Tests/Module/CheckTypeSize/CheckTypeSize.c b/Tests/Module/CheckTypeSize/CheckTypeSize.c index adfd2fc..eb1aa94 100644 --- a/Tests/Module/CheckTypeSize/CheckTypeSize.c +++ b/Tests/Module/CheckTypeSize/CheckTypeSize.c @@ -28,7 +28,7 @@ result = 1; \ } while (0) -int main() +int main(void) { int result = 0; struct somestruct x; diff --git a/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c b/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c index 487e66d..34c373c 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c +++ b/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c @@ -1,7 +1,7 @@ #include "test_compiler_detection.h" -int main() +int main(void) { return 0; } diff --git a/Tests/Module/WriteCompilerDetectionHeader/main.c b/Tests/Module/WriteCompilerDetectionHeader/main.c index 3420c67..1253563 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/main.c +++ b/Tests/Module/WriteCompilerDetectionHeader/main.c @@ -24,7 +24,7 @@ # error Expect no CXX features defined #endif -int main() +int main(void) { return 0; } diff --git a/Tests/Module/WriteCompilerDetectionHeader/main_multi.c b/Tests/Module/WriteCompilerDetectionHeader/main_multi.c index 28f9dae..3853671 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/main_multi.c +++ b/Tests/Module/WriteCompilerDetectionHeader/main_multi.c @@ -24,7 +24,7 @@ # error Expect no CXX features defined #endif -int main() +int main(void) { return 0; } diff --git a/Tests/OutOfBinary/outlib.c b/Tests/OutOfBinary/outlib.c index d309ebe..3eaedea 100644 --- a/Tests/OutOfBinary/outlib.c +++ b/Tests/OutOfBinary/outlib.c @@ -1,4 +1,4 @@ -int outlib() +int outlib(void) { return 456; } diff --git a/Tests/PDBDirectoryAndName/myexe.c b/Tests/PDBDirectoryAndName/myexe.c index fdb8b09..3d66794 100644 --- a/Tests/PDBDirectoryAndName/myexe.c +++ b/Tests/PDBDirectoryAndName/myexe.c @@ -1,8 +1,8 @@ -extern int mylibA(); -extern int mylibB(); -extern int mylibC(); -extern int mylibD(); -int main() +extern int mylibA(void); +extern int mylibB(void); +extern int mylibC(void); +extern int mylibD(void); +int main(void) { return mylibA() + mylibB() + mylibC() + mylibD(); } diff --git a/Tests/PDBDirectoryAndName/myexe2.c b/Tests/PDBDirectoryAndName/myexe2.c index 250d651..b32e0b1 100644 --- a/Tests/PDBDirectoryAndName/myexe2.c +++ b/Tests/PDBDirectoryAndName/myexe2.c @@ -1,6 +1,6 @@ -extern int mylibA(); -extern int mylibD(); -int main() +extern int mylibA(void); +extern int mylibD(void); +int main(void) { return mylibA() + mylibD(); } diff --git a/Tests/PDBDirectoryAndName/mylibA.c b/Tests/PDBDirectoryAndName/mylibA.c index 5bc279b..58ff123 100644 --- a/Tests/PDBDirectoryAndName/mylibA.c +++ b/Tests/PDBDirectoryAndName/mylibA.c @@ -1,4 +1,4 @@ -__declspec(dllexport) int mylibA() +__declspec(dllexport) int mylibA(void) { return 1; } diff --git a/Tests/PDBDirectoryAndName/mylibB.c b/Tests/PDBDirectoryAndName/mylibB.c index 3a95845..8be2aa0 100644 --- a/Tests/PDBDirectoryAndName/mylibB.c +++ b/Tests/PDBDirectoryAndName/mylibB.c @@ -1,4 +1,4 @@ -int mylibB() +int mylibB(void) { return -1; } diff --git a/Tests/PDBDirectoryAndName/mylibC.c b/Tests/PDBDirectoryAndName/mylibC.c index 8982849..64dde72 100644 --- a/Tests/PDBDirectoryAndName/mylibC.c +++ b/Tests/PDBDirectoryAndName/mylibC.c @@ -1,4 +1,4 @@ -__declspec(dllexport) int mylibC() +__declspec(dllexport) int mylibC(void) { return 1; } diff --git a/Tests/PDBDirectoryAndName/mylibD.c b/Tests/PDBDirectoryAndName/mylibD.c index a53b7a2..c9c04d8 100644 --- a/Tests/PDBDirectoryAndName/mylibD.c +++ b/Tests/PDBDirectoryAndName/mylibD.c @@ -1,4 +1,4 @@ -int mylibD() +int mylibD(void) { return -1; } diff --git a/Tests/PerConfig/perconfig.c b/Tests/PerConfig/perconfig.c index d942d45..5162ae0 100644 --- a/Tests/PerConfig/perconfig.c +++ b/Tests/PerConfig/perconfig.c @@ -1,6 +1,6 @@ #include "pcShared.h" extern const char* pcStatic(void); -int main() +int main(void) { pcStatic(); pcShared(); diff --git a/Tests/PolicyScope/main.c b/Tests/PolicyScope/main.c index f8b643a..8488f4e 100644 --- a/Tests/PolicyScope/main.c +++ b/Tests/PolicyScope/main.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/PrecompiledHeader/foo1.c b/Tests/PrecompiledHeader/foo1.c index fef2586..e743f8b 100644 --- a/Tests/PrecompiledHeader/foo1.c +++ b/Tests/PrecompiledHeader/foo1.c @@ -2,7 +2,7 @@ # error "Precompiled header foo_precompiled.h has not been loaded." #endif -int main() +int main(void) { return foo(); } diff --git a/Tests/PrecompiledHeader/foo2.c b/Tests/PrecompiledHeader/foo2.c index 3ed04ed..5211f3e 100644 --- a/Tests/PrecompiledHeader/foo2.c +++ b/Tests/PrecompiledHeader/foo2.c @@ -3,7 +3,7 @@ # error "Precompiled header foo_precompiled.h has not been loaded." #endif -int foo() +int foo(void) { return 0; } diff --git a/Tests/RunCMake/AppleTextStubs/foo.c b/Tests/RunCMake/AppleTextStubs/foo.c index 7f39d71..f669327 100644 --- a/Tests/RunCMake/AppleTextStubs/foo.c +++ b/Tests/RunCMake/AppleTextStubs/foo.c @@ -1,5 +1,5 @@ -int foo() +int foo(void) { return 0; } diff --git a/Tests/RunCMake/AppleTextStubs/main.c b/Tests/RunCMake/AppleTextStubs/main.c index dc5ce3d..390f7d1 100644 --- a/Tests/RunCMake/AppleTextStubs/main.c +++ b/Tests/RunCMake/AppleTextStubs/main.c @@ -1,7 +1,7 @@ extern int foo(void); -int main() +int main(void) { return foo(); } diff --git a/Tests/RunCMake/AutoExportDll/foo.c b/Tests/RunCMake/AutoExportDll/foo.c index d13bc3e..b4f3ae0 100644 --- a/Tests/RunCMake/AutoExportDll/foo.c +++ b/Tests/RunCMake/AutoExportDll/foo.c @@ -4,12 +4,12 @@ # define WINAPI #endif -int WINAPI foo() +int WINAPI foo(void) { return 10; } -int bar() +int bar(void) { return 5; } diff --git a/Tests/RunCMake/AutoExportDll/hello2.c b/Tests/RunCMake/AutoExportDll/hello2.c index d4d6b72..66e7caf 100644 --- a/Tests/RunCMake/AutoExportDll/hello2.c +++ b/Tests/RunCMake/AutoExportDll/hello2.c @@ -2,7 +2,7 @@ extern int own_auto_export_function(int i); -void hello2() +void hello2(void) { printf("hello exec:%i", own_auto_export_function(41)); } diff --git a/Tests/RunCMake/AutoExportDll/objlib.c b/Tests/RunCMake/AutoExportDll/objlib.c index 54a9658..4e580c3 100644 --- a/Tests/RunCMake/AutoExportDll/objlib.c +++ b/Tests/RunCMake/AutoExportDll/objlib.c @@ -1,4 +1,4 @@ -int objlib() +int objlib(void) { return 7; } diff --git a/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c b/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c index 0795aaa..bd54d76 100644 --- a/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c +++ b/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c @@ -1,5 +1,5 @@ #include <MakeInProjectOnly.h> -int main() +int main(void) { return MakeInProjectOnly(); } diff --git a/Tests/RunCMake/BuiltinTargets/CMakeLists.txt b/Tests/RunCMake/BuiltinTargets/CMakeLists.txt new file mode 100644 index 0000000..6a9ce76 --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake b/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake new file mode 100644 index 0000000..6a74f57 --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake @@ -0,0 +1,19 @@ +include(RunCMake) + +if(RunCMake_GENERATOR MATCHES "Make|Ninja") + set(test_target "test") +else() + set(test_target "RUN_TESTS") +endif() + +function(run_BuiltinTarget case target) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + run_cmake(${case}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug --target ${${target}_target}) +endfunction() + +run_BuiltinTarget(TestDependsAll-Default test) +run_BuiltinTarget(TestDependsAll-No test) +run_BuiltinTarget(TestDependsAll-Yes test) diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake new file mode 100644 index 0000000..5ef321a --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TestDependsAll-No-build-check.cmake) diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake new file mode 100644 index 0000000..6abd56c --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake @@ -0,0 +1 @@ +include(TestDependsAll-common.cmake) diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake new file mode 100644 index 0000000..f42244e --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake @@ -0,0 +1,3 @@ +if(EXISTS ${RunCMake_TEST_BINARY_DIR}/custom-output.txt) + set(RunCMake_TEST_FAILED "Building 'test' target incorrectly built 'all' target.") +endif() diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake new file mode 100644 index 0000000..50ec3b9 --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake @@ -0,0 +1,2 @@ +include(TestDependsAll-common.cmake) +set(CMAKE_SKIP_TEST_ALL_DEPENDENCY ON) diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake new file mode 100644 index 0000000..ed175d4 --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake @@ -0,0 +1,3 @@ +if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/custom-output.txt) + set(RunCMake_TEST_FAILED "Building 'test' target did not build 'all' target:\n ${RunCMake_TEST_BINARY_DIR}/custom-output.txt") +endif() diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake new file mode 100644 index 0000000..c35c98d --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake @@ -0,0 +1,2 @@ +include(TestDependsAll-common.cmake) +set(CMAKE_SKIP_TEST_ALL_DEPENDENCY OFF) diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake new file mode 100644 index 0000000..bbe7c75 --- /dev/null +++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake @@ -0,0 +1,3 @@ +enable_testing() +add_custom_target(custom ALL COMMAND ${CMAKE_COMMAND} -E touch custom-output.txt) +add_test(NAME test COMMAND ${CMAKE_COMMAND} -E echo) diff --git a/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt new file mode 100644 index 0000000..7d2608b --- /dev/null +++ b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0126 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/CMP0126/CMP0126-OLD_CL-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt new file mode 100644 index 0000000..1f2179c --- /dev/null +++ b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0126-OLD_CL\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0126 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/CMP0156/CMP0156-Common.cmake b/Tests/RunCMake/CMP0156/CMP0156-Common.cmake new file mode 100644 index 0000000..a382c77 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-Common.cmake @@ -0,0 +1,27 @@ + +enable_language(C) + +# ensure link is successful in case of circular dependency +add_library(lib1 STATIC lib1.c) +add_library(lib2 STATIC lib2.c) + +target_link_libraries(lib1 PRIVATE lib2) +target_link_libraries(lib2 PRIVATE lib1) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib1) +if (APPLE_TEST) + target_link_options(main PRIVATE "LINKER:-fatal_warnings") +else() + target_link_options(main PRIVATE "$<$<AND:$<NOT:$<TARGET_POLICY:CMP0156>>,$<C_COMPILER_ID:AppleClang>,$<VERSION_GREATER_EQUAL:$<C_COMPILER_VERSION>,15.0>>:LINKER:-no_warn_duplicate_libraries>") +endif() + + +add_library(lib3 SHARED lib3.c) +add_library(lib4 STATIC lib4.c) +target_link_libraries(lib4 PRIVATE lib3) + +# link specifying a shared library not directly used by the target +# on Windows, with CMP0156=NEW, lib3 is specified before lib4 on link step +add_executable(main2 main2.c) +target_link_libraries(main2 PRIVATE lib3 lib4) diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake new file mode 100644 index 0000000..59f5ecd --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake @@ -0,0 +1,5 @@ + +cmake_policy(SET CMP0156 NEW) +set(APPLE_TEST TRUE) + +include (CMP0156-Common.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake new file mode 100644 index 0000000..4387b37 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake @@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0156 NEW) + +include (CMP0156-Common.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt new file mode 100644 index 0000000..b18168c --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt @@ -0,0 +1 @@ +.+ diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt new file mode 100644 index 0000000..b4afc27 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt @@ -0,0 +1,3 @@ +ld: warning: ignoring duplicate libraries: '.*liblib1.a', '.*liblib2.a' +ld: fatal warning\(s\) induced error \(-fatal_warnings\) +clang: error: linker command failed with exit code 1 \(use -v to see invocation\) diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake new file mode 100644 index 0000000..33e1287 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake @@ -0,0 +1,5 @@ + +cmake_policy(SET CMP0156 OLD) +set(APPLE_TEST TRUE) + +include (CMP0156-Common.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake b/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake new file mode 100644 index 0000000..b012a1a --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake @@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0156 OLD) + +include (CMP0156-Common.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt new file mode 100644 index 0000000..f52d7d8 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt @@ -0,0 +1,35 @@ +CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_library\): + Policy CMP0156 is not set: De-duplicate libraries on link lines based on + linker capabilities. Run "cmake --help-policy CMP0156" for policy details. + Use the cmake_policy command to set the policy and suppress this warning. + + Since the policy is not set, legacy libraries de-duplication strategy will + be applied. +Call Stack \(most recent call first\): + CMP0156-WARN.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_library\): + Policy CMP0156 is not set: De-duplicate libraries on link lines based on + linker capabilities. Run "cmake --help-policy CMP0156" for policy details. + Use the cmake_policy command to set the policy and suppress this warning. + + Since the policy is not set, legacy libraries de-duplication strategy will + be applied. +Call Stack \(most recent call first\): + CMP0156-WARN.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_executable\): + Policy CMP0156 is not set: De-duplicate libraries on link lines based on + linker capabilities. Run "cmake --help-policy CMP0156" for policy details. + Use the cmake_policy command to set the policy and suppress this warning. + + Since the policy is not set, legacy libraries de-duplication strategy will + be applied. +Call Stack \(most recent call first\): + CMP0156-WARN.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake b/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake new file mode 100644 index 0000000..5f469ef --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake @@ -0,0 +1,4 @@ + +set(CMAKE_POLICY_WARNING_CMP0156 TRUE) + +include (CMP0156-Common.cmake) diff --git a/Tests/RunCMake/CMP0156/CMakeLists.txt b/Tests/RunCMake/CMP0156/CMakeLists.txt new file mode 100644 index 0000000..922aad6 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.27) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0156/RunCMakeTest.cmake b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake new file mode 100644 index 0000000..bd51830 --- /dev/null +++ b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake @@ -0,0 +1,26 @@ +include(RunCMake) + +# CMP0156 control how libraries are specified for the link step +# a sensible configuration is how circular dependency is handled + +macro(run_cmake_and_build test) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_OUTPUT_MERGE TRUE) + run_cmake(${test}) + set(RunCMake_TEST_NO_CLEAN TRUE) + run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release) + unset(RunCMake_TEST_NO_CLEAN) + unset(RunCMake_TEST_BINARY_DIR) + unset(RunCMake_TEST_OUTPUT_MERGE) +endmacro() + +run_cmake(CMP0156-WARN) +run_cmake_and_build(CMP0156-OLD) +run_cmake_and_build(CMP0156-NEW) + +if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" + AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "15.0") + # special case for Apple: with CMP0156=OLD, linker will warning on duplicate libraries + run_cmake_and_build(CMP0156-OLD-AppleClang) + run_cmake_and_build(CMP0156-NEW-AppleClang) +endif() diff --git a/Tests/RunCMake/CMP0156/lib1.c b/Tests/RunCMake/CMP0156/lib1.c new file mode 100644 index 0000000..faad375 --- /dev/null +++ b/Tests/RunCMake/CMP0156/lib1.c @@ -0,0 +1,7 @@ + +extern void lib2(void); + +void lib1(void) +{ + lib2(); +} diff --git a/Tests/RunCMake/CMP0156/lib2.c b/Tests/RunCMake/CMP0156/lib2.c new file mode 100644 index 0000000..9db7914 --- /dev/null +++ b/Tests/RunCMake/CMP0156/lib2.c @@ -0,0 +1,7 @@ + +extern void lib1(void); + +void lib2(void) +{ + lib1(); +} diff --git a/Tests/RunCMake/CMP0156/lib3.c b/Tests/RunCMake/CMP0156/lib3.c new file mode 100644 index 0000000..e63e456 --- /dev/null +++ b/Tests/RunCMake/CMP0156/lib3.c @@ -0,0 +1,7 @@ + +#if defined(_WIN32) +__declspec(dllexport) +#endif + void lib3(void) +{ +} diff --git a/Tests/RunCMake/CMP0156/lib4.c b/Tests/RunCMake/CMP0156/lib4.c new file mode 100644 index 0000000..a992168 --- /dev/null +++ b/Tests/RunCMake/CMP0156/lib4.c @@ -0,0 +1,9 @@ + +#if defined(_WIN32) +__declspec(dllimport) +#endif + void lib3(void); + +void lib4(void) +{ +} diff --git a/Tests/RunCMake/CMP0156/main.c b/Tests/RunCMake/CMP0156/main.c new file mode 100644 index 0000000..06edfd5 --- /dev/null +++ b/Tests/RunCMake/CMP0156/main.c @@ -0,0 +1,9 @@ + +extern void lib1(void); + +int main(void) +{ + lib1(); + + return 0; +} diff --git a/Tests/RunCMake/CMP0156/main2.c b/Tests/RunCMake/CMP0156/main2.c new file mode 100644 index 0000000..9fc2838 --- /dev/null +++ b/Tests/RunCMake/CMP0156/main2.c @@ -0,0 +1,9 @@ + +extern void lib4(void); + +int main(void) +{ + lib4(); + + return 0; +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 57034e5..dbcb94e 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -87,10 +87,6 @@ endfunction() # Some tests use python for extra checks. find_package(Python QUIET) -if(XCODE_VERSION AND "${XCODE_VERSION}" VERSION_LESS 6.1) - set(Swift_ARGS -DXCODE_BELOW_6_1=1) -endif() - # Test MSVC for older host CMake versions, and test # WIN32/CMAKE_C_COMPILER_ID to fix check on Intel for Windows. if(MSVC OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel") @@ -170,6 +166,8 @@ if(NOT WIN32 OR CYGWIN) endif() add_RunCMake_test(CMP0153) +add_RunCMake_test(CMP0156 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode @@ -322,10 +320,18 @@ add_RunCMake_test(BuildDepends -DCMAKE_C_LINK_DEPENDS_USE_COMPILER=${CMAKE_C_LINK_DEPENDS_USE_COMPILER} -DCMake_TEST_BuildDepends_GNU_AS=${CMake_TEST_BuildDepends_GNU_AS} ) +add_RunCMake_test(BuiltinTargets) if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") add_RunCMake_test(Byproducts) endif() add_RunCMake_test(CMakeDependentOption) +if(APPLE # Remove these conditions when the test has non-Apple cases + AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + add_RunCMake_test(CMakePackage + -DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION} + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + ) +endif() add_RunCMake_test(CMakeRoleGlobalProperty) add_RunCMake_test(CMakeRelease -DCMake_TEST_JQ=${CMake_TEST_JQ}) if(CMAKE_GENERATOR MATCHES "Make|Ninja") @@ -432,7 +438,9 @@ if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STRE add_RunCMake_test(RuntimePath) endif() add_RunCMake_test(ScriptMode) -add_RunCMake_test(Swift -DCMAKE_Swift_COMPILER=${CMAKE_Swift_COMPILER} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) +add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMake_TEST_Swift=${CMake_TEST_Swift} + -DXCODE_VERSION=${XCODE_VERSION}) add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(TargetObjects) add_RunCMake_test(TargetProperties) @@ -512,6 +520,7 @@ add_RunCMake_test(ctest_skipped_test) add_RunCMake_test(ctest_update) add_RunCMake_test(ctest_upload) add_RunCMake_test(ctest_environment) +add_RunCMake_test(ctest_empty_binary_directory) add_RunCMake_test(ctest_fixtures) add_RunCMake_test(define_property) add_RunCMake_test(file -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}) @@ -527,7 +536,7 @@ endforeach() add_RunCMake_test(file-DOWNLOAD) add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(find_file -DMINGW=${MINGW}) -add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}) +add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DMSVC=${MSVC}) add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS}) add_RunCMake_test(find_path -DMINGW=${MINGW}) add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) @@ -536,7 +545,7 @@ add_RunCMake_test(function) add_RunCMake_test(block) add_RunCMake_test(get_filename_component) add_RunCMake_test(get_property) -add_RunCMake_test(if) +add_RunCMake_test(if -DMSYS=${MSYS}) add_RunCMake_test(include) add_RunCMake_test(include_directories) add_RunCMake_test(include_guard) @@ -583,8 +592,10 @@ function(add_RunCMake_test_try_compile) endfunction() add_RunCMake_test_try_compile() -add_RunCMake_test(try_run -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} - -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) +if(CMAKE_Fortran_COMPILER) + list(APPEND try_run_ARGS -DCMake_TEST_Fortran=1) +endif() +add_RunCMake_test(try_run) add_RunCMake_test(set) add_RunCMake_test(variable_watch) add_RunCMake_test(while) @@ -738,6 +749,15 @@ if((CMAKE_C_COMPILER_ID STREQUAL "AppleClang" endif() endif() +if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)" + AND CMAKE_C_COMPILER_ID MATCHES "^(AppleClang|Clang|GNU|MSVC|NVIDIA)$" + AND NOT CMAKE_GENERATOR STREQUAL "Green Hills MULTI") + add_RunCMake_test(LinkerSelection -DCMake_TEST_CUDA=${CMake_TEST_CUDA} + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) +endif() + +add_RunCMake_test(LinkLibrariesProcessing) add_RunCMake_test(File_Archive) add_RunCMake_test(File_Configure) add_RunCMake_test(File_Generate) @@ -961,6 +981,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") endif() set(cpack_tests + DEB.AUTO_SUFFIXES DEB.CUSTOM_NAMES DEB.DEBUGINFO DEB.DEFAULT_PERMISSIONS @@ -979,6 +1000,7 @@ set(cpack_tests DEB.DEB_DESCRIPTION DEB.PROJECT_META + RPM.AUTO_SUFFIXES RPM.CUSTOM_BINARY_SPEC_FILE RPM.CUSTOM_NAMES RPM.DEBUGINFO @@ -1056,7 +1078,7 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_SIMULATE_ID=${CMAKE_C_SIMULATE_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) -add_RunCMake_test("UnityBuild") +add_RunCMake_test(UnityBuild -DCMake_TEST_OBJC=${CMake_TEST_OBJC}) add_RunCMake_test(CMakePresets -DPython_EXECUTABLE=${Python_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA} @@ -1089,3 +1111,8 @@ endif() if(WIN32) add_RunCMake_test(Win32GenEx) endif() + +if(CMake_TEST_IAR_TOOLCHAINS) + add_RunCMake_test(IAR -DCMake_TEST_IAR_TOOLCHAINS=${CMake_TEST_IAR_TOOLCHAINS}) + set_property(TEST RunCMake.IAR APPEND PROPERTY LABELS "IAR") +endif() diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt new file mode 100644 index 0000000..03be015 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at [^ +]*/Modules/CMakePackageConfigHelpers\.cmake:[0-9]+ \(message\): + No INSTALL_DESTINATION given to generate_apple_platform_selection_file\(\) +Call Stack \(most recent call first\): + ApplePlatformMissingDest\.cmake:[0-9]+ \(generate_apple_platform_selection_file\) + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake new file mode 100644 index 0000000..e47b595 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake @@ -0,0 +1,4 @@ +include(CMakePackageConfigHelpers) +generate_apple_platform_selection_file(mylib-config-install.cmake + #missing: INSTALL_DESTINATION lib/cmake/mylib + ) diff --git a/Tests/RunCMake/CMakePackage/CMakeLists.txt b/Tests/RunCMake/CMakePackage/CMakeLists.txt new file mode 100644 index 0000000..6a9ce76 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake new file mode 100644 index 0000000..4145ccc --- /dev/null +++ b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake @@ -0,0 +1,110 @@ +include(RunCMake) + +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Release) +endif() + +function(apple_export platform system_name archs sysroot) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-export-${platform}-build) + string(REPLACE ";" "\\;" archs "${archs}") + if(select_archs) + string(REPLACE ";" "\\\\;" maybe_IOS_SIMULATOR_SELECT_ARCHS "-DIOS_SIMULATOR_SELECT_ARCHS=${select_archs}") + endif() + run_cmake_with_options(apple-export-${platform} + "-DCMAKE_SYSTEM_NAME=${system_name}" + "-DCMAKE_OSX_ARCHITECTURES=${archs}" + "-DCMAKE_OSX_SYSROOT=${sysroot}" + "-DCMAKE_INSTALL_PREFIX=${apple_install}" + ${maybe_CMAKE_BUILD_TYPE} + ${maybe_IOS_SIMULATOR_SELECT_ARCHS} + ) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(apple-export-${platform}-build ${CMAKE_COMMAND} --build . --config Release) + run_cmake_command(apple-export-${platform}-install ${CMAKE_COMMAND} --install . --config Release) + file(APPEND "${apple_install}/lib/${platform}/cmake/mylib/mylib-targets.cmake" "\n" + "message(STATUS \"loaded: '\${CMAKE_CURRENT_LIST_FILE}'\")\n" + ) +endfunction() + +function(apple_import platform system_name archs sysroot) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-import-${platform}-build) + string(REPLACE ";" "\\;" archs "${archs}") + run_cmake_with_options(apple-import-${platform} + "-DCMAKE_SYSTEM_NAME=${system_name}" + "-DCMAKE_OSX_ARCHITECTURES=${archs}" + "-DCMAKE_OSX_SYSROOT=${sysroot}" + "-DCMAKE_PREFIX_PATH=${apple_install}" + ${maybe_CMAKE_BUILD_TYPE} + ) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(apple-import-${platform}-build ${CMAKE_COMMAND} --build . --config Release) +endfunction() + +if(APPLE) + run_cmake(ApplePlatformMissingDest) +endif() + +if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + set(apple_install ${RunCMake_BINARY_DIR}/apple-install) + file(REMOVE_RECURSE "${apple_install}") + + if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12) + set(macos_archs "x86_64;arm64") + set(watch_sim_archs "x86_64") + set(select_archs "arm64;x86_64") + else() + set(macos_archs "x86_64") + set(watch_sim_archs "i386") + set(select_archs "") + endif() + + if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 9) + set(watch_archs "armv7k;arm64_32") + else() + set(watch_archs "armv7k") + endif() + + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # set(enable_visionos 1) + #endif() + + apple_export(macos Darwin "${macos_archs}" macosx) + apple_export(ios iOS "arm64" iphoneos) + apple_export(tvos tvOS "arm64" appletvos) + if(enable_visionos) + apple_export(visionos visionOS "arm64" xros) + endif() + apple_export(watchos watchOS "${watch_archs}" watchos) + apple_export(ios-simulator iOS "${macos_archs}" iphonesimulator) + if(select_archs) + foreach(arch IN LISTS macos_archs) + apple_export(ios-simulator-${arch} iOS "${arch}" iphonesimulator) + endforeach() + endif() + + apple_export(tvos-simulator tvOS "${macos_archs}" appletvsimulator) + if(enable_visionos) + apple_export(visionos-simulator visionOS "${macos_archs}" xrsimulator) + endif() + apple_export(watchos-simulator watchOS "${watch_sim_archs}" watchsimulator) + + apple_import(macos Darwin "${macos_archs}" macosx) + apple_import(ios iOS "arm64" iphoneos) + apple_import(tvos tvOS "arm64" appletvos) + if(enable_visionos) + apple_import(visionos visionOS "arm64" xros) + endif() + apple_import(watchos watchOS "${watch_archs}" watchos) + apple_import(ios-simulator iOS "${macos_archs}" iphonesimulator) + if(select_archs) + foreach(arch IN LISTS macos_archs) + apple_import(ios-simulator-${arch} iOS "${arch}" iphonesimulator) + endforeach() + endif() + apple_import(tvos-simulator tvOS "${macos_archs}" appletvsimulator) + if(enable_visionos) + apple_import(visionos-simulator visionOS "${macos_archs}" xrsimulator) + endif() + apple_import(watchos-simulator watchOS "${watch_sim_archs}" watchsimulator) +endif() diff --git a/Tests/RunCMake/CMakePackage/apple-common.cmake b/Tests/RunCMake/CMakePackage/apple-common.cmake new file mode 100644 index 0000000..f854f34 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-common.cmake @@ -0,0 +1,15 @@ +enable_language(C) + +if(CMAKE_SYSTEM_NAME MATCHES "^(iOS)$") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "^(tvOS|watchOS|visionOS)$") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +endif() diff --git a/Tests/RunCMake/CMakePackage/apple-export-common.cmake b/Tests/RunCMake/CMakePackage/apple-export-common.cmake new file mode 100644 index 0000000..d334f75 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-common.cmake @@ -0,0 +1,29 @@ +include(apple-common.cmake) + +add_library(mylib STATIC src/mylib.c) +target_sources(mylib PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES include/mylib.h) +install(TARGETS mylib EXPORT mylib-targets FILE_SET HEADERS ARCHIVE DESTINATION lib/${platform_name}) + +install(EXPORT mylib-targets DESTINATION lib/${platform_name}/cmake/mylib) + +if(IOS_SIMULATOR_SELECT_ARCHS) + set(IOS_SIMULATOR_CONFIG_FILE lib/ios-simulator/cmake/mylib/mylib-select-arch.cmake) +else() + set(IOS_SIMULATOR_CONFIG_FILE lib/ios-simulator/cmake/mylib/mylib-targets.cmake) +endif() + +include(CMakePackageConfigHelpers) +generate_apple_platform_selection_file(mylib-config-install.cmake + INSTALL_DESTINATION lib/cmake/mylib + INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} + MACOS_CONFIG_FILE lib/macos/cmake/mylib/mylib-targets.cmake + IOS_CONFIG_FILE lib/ios/cmake/mylib/mylib-targets.cmake + IOS_SIMULATOR_CONFIG_FILE ${IOS_SIMULATOR_CONFIG_FILE} + TVOS_CONFIG_FILE lib/tvos/cmake/mylib/mylib-targets.cmake + TVOS_SIMULATOR_CONFIG_FILE lib/tvos-simulator/cmake/mylib/mylib-targets.cmake + VISIONOS_CONFIG_FILE lib/watchos/cmake/mylib/mylib-targets.cmake + VISIONOS_SIMULATOR_CONFIG_FILE lib/watchos-simulator/cmake/mylib/mylib-targets.cmake + WATCHOS_CONFIG_FILE lib/watchos/cmake/mylib/mylib-targets.cmake + WATCHOS_SIMULATOR_CONFIG_FILE lib/watchos-simulator/cmake/mylib/mylib-targets.cmake + ) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-install.cmake DESTINATION lib/cmake/mylib RENAME mylib-config.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake new file mode 100644 index 0000000..6984df2 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake @@ -0,0 +1,2 @@ +set(platform_name ios-simulator-arm64) +include(apple-export-ios-simulator-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake new file mode 100644 index 0000000..ac09d65 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake @@ -0,0 +1,13 @@ +include(apple-export-common.cmake) + +if(IOS_SIMULATOR_SELECT_ARCHS) + generate_apple_architecture_selection_file(mylib-select-arch-install.cmake + INSTALL_DESTINATION lib/ios-simulator/cmake/mylib + INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} + SINGLE_ARCHITECTURES "${IOS_SIMULATOR_SELECT_ARCHS}" + SINGLE_ARCHITECTURE_CONFIG_FILES "lib/ios-simulator-arm64/cmake/mylib/mylib-targets.cmake;lib/ios-simulator-x86_64/cmake/mylib/mylib-targets.cmake" + UNIVERSAL_ARCHITECTURES "${IOS_SIMULATOR_SELECT_ARCHS}" + UNIVERSAL_CONFIG_FILE "lib/ios-simulator/cmake/mylib/mylib-targets.cmake" + ) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-select-arch-install.cmake DESTINATION lib/ios-simulator/cmake/mylib RENAME mylib-select-arch.cmake) +endif() diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake new file mode 100644 index 0000000..290cfa7 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake @@ -0,0 +1,2 @@ +set(platform_name ios-simulator-x86_64) +include(apple-export-ios-simulator-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake new file mode 100644 index 0000000..bc3deb3 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake @@ -0,0 +1,2 @@ +set(platform_name ios-simulator) +include(apple-export-ios-simulator-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios.cmake new file mode 100644 index 0000000..33daa40 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-ios.cmake @@ -0,0 +1,2 @@ +set(platform_name ios) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-macos.cmake b/Tests/RunCMake/CMakePackage/apple-export-macos.cmake new file mode 100644 index 0000000..d845d5c --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-macos.cmake @@ -0,0 +1,2 @@ +set(platform_name macos) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake new file mode 100644 index 0000000..d44d663 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake @@ -0,0 +1,2 @@ +set(platform_name tvos-simulator) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake b/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake new file mode 100644 index 0000000..c58144b --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake @@ -0,0 +1,2 @@ +set(platform_name tvos) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake new file mode 100644 index 0000000..e783d80 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake @@ -0,0 +1,2 @@ +set(platform_name visionos-simulator) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake b/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake new file mode 100644 index 0000000..73e1b2e --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake @@ -0,0 +1,2 @@ +set(platform_name visionos) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake new file mode 100644 index 0000000..f4f95a6 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake @@ -0,0 +1,2 @@ +set(platform_name watchos-simulator) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake b/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake new file mode 100644 index 0000000..59fc572 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake @@ -0,0 +1,2 @@ +set(platform_name watchos) +include(apple-export-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-common.cmake b/Tests/RunCMake/CMakePackage/apple-import-common.cmake new file mode 100644 index 0000000..ce79541 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-common.cmake @@ -0,0 +1,7 @@ +include(apple-common.cmake) + +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) +find_package(mylib CONFIG REQUIRED) + +add_executable(myexe src/myexe.c) +target_link_libraries(myexe PRIVATE mylib) diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt new file mode 100644 index 0000000..7999474 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator-arm64/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt new file mode 100644 index 0000000..4ca925d --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt new file mode 100644 index 0000000..ec52735 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator-x86_64/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt new file mode 100644 index 0000000..eabb96a --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-ios.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt new file mode 100644 index 0000000..2c3d87d --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/macos/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-macos.cmake b/Tests/RunCMake/CMakePackage/apple-import-macos.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-macos.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt new file mode 100644 index 0000000..544f90f --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/tvos-simulator/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt new file mode 100644 index 0000000..eccad6a --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/tvos/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake b/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt new file mode 100644 index 0000000..c4cc068 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/visionos-simulator/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt new file mode 100644 index 0000000..410eda2 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/visionos/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake b/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt new file mode 100644 index 0000000..752e2be --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/watchos-simulator/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt new file mode 100644 index 0000000..5654fe8 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt @@ -0,0 +1 @@ +loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/watchos/cmake/mylib/mylib-targets.cmake' diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake b/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake new file mode 100644 index 0000000..fa7ba53 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake @@ -0,0 +1 @@ +include(apple-import-common.cmake) diff --git a/Tests/RunCMake/CMakePackage/include/mylib.h b/Tests/RunCMake/CMakePackage/include/mylib.h new file mode 100644 index 0000000..4955e74 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/include/mylib.h @@ -0,0 +1,3 @@ +#pragma once + +extern int mylib(void); diff --git a/Tests/RunCMake/CMakePackage/src/myexe.c b/Tests/RunCMake/CMakePackage/src/myexe.c new file mode 100644 index 0000000..c1182a2 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/src/myexe.c @@ -0,0 +1,6 @@ +#include <mylib.h> /* include by angle-bracket to find installed copy */ + +int main(void) +{ + return mylib(); +} diff --git a/Tests/RunCMake/CMakePackage/src/mylib.c b/Tests/RunCMake/CMakePackage/src/mylib.c new file mode 100644 index 0000000..f4c047e --- /dev/null +++ b/Tests/RunCMake/CMakePackage/src/mylib.c @@ -0,0 +1,4 @@ +int mylib(void) +{ + return 0; +} diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index ca02b76..258f6a6 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -9,6 +9,7 @@ find_program(DEBUGEDIT debugedit) # run_cpack_test args: TEST_NAME "GENERATORS" RUN_CMAKE_BUILD_STEP "PACKAGING_TYPES" run_cpack_test(CUSTOM_BINARY_SPEC_FILE "RPM.CUSTOM_BINARY_SPEC_FILE" false "MONOLITHIC;COMPONENT") run_cpack_test(CUSTOM_NAMES "RPM.CUSTOM_NAMES;DEB.CUSTOM_NAMES;TGZ;DragNDrop" true "COMPONENT") +run_cpack_test(AUTO_SUFFIXES "RPM.AUTO_SUFFIXES;DEB.AUTO_SUFFIXES" false "MONOLITHIC") run_cpack_test(DEBUGINFO "DEB.DEBUGINFO" true "COMPONENT") if(NOT "${DEBUGEDIT}" STREQUAL "DEBUGEDIT-NOTFOUND") run_cpack_test(DEBUGINFO "RPM.DEBUGINFO" true "COMPONENT") @@ -69,3 +70,4 @@ run_cpack_test_subtests( ) run_cpack_test(PROJECT_META "RPM.PROJECT_META;DEB.PROJECT_META" false "MONOLITHIC;COMPONENT") run_cpack_test_package_target(PRE_POST_SCRIPTS "ZIP" false "MONOLITHIC;COMPONENT") +run_cpack_test_subtests(DUPLICATE_FILE "success;conflict_file;conflict_symlink" "TGZ" false "COMPONENT;GROUP") diff --git a/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake new file mode 100644 index 0000000..1ca48d8 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake @@ -0,0 +1,8 @@ +set(EXPECTED_FILES_COUNT "1") +set(EXPECTED_FILES_NAME_GENERATOR_SPECIFIC_FORMAT TRUE) +set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt") + +if(GENERATOR_TYPE STREQUAL "DEB" OR GENERATOR_TYPE STREQUAL "RPM") + string(TOLOWER "${GENERATOR_TYPE}" file_extension_) + set(EXPECTED_FILE_1 "autosuffixpackage.${file_extension_}") +endif() diff --git a/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake new file mode 100644 index 0000000..84c9bec --- /dev/null +++ b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake @@ -0,0 +1,5 @@ +install(FILES CMakeLists.txt DESTINATION foo COMPONENT test) + +# if the filename doesn't have the expected deb/rpm suffix, test that it is appended automatically +set(CPACK_DEBIAN_FILE_NAME "autosuffixpackage") +set(CPACK_RPM_FILE_NAME "autosuffixpackage") diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake new file mode 100644 index 0000000..5341ecd --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake @@ -0,0 +1,16 @@ +if(RunCMake_SUBTEST_SUFFIX STREQUAL "success") + if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(EXPECTED_FILES_COUNT "1") + set(EXPECTED_FILE_CONTENT_1_LIST "/files;/files/1.txt;/files/2.txt;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt;/files/symlink2") + elseif(PACKAGING_TYPE STREQUAL "GROUP") + set(EXPECTED_FILES_COUNT "3") + set(EXPECTED_FILE_1 "duplicate_file-0.1.1-*-g1.${cpack_archive_extension_}") + set(EXPECTED_FILE_CONTENT_1_LIST "/files;/files/1.txt;/files/2.txt;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/symlink2") + set(EXPECTED_FILE_2 "duplicate_file-0.1.1-*-g2.${cpack_archive_extension_}") + set(EXPECTED_FILE_CONTENT_2_LIST "/files;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt") + set(EXPECTED_FILE_3 "duplicate_file-0.1.1-*-c5.${cpack_archive_extension_}") + set(EXPECTED_FILE_CONTENT_3_LIST "/files;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt;/files/9.txt") + endif () +else() + set(EXPECTED_FILES_COUNT "0") +endif () diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt new file mode 100644 index 0000000..5544885 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt @@ -0,0 +1 @@ +CPack Error: ERROR The data in files with the same filename is different.* diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt new file mode 100644 index 0000000..5544885 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt @@ -0,0 +1 @@ +CPack Error: ERROR The data in files with the same filename is different.* diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake new file mode 100644 index 0000000..89d6784 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake @@ -0,0 +1,74 @@ +# Create files named 1 to 9 +foreach(i RANGE 1 9) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${i}.txt" "This is file ${i}") +endforeach() + +set(COMPONENT_NAMES c1 c2 c3 c4 c5) +foreach(j RANGE 1 5) + # Select 4 file and install to the component + math(EXPR COMPONENT_IDX "${j} - 1") + list(GET COMPONENT_NAMES "${COMPONENT_IDX}" SELECTED_COMPONENT) + math(EXPR END_FILE "${j} + 4") + foreach(k RANGE ${j} ${END_FILE}) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${k}.txt" DESTINATION "files" COMPONENT ${SELECTED_COMPONENT}) + endforeach() +endforeach() + +if(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_file") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/conflict/1.txt" "This should create a conflict.") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/1.txt" DESTINATION "files" COMPONENT c2) +endif () + +# You cannot create symlink in Windows test environment. Instead mock the symlink. +if(NOT CMAKE_HOST_WIN32) + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/2.txt" "${CMAKE_CURRENT_BINARY_DIR}/symlink2" SYMBOLIC) +else() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/symlink2" "This is file 2") +endif() +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/symlink2" DESTINATION "files" COMPONENT c1) + +if(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_symlink" AND NOT CMAKE_HOST_WIN32) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/conflict) + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/1.txt" "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" SYMBOLIC) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" DESTINATION "files" COMPONENT c2) +elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_symlink" AND CMAKE_HOST_WIN32) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" "This should create a conflict.") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" DESTINATION "files" COMPONENT c2) +else() + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/symlink2" DESTINATION "files" COMPONENT c2) +endif () + + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE ON) + set(CPACK_COMPONENTS_ALL "c1;c2;c3;c4") +elseif(PACKAGING_TYPE STREQUAL "GROUP") + set(CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP ON) + set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) + include(CPackComponent) + + cpack_add_component_group(g1 DISPLAY_NAME "Group 1") + cpack_add_component_group(g2 DISPLAY_NAME "Group 2") + cpack_add_component(c1 + DISPLAY_NAME "Group 1" + DESCRIPTION "Component for Group 1" + GROUP g1 + ) + cpack_add_component(c2 + DISPLAY_NAME "Group 1" + DESCRIPTION "Component for Group 1" + GROUP g1 + ) + cpack_add_component(c3 + DISPLAY_NAME "Group 2" + DESCRIPTION "Component for Group 2" + GROUP g2 + ) + cpack_add_component(c4 + DISPLAY_NAME "Group 2" + DESCRIPTION "Component for Group 2" + GROUP g2 + ) + + set(CPACK_${GENERATOR_TYPE}_PACKAGE_GROUP g1 g2) +endif () diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index 223a61c..1b8d0d9 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake @@ -229,19 +229,22 @@ function(run_TestLoad name load) file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " add_test(TestLoad1 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\") + set_tests_properties(TestLoad1 PROPERTIES PROCESSORS 2) add_test(TestLoad2 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\") + set_tests_properties(TestLoad2 PROPERTIES PROCESSORS 2) ") - run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j2 --test-load ${load}) + run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j8 --test-load ${load}) endfunction() # Tests for the --test-load feature of ctest # # Spoof a load average value to make these tests more reliable. -set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5) +set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7) # Verify that new tests are not started when the load average exceeds # our threshold and that they then run once the load average drops. -run_TestLoad(test-load-wait 3) +run_TestLoad(test-load-wait0 5) +run_TestLoad(test-load-wait1 8) # Verify that warning message is displayed but tests still start when # an invalid argument is given. @@ -249,7 +252,7 @@ run_TestLoad(test-load-invalid 'two') # Verify that new tests are started when the load average falls below # our threshold. -run_TestLoad(test-load-pass 10) +run_TestLoad(test-load-pass 12) unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING}) diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt index db7d7f3..d112fde 100644 --- a/Tests/RunCMake/CTestCommandLine/test-load-wait-stdout.txt +++ b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt @@ -2,7 +2,7 @@ Test project [^ ]*/Tests/RunCMake/CTestCommandLine/TestLoad( [^*][^ ]*)* -\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 3, Smallest test TestLoad[1-2] requires 1\*\*\*\*\* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 5\*\*\*\*\* test 1 Start 1: TestLoad1 +( diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt new file mode 100644 index 0000000..aa91950 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt @@ -0,0 +1,21 @@ +Test project [^ +]*/Tests/RunCMake/CTestCommandLine/TestLoad( +[^*][^ +]*)* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test TestLoad[1-2] requires 2\*\*\*\*\* +test 1 + Start 1: TestLoad1 ++( +[^*][^ +]*)* +test 2 + Start 2: TestLoad2 ++( +[^*][^ +]*)* +1/2 Test #[1-2]: TestLoad[1-2] ........................ Passed +[0-9.]+ sec( +[^*][^ +]*)* +2/2 Test #[1-2]: TestLoad[1-2] ........................ Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 2 diff --git a/Tests/RunCMake/CXXModules/sources/c-anchor.c b/Tests/RunCMake/CXXModules/sources/c-anchor.c index c782188..8adfcbb 100644 --- a/Tests/RunCMake/CXXModules/sources/c-anchor.c +++ b/Tests/RunCMake/CXXModules/sources/c-anchor.c @@ -1,4 +1,4 @@ -int c_anchor() +int c_anchor(void) { return 0; } diff --git a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake index 23b3006..c85783d 100644 --- a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake +++ b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0157 NEW) enable_language (Swift) include(CheckCompilerFlag) diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake index 767fa69..8edff2c 100644 --- a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake +++ b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0157 NEW) enable_language(Swift) include(CheckSourceCompiles) diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index c01f414..2bbe1c8 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt @@ -1 +1 @@ -^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":6}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ +^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":7}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt new file mode 100644 index 0000000..8210d59 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt @@ -0,0 +1 @@ +^Hello world$ diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin.cmake b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake new file mode 100644 index 0000000..e83e619 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake @@ -0,0 +1,10 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" "ell") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" "rld") +execute_process( + COMMAND ${CMAKE_COMMAND} -E echo_append "H" + COMMAND ${CMAKE_COMMAND} -E cat - + ) +execute_process( + COMMAND ${CMAKE_COMMAND} -E echo_append "o wo" + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" - "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" + ) diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index b29e50a..03b9301 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -782,6 +782,8 @@ run_cmake_command(E_cat-without-double-dash ${CMAKE_COMMAND} -E cat "-file-start unset(RunCMake_TEST_COMMAND_WORKING_DIRECTORY) unset(out) +run_cmake_command(E_cat-stdin ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_cat-stdin.cmake) + # Unset environment variables that are used for testing cmake -E unset(ENV{TEST_ENV}) unset(ENV{TEST_ENV_EXPECTED}) diff --git a/Tests/RunCMake/CompileDefinitions/foo.c b/Tests/RunCMake/CompileDefinitions/foo.c index 74a86e1..7d75e37 100644 --- a/Tests/RunCMake/CompileDefinitions/foo.c +++ b/Tests/RunCMake/CompileDefinitions/foo.c @@ -1,4 +1,4 @@ -void foo() +void foo(void) { } diff --git a/Tests/RunCMake/CompileWarningAsError/warn.c b/Tests/RunCMake/CompileWarningAsError/warn.c index cd0c2ba..4041167 100644 --- a/Tests/RunCMake/CompileWarningAsError/warn.c +++ b/Tests/RunCMake/CompileWarningAsError/warn.c @@ -1,4 +1,4 @@ -static void unused_function(); +static void unused_function(void); #ifdef __SUNPRO_C KandR(x) int x; diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt index b7db7eb..bc5d9ea 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerC.cmake:2 \(enable_language\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the JOM generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt index 03c5933..80d8a21 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerC.cmake:2 \(enable_language\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the NMake generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt index c98842d..96d02de 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerC.cmake:2 \(enable_language\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? Tell CMake where to find the compiler by setting either the environment variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt index 4b42ea6..d108787 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCXX.cmake:2 \(enable_language\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the JOM generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt index 1bfcdcc..d3c0ccd 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCXX.cmake:2 \(enable_language\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the NMake generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt index 7ef4f5e..79db41d 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCXX.cmake:2 \(enable_language\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? Tell CMake where to find the compiler by setting either the environment variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt index f25a267..360a8c2 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the JOM generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is @@ -21,7 +22,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the JOM generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt index ffcdce8..8438d0e 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the NMake generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is @@ -21,7 +22,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? To use the NMake generator with Visual C\+\+, cmake must be run from a shell that can use the compiler cl from the command line. This environment is diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt index eecff54..cba0db2 100644 --- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt +++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt @@ -3,7 +3,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-C-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? Tell CMake where to find the compiler by setting either the environment variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to @@ -16,7 +17,8 @@ CMake Error at BadCompilerCandCXX.cmake:3 \(project\): no-CXX-compiler - is not a full path and was not found in the PATH. + is not a full path and was not found in the PATH.( Perhaps the extension is + missing\?)? Tell CMake where to find the compiler by setting either the environment variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path diff --git a/Tests/RunCMake/Configure/ReadOnly-result.txt b/Tests/RunCMake/Configure/ReadOnly-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/Configure/ReadOnly-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Configure/ReadOnly-stderr.txt b/Tests/RunCMake/Configure/ReadOnly-stderr.txt new file mode 100644 index 0000000..fb00ae9 --- /dev/null +++ b/Tests/RunCMake/Configure/ReadOnly-stderr.txt @@ -0,0 +1,8 @@ +CMake Error: Unable to \(re\)create the private pkgRedirects directory: + [^ +]*/Tests/RunCMake/Configure/ReadOnly-build/CMakeFiles/pkgRedirects +This may be caused by not having read/write access to the build directory. +Try specifying a location with read/write access like: + cmake -B build +If using a CMake presets file, ensure that preset parameter +'binaryDir' expands to a writable directory. diff --git a/Tests/RunCMake/Configure/ReadOnly.cmake b/Tests/RunCMake/Configure/ReadOnly.cmake new file mode 100644 index 0000000..2fc38e5 --- /dev/null +++ b/Tests/RunCMake/Configure/ReadOnly.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake index df6849e..842a005 100644 --- a/Tests/RunCMake/Configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake @@ -55,3 +55,19 @@ if(NOT RunCMake_GENERATOR MATCHES "^Ninja Multi-Config$") run_cmake(NoCMAKE_DEFAULT_BUILD_TYPE) run_cmake(NoCMAKE_DEFAULT_CONFIGS) endif() + +if(NOT CMAKE_HOST_WIN32) + block() + # Test a non-writable build directory. + # Exclude when running as root because directories are always writable. + get_unix_uid(uid) + if(NOT uid STREQUAL "0") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ReadOnly-build) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(CHMOD "${RunCMake_TEST_BINARY_DIR}" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake(ReadOnly) + endif() + endblock() +endif() diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake new file mode 100644 index 0000000..8a6a702 --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake @@ -0,0 +1,28 @@ +set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake") +if(EXISTS "${testfile}") + file(READ "${testfile}" testfile_contents) +else() + message(FATAL_ERROR "Could not find expected CTestTestfile.cmake.") +endif() + +set(error_details "There is a problem with generated test file: ${testfile}") + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]ShouldNotUseEmulator [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake new file mode 100644 index 0000000..9f9547c --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake @@ -0,0 +1,25 @@ +if(CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "cross compiling") +endif() + +cmake_policy(SET CMP0158 NEW) + +enable_testing() +add_test(NAME DoesNotUseEmulator + COMMAND ${CMAKE_COMMAND} -E echo "Hi") + +add_executable(generated_exe simple_src_exiterror.cxx) + +add_test(NAME ShouldNotUseEmulator + COMMAND generated_exe) + +add_test(NAME DoesNotUseEmulatorWithGenex + COMMAND $<TARGET_FILE:generated_exe>) + +add_subdirectory(AddTest) + +add_test(NAME ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex + COMMAND generated_exe_in_subdir_added_to_test_without_genex) + +add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex + COMMAND $<TARGET_FILE:generated_exe_in_subdir_added_to_test_with_genex>) diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake new file mode 100644 index 0000000..588b77b --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake @@ -0,0 +1,28 @@ +set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake") +if(EXISTS "${testfile}") + file(READ "${testfile}" testfile_contents) +else() + message(FATAL_ERROR "Could not find expected CTestTestfile.cmake.") +endif() + +set(error_details "There is a problem with generated test file: ${testfile}") + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() + +if(NOT testfile_contents MATCHES "add_test[(]UsesEmulator [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Did not use emulator when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() + +if(NOT testfile_contents MATCHES "add_test[(]UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Did not use emulator when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^\n]+pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") +endif() diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake new file mode 100644 index 0000000..910f1b4 --- /dev/null +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake @@ -0,0 +1,25 @@ +if(CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "cross compiling") +endif() + +cmake_policy(SET CMP0158 OLD) + +enable_testing() +add_test(NAME DoesNotUseEmulator + COMMAND ${CMAKE_COMMAND} -E echo "Hi") + +add_executable(generated_exe simple_src_exiterror.cxx) + +add_test(NAME UsesEmulator + COMMAND generated_exe) + +add_test(NAME DoesNotUseEmulatorWithGenex + COMMAND $<TARGET_FILE:generated_exe>) + +add_subdirectory(AddTest) + +add_test(NAME UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex + COMMAND generated_exe_in_subdir_added_to_test_without_genex) + +add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex + COMMAND $<TARGET_FILE:generated_exe_in_subdir_added_to_test_with_genex>) diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake index 588b77b..459e922 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake @@ -26,3 +26,7 @@ endif() if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^\n]+pseudo_emulator[^\n]+\n") message(SEND_ERROR "Used emulator when it should not be used. ${error_details}") endif() + +if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncherAndEmulator[^\n]+pseudo_test_launcher.*pseudo_emulator[^\n]+\n") + message(SEND_ERROR "Did not use test launcher and emulator when they should be used. ${error_details}") +endif() diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake index 23e2e8d..4bcb2cf 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake +++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake @@ -18,3 +18,9 @@ add_test(NAME UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex COMMAND $<TARGET_FILE:generated_exe_in_subdir_added_to_test_with_genex>) + +add_executable(generated_exe_test_launcher simple_src_exiterror.cxx) +set_property(TARGET generated_exe_test_launcher PROPERTY TEST_LAUNCHER "pseudo_test_launcher") + +add_test(NAME UsesTestLauncherAndEmulator + COMMAND generated_exe_test_launcher) diff --git a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake index 1ffd91c..c595f1a 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake +++ b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake @@ -6,6 +6,8 @@ set(RunCMake_TEST_OPTIONS run_cmake(CrosscompilingEmulatorProperty) run_cmake(TryRun) run_cmake(AddTest) +run_cmake(AddTest-CMP0158-OLD) +run_cmake(AddTest-CMP0158-NEW) function(CustomCommandGenerator_run_and_build case) # Use a single build tree for a few tests without cleaning. diff --git a/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt b/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt new file mode 100644 index 0000000..53e554b --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt @@ -0,0 +1,6 @@ +-- Before cmake_language +-- After cmake_language +-- Intercepted FetchContent_MakeAvailable\(SomeDep\) +-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/ProviderFirst-build/_deps/somedep-build;EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR;SOURCE_SUBDIR;DoesNotExist;FIND_PACKAGE_ARGS;QUIET;NO_DEFAULT_PATH;COMPONENTS;abc +-- FetchContent_MakeAvailable\(\) succeeded +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake b/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake new file mode 100644 index 0000000..b27c841 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake @@ -0,0 +1,8 @@ +include(FetchContent) +FetchContent_Declare(SomeDep + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} + SOURCE_SUBDIR DoesNotExist + FIND_PACKAGE_ARGS NO_DEFAULT_PATH COMPONENTS abc +) +FetchContent_MakeAvailable(SomeDep) +message(STATUS "FetchContent_MakeAvailable() succeeded") diff --git a/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake index 42893d2..300bbc1 100644 --- a/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake +++ b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake @@ -61,6 +61,11 @@ run_cmake_with_options(RedirectFetchContentSerial -D "provider_command=redirect_FetchContentSerial_provider" -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL" ) +run_cmake_with_options(ProviderFirst + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=FetchContentSerial_provider" + -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL" +) run_cmake_with_options(Bypass -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" -D "provider_command=forward_find_package" diff --git a/Tests/RunCMake/ExcludeFromAll/main.c b/Tests/RunCMake/ExcludeFromAll/main.c index f8b643a..8488f4e 100644 --- a/Tests/RunCMake/ExcludeFromAll/main.c +++ b/Tests/RunCMake/ExcludeFromAll/main.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake b/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake new file mode 100644 index 0000000..e6f4e16 --- /dev/null +++ b/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake @@ -0,0 +1,6 @@ +if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue") + message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n SomeOtherValue\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") +endif() + +set(fp_called TRUE) +set(FDE-S_FOUND TRUE) diff --git a/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake b/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake new file mode 100644 index 0000000..ef91cc3 --- /dev/null +++ b/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake @@ -0,0 +1,6 @@ +if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME) + message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should not be defined") +endif() + +set(fp_called TRUE) +set(FDE-U_FOUND TRUE) diff --git a/Tests/RunCMake/FetchContent/FindDependencyExport.cmake b/Tests/RunCMake/FetchContent/FindDependencyExport.cmake new file mode 100644 index 0000000..c79c357 --- /dev/null +++ b/Tests/RunCMake/FetchContent/FindDependencyExport.cmake @@ -0,0 +1,78 @@ +include(FetchContent) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +unset(dp_called) +unset(fp_called) +set(_expected_export_find_package_name_dp FDE-U) +FetchContent_Declare( + FDE-U + FIND_PACKAGE_ARGS + ) +FetchContent_MakeAvailable(FDE-U) + +if(NOT dp_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call dependency provider") +endif() +if(NOT fp_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call find_package()") +endif() + +if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME) + message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should have been unset after FetchContent_MakeAvailable().\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") +endif() + +unset(sub_called) +set(_expected_export_find_package_name_dp FDE-U-Sub) +set(_expected_export_find_package_name_sub FDE-U-Sub) +FetchContent_Declare( + FDE-U-Sub + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/FindDependencyExport + ) +FetchContent_MakeAvailable(FDE-U-Sub) + +if(NOT sub_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call add_subdirectory()") +endif() + +if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME) + message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should have been unset after FetchContent_MakeAvailable()") +endif() + +unset(dp_called) +unset(fp_called) +set(CMAKE_EXPORT_FIND_PACKAGE_NAME SomeOtherValue) +set(_expected_export_find_package_name_dp FDE-S) +FetchContent_Declare( + FDE-S + FIND_PACKAGE_ARGS + ) +FetchContent_MakeAvailable(FDE-S) + +if(NOT dp_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call dependency provider") +endif() +if(NOT fp_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call find_package()") +endif() + +if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue") + message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n SomeOtherValue\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") +endif() + +unset(sub_called) +set(_expected_export_find_package_name_dp FDE-S-Sub) +set(_expected_export_find_package_name_sub FDE-S-Sub) +FetchContent_Declare( + FDE-S-Sub + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/FindDependencyExport + ) +FetchContent_MakeAvailable(FDE-S-Sub) + +if(NOT sub_called) + message(FATAL_ERROR "FetchContent_MakeAvailable did not call add_subdirectory()") +endif() + +if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue") + message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n SomeOtherValue\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") +endif() diff --git a/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt b/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt new file mode 100644 index 0000000..8bd4036 --- /dev/null +++ b/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt @@ -0,0 +1,5 @@ +if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL _expected_export_find_package_name_sub) + message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n ${_expected_export_find_package_name_sub}\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") +endif() + +set(sub_called TRUE PARENT_SCOPE) diff --git a/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake b/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake new file mode 100644 index 0000000..7cabe51 --- /dev/null +++ b/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake @@ -0,0 +1,11 @@ +function(fde_provide_dependency method name) + if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL _expected_export_find_package_name_dp) + message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n ${_expected_export_find_package_name_dp}\nActual value:\n ${CMAKE_EXPORT_FIND_PACKAGE_NAME}") + endif() + + set(dp_called TRUE PARENT_SCOPE) +endfunction() + +cmake_language(SET_DEPENDENCY_PROVIDER fde_provide_dependency + SUPPORTED_METHODS FETCHCONTENT_MAKEAVAILABLE_SERIAL + ) diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake index d0790eb..0f443a7 100644 --- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake +++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake @@ -19,6 +19,10 @@ run_cmake(MakeAvailableTwice) run_cmake(MakeAvailableUndeclared) run_cmake(VerifyHeaderSet) +run_cmake_with_options(FindDependencyExport + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${CMAKE_CURRENT_LIST_DIR}/FindDependencyExportDP.cmake" +) + run_cmake_with_options(ManualSourceDirectory -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/WithProject" ) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 807d92b..5f41ad9 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -12,7 +12,7 @@ def read_codemodel_json_data(filename): def check_objects(o, g): assert is_list(o) assert len(o) == 1 - check_index_object(o[0], "codemodel", 2, 6, check_object_codemodel(g)) + check_index_object(o[0], "codemodel", 2, 7, check_object_codemodel(g)) def check_backtrace(t, b, backtrace): btg = t["backtraceGraph"] @@ -405,6 +405,30 @@ def check_target(c): missing_exception=lambda e: "Install path: %s" % e["path"], extra_exception=lambda a: "Install path: %s" % a["path"]) + if "launchers" in expected: + if expected["launchers"] is not None: + expected_keys.append("launchers") + def check_launcher(actual, expected): + assert is_dict(actual) + launcher_keys = ["command", "type"] + if "arguments" in expected: + launcher_keys.append("arguments") + assert sorted(actual.keys()) == sorted(launcher_keys) + assert matches(actual["command"], expected["command"]) + assert matches(actual["type"], expected["type"]) + if "arguments" in expected: + if expected["arguments"] is not None: + check_list_match(lambda a, e: matches(a, e), + actual["arguments"], expected["arguments"], + missing_exception=lambda e: "argument: %s" % e, + extra_exception=lambda a: "argument: %s" % actual["arguments"]) + check_list_match(lambda a, e: matches(a["type"], e["type"]), + obj["launchers"], expected["launchers"], + check=check_launcher, + check_exception=lambda a, e: "launchers: %s" % a, + missing_exception=lambda e: "launchers: %s" % e, + extra_exception=lambda a: "launchers: %s" % a) + if expected["link"] is not None: expected_keys.append("link") assert is_dict(obj["link"]) @@ -709,6 +733,7 @@ def gen_check_directories(c, g): read_codemodel_json_data("directories/alias.json"), read_codemodel_json_data("directories/custom.json"), read_codemodel_json_data("directories/cxx.json"), + read_codemodel_json_data("directories/cxx.cross.json"), read_codemodel_json_data("directories/imported.json"), read_codemodel_json_data("directories/interface.json"), read_codemodel_json_data("directories/object.json"), @@ -782,6 +807,10 @@ def gen_check_targets(c, g, inSource): read_codemodel_json_data("targets/zero_check_cxx.json"), read_codemodel_json_data("targets/cxx_lib.json"), read_codemodel_json_data("targets/cxx_exe.json"), + read_codemodel_json_data("targets/cxx_exe_cross_emulator.json"), + read_codemodel_json_data("targets/cxx_exe_cross_emulator_args.json"), + read_codemodel_json_data("targets/cxx_exe_test_launcher_and_cross_emulator.json"), + read_codemodel_json_data("targets/cxx_exe_test_launcher.json"), read_codemodel_json_data("targets/cxx_standard_compile_feature_exe.json"), read_codemodel_json_data("targets/cxx_standard_exe.json"), read_codemodel_json_data("targets/cxx_shared_lib.json"), diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json new file mode 100644 index 0000000..8f6ded5 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json @@ -0,0 +1,16 @@ +{ + "source": "^cxx/cross$", + "build": "^cxx/cross$", + "parentSource": "^cxx$", + "parentIndex": 2, + "childSources": null, + "targetIds": [ + "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$", + "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$", + "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$" + ], + "projectName": "Cxx", + "minimumCMakeVersion": "3.13", + "hasInstallRule": null, + "installers": [] +} diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json index 22dfabd..912d664 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json @@ -2,11 +2,14 @@ "source": "^cxx$", "build": "^cxx$", "parentSource": "^\\.$", - "childSources": null, + "childSources": [ + "^cxx/cross$" + ], "targetIds": [ "^ALL_BUILD::@a56b12a3f5c0529fb296$", "^ZERO_CHECK::@a56b12a3f5c0529fb296$", "^cxx_exe::@a56b12a3f5c0529fb296$", + "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$", "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$", "^cxx_standard_exe::@a56b12a3f5c0529fb296$", "^cxx_lib::@a56b12a3f5c0529fb296$", diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json index 363e853..c8a1a83 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json @@ -3,13 +3,18 @@ "parentName": "codemodel-v2", "childNames": null, "directorySources": [ - "^cxx$" + "^cxx$", + "^cxx/cross$" ], "targetIds": [ "^ALL_BUILD::@a56b12a3f5c0529fb296$", "^ZERO_CHECK::@a56b12a3f5c0529fb296$", "^cxx_lib::@a56b12a3f5c0529fb296$", "^cxx_exe::@a56b12a3f5c0529fb296$", + "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$", + "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$", + "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$", + "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$", "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$", "^cxx_standard_exe::@a56b12a3f5c0529fb296$", "^cxx_shared_lib::@a56b12a3f5c0529fb296$", diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json index bf36bfe..3a83d29 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json @@ -83,6 +83,22 @@ "backtrace": null }, { + "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$", + "backtrace": null + }, + { "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$", "backtrace": null }, diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json index 9d0007f..ec72eb8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json @@ -123,6 +123,22 @@ "backtrace": null }, { + "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$", + "backtrace": null + }, + { + "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$", + "backtrace": null + }, + { "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$", "backtrace": null }, diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json new file mode 100644 index 0000000..bbd973b --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json @@ -0,0 +1,105 @@ +{ + "name": "cxx_exe_cross_emulator", + "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$", + "directorySource": "^cxx/cross$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": null, + "fileSets": null, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": null, + "fileSetName": null, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 3, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$" + ] + } + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$" + ], + "includes": null, + "frameworks": null, + "defines": null, + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 3, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^cxx_exe_cross_emulator(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator(\\.exe)?$", + "_dllExtra": false + }, + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator\\.pdb$", + "_dllExtra": true + } + ], + "build": "^cxx/cross$", + "source": "^cxx/cross$", + "install": null, + "launchers" : [ + { + "command": "^no-such-emulator(\\.exe)?$", + "type" : "emulator" + } + ], + "link": { + "language": "CXX", + "lto": null, + "commandFragments": [ + { + "fragment" : ".*", + "role" : "flags", + "backtrace": null + } + ] + }, + "archive": null, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": null + } + ] +} diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json new file mode 100644 index 0000000..c1a8b0c --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json @@ -0,0 +1,109 @@ +{ + "name": "cxx_exe_cross_emulator_args", + "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$", + "directorySource": "^cxx/cross$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": null, + "fileSets": null, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": null, + "fileSetName": null, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 6, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$" + ] + } + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$" + ], + "includes": null, + "frameworks": null, + "defines": null, + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 6, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^cxx_exe_cross_emulator_args(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator_args(\\.exe)?$", + "_dllExtra": false + }, + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator_args\\.pdb$", + "_dllExtra": true + } + ], + "build": "^cxx/cross$", + "source": "^cxx/cross$", + "install": null, + "launchers" : [ + { + "arguments" : [ + "arg1", + "arg2 with space" + ], + "command": "^no-such-emulator(\\.exe)?$", + "type" : "emulator" + } + ], + "link": { + "language": "CXX", + "lto": null, + "commandFragments": [ + { + "fragment" : ".*", + "role" : "flags", + "backtrace": null + } + ] + }, + "archive": null, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": null + } + ] +} diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json new file mode 100644 index 0000000..9002368 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json @@ -0,0 +1,105 @@ +{ + "name": "cxx_exe_test_launcher", + "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": null, + "fileSets": null, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": null, + "fileSetName": null, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 49, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$" + ] + } + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$" + ], + "includes": null, + "frameworks": null, + "defines": null, + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 49, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^cxx_exe_test_launcher(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher(\\.exe)?$", + "_dllExtra": false + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher\\.pdb$", + "_dllExtra": true + } + ], + "build": "^cxx$", + "source": "^cxx$", + "install": null, + "launchers" : [ + { + "command": "^no-such-launcher(\\.exe)?$", + "type" : "test" + } + ], + "link": { + "language": "CXX", + "lto": null, + "commandFragments": [ + { + "fragment" : ".*", + "role" : "flags", + "backtrace": null + } + ] + }, + "archive": null, + "dependencies": [ + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": null + } + ] +} diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json new file mode 100644 index 0000000..06e7a7b --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json @@ -0,0 +1,109 @@ +{ + "name": "cxx_exe_test_launcher_and_cross_emulator", + "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$", + "directorySource": "^cxx/cross$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": null, + "fileSets": null, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": null, + "fileSetName": null, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$" + ] + } + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$" + ], + "includes": null, + "frameworks": null, + "defines": null, + "compileCommandFragments": null + } + ], + "backtrace": [ + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": true + }, + { + "file": "^cxx/cross/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^cxx_exe_test_launcher_and_cross_emulator(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher_and_cross_emulator(\\.exe)?$", + "_dllExtra": false + }, + { + "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher_and_cross_emulator\\.pdb$", + "_dllExtra": true + } + ], + "build": "^cxx/cross$", + "source": "^cxx/cross$", + "install": null, + "launchers" : [ + { + "command": "^no-such-launcher(\\.exe)?$", + "type" : "test" + }, + { + "command": "^no-such-emulator(\\.exe)?$", + "type" : "emulator" + } + ], + "link": { + "language": "CXX", + "lto": null, + "commandFragments": [ + { + "fragment" : ".*", + "role" : "flags", + "backtrace": null + } + ] + }, + "archive": null, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": null + } + ] +} diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt index 3ae3b60..c465297 100644 --- a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt +++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt @@ -45,3 +45,8 @@ if(_rdeps) FRAMEWORK DESTINATION fw ) endif() + +add_executable(cxx_exe_test_launcher ../empty.cxx) +set_property(TARGET cxx_exe_test_launcher PROPERTY TEST_LAUNCHER no-such-launcher) + +add_subdirectory(cross) diff --git a/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt new file mode 100644 index 0000000..2f2594f --- /dev/null +++ b/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt @@ -0,0 +1,11 @@ +# Cross-compiling is normally global. Cover it without duplicating everything. +set(CMAKE_CROSSCOMPILING 1) +add_executable(cxx_exe_cross_emulator ../../empty.cxx) +set_property(TARGET cxx_exe_cross_emulator PROPERTY CROSSCOMPILING_EMULATOR no-such-emulator) + +add_executable(cxx_exe_cross_emulator_args ../../empty.cxx) +set_property(TARGET cxx_exe_cross_emulator_args PROPERTY CROSSCOMPILING_EMULATOR "no-such-emulator;arg1;arg2 with space") + +add_executable(cxx_exe_test_launcher_and_cross_emulator ../../empty.cxx) +set_property(TARGET cxx_exe_test_launcher_and_cross_emulator PROPERTY TEST_LAUNCHER "no-such-launcher") +set_property(TARGET cxx_exe_test_launcher_and_cross_emulator PROPERTY CROSSCOMPILING_EMULATOR "no-such-emulator") diff --git a/Tests/RunCMake/File_Generate/empty.c b/Tests/RunCMake/File_Generate/empty.c index f097d0a..05ffe2d 100644 --- a/Tests/RunCMake/File_Generate/empty.c +++ b/Tests/RunCMake/File_Generate/empty.c @@ -2,7 +2,7 @@ #ifdef _WIN32 __declspec(dllexport) #endif - int empty_c() + int empty_c(void) { return 0; } diff --git a/Tests/RunCMake/Framework/consumer.c b/Tests/RunCMake/Framework/consumer.c index a578976..4208a73 100644 --- a/Tests/RunCMake/Framework/consumer.c +++ b/Tests/RunCMake/Framework/consumer.c @@ -1,7 +1,7 @@ #include <Gui2/Gui.h> -int consumer() +int consumer(void) { foo(); diff --git a/Tests/RunCMake/Framework/foo.c b/Tests/RunCMake/Framework/foo.c index b85b60d..6c1d727 100644 --- a/Tests/RunCMake/Framework/foo.c +++ b/Tests/RunCMake/Framework/foo.c @@ -1,4 +1,4 @@ -int foo() +int foo(void) { return 42; } diff --git a/Tests/RunCMake/Framework/foo.h b/Tests/RunCMake/Framework/foo.h index 5d5f8f0..5a2ca62 100644 --- a/Tests/RunCMake/Framework/foo.h +++ b/Tests/RunCMake/Framework/foo.h @@ -1 +1 @@ -int foo(); +int foo(void); diff --git a/Tests/RunCMake/Framework/main.c b/Tests/RunCMake/Framework/main.c index fc09922..74ae236 100644 --- a/Tests/RunCMake/Framework/main.c +++ b/Tests/RunCMake/Framework/main.c @@ -1,7 +1,7 @@ #include <Gui/Gui.h> -int main() +int main(void) { foo(); diff --git a/Tests/RunCMake/Framework/main2.c b/Tests/RunCMake/Framework/main2.c index 11f4e4d..26908d7 100644 --- a/Tests/RunCMake/Framework/main2.c +++ b/Tests/RunCMake/Framework/main2.c @@ -1,7 +1,7 @@ #include <Gui2/Gui.h> -int main() +int main(void) { foo(); diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt new file mode 100644 index 0000000..acb13da --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt @@ -0,0 +1,11 @@ +CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + [^ +]* + + given toolset + + fortran=bad + + but the value is not "ifx" or "ifort"\. diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake new file mode 100644 index 0000000..2fc38e5 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake index 71cc2d4..b86c481 100644 --- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake @@ -43,6 +43,12 @@ if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[0124567]") set(RunCMake_GENERATOR_TOOLSET "${VsNormal_Toolset},customFlagTableDir=does_not_exist") run_cmake(BadToolsetCustomFlagTableDir) endif() + set(RunCMake_GENERATOR_TOOLSET "fortran=ifort") + run_cmake(TestToolsetFortranIFORT) + set(RunCMake_GENERATOR_TOOLSET "fortran=ifx") + run_cmake(TestToolsetFortranIFX) + set(RunCMake_GENERATOR_TOOLSET "fortran=bad") + run_cmake(BadToolsetFortran) if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[24567]") set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64") run_cmake(TestToolsetHostArchBoth) diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt new file mode 100644 index 0000000..9fa8c21 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt @@ -0,0 +1 @@ +-- CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='ifort' diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake new file mode 100644 index 0000000..7f18fbc --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake @@ -0,0 +1 @@ +message(STATUS "CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}'") diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt new file mode 100644 index 0000000..56a0c30 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt @@ -0,0 +1 @@ +-- CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='ifx' diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake new file mode 100644 index 0000000..7f18fbc --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake @@ -0,0 +1 @@ +message(STATUS "CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}'") diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt index 3a6572c..84bbe9f 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt @@ -2,6 +2,7 @@ ( *|[0-9]+>) Error running test executable. ?( *|[0-9]+>) ( *|[0-9]+>) Path: '.*discovery_timeout_test(\.exe)?' +( *|[0-9]+>) Working directory: '[^']*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout' ( *|[0-9]+>) Result: Process terminated due to timeout ( *|[0-9]+>) Output: ( *|[0-9]+>) + diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt index 75afe4a..03d73ee 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt @@ -2,6 +2,7 @@ CMake Error at .*GoogleTestAddTests.cmake:[0-9]+ \(message\): [ \t]*Error running test executable. + [ \t]*Path: '.*discovery_timeout_test(\.exe)?' +[ \t]*Working directory: '[^']*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout' [ \t]*Result: Process terminated due to timeout [ \t]*Output: [ \t]*timeout. diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake new file mode 100644 index 0000000..d65fae8 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake @@ -0,0 +1,13 @@ +enable_language(CXX) + +# 'GoogleTest' module is NOT included here by design to validate that including +# it in a subdirectory will still result in test discovery working correctly if +# 'gtest_discover_tests()' is invoked from a different scope. + +enable_testing() + +include(xcode_sign_adhoc.cmake) + +add_subdirectory(GoogleTestDiscoveryTestListScoped) + +add_gtest_executable(test_list_scoped_test test_list_test.cpp) diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt new file mode 100644 index 0000000..762a537 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt @@ -0,0 +1,13 @@ +# This file mimics one containing GoogleTest-related helper functions. It +# includes the GoogleTest module that (in this test) is NOT included in the +# top-level CMake file. + +include(GoogleTest) + +function(add_gtest_executable TARGET SOURCE) + add_executable(${TARGET} ${SOURCE}) + xcode_sign_adhoc(${TARGET}) + gtest_discover_tests( + ${TARGET} + ) +endfunction() diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index b494cef..56bbfc9 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake @@ -292,6 +292,32 @@ function(run_GoogleTest_discovery_flush_script DISCOVERY_MODE) ) endfunction() +function(run_GoogleTest_discovery_test_list_scoped DISCOVERY_MODE) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-test-list-scoped-build) + set(RunCMake_TEST_NO_CLEAN 1) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + run_cmake_with_options(GoogleTestDiscoveryTestListScoped -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE}) + + run_cmake_command(GoogleTest-discovery-test-list-scoped-build + ${CMAKE_COMMAND} + --build . + --config Debug + --target test_list_scoped_test + ) + + run_cmake_command(GoogleTest-discovery-test-list-scoped-test + ${CMAKE_CTEST_COMMAND} + -C Debug + --no-label-summary + ) +endfunction() + foreach(DISCOVERY_MODE POST_BUILD PRE_TEST) message("Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...") run_GoogleTest(${DISCOVERY_MODE}) @@ -303,6 +329,7 @@ foreach(DISCOVERY_MODE POST_BUILD PRE_TEST) run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE}) endif() run_GoogleTest_discovery_test_list(${DISCOVERY_MODE}) + run_GoogleTest_discovery_test_list_scoped(${DISCOVERY_MODE}) run_GoogleTest_discovery_flush_script(${DISCOVERY_MODE}) endforeach() diff --git a/Tests/RunCMake/Graphviz/test_project/core_library.c b/Tests/RunCMake/Graphviz/test_project/core_library.c index e8a8844..ce390ce 100644 --- a/Tests/RunCMake/Graphviz/test_project/core_library.c +++ b/Tests/RunCMake/Graphviz/test_project/core_library.c @@ -1,3 +1,3 @@ -void log_something() +void log_something(void) { } diff --git a/Tests/RunCMake/Graphviz/test_project/graphic_library.c b/Tests/RunCMake/Graphviz/test_project/graphic_library.c index 958c8ab..7a31ad4 100644 --- a/Tests/RunCMake/Graphviz/test_project/graphic_library.c +++ b/Tests/RunCMake/Graphviz/test_project/graphic_library.c @@ -1,3 +1,3 @@ -void initialize_graphics() +void initialize_graphics(void) { } diff --git a/Tests/RunCMake/Graphviz/test_project/module.c b/Tests/RunCMake/Graphviz/test_project/module.c index a508b09..56e833d 100644 --- a/Tests/RunCMake/Graphviz/test_project/module.c +++ b/Tests/RunCMake/Graphviz/test_project/module.c @@ -1,3 +1,3 @@ -static void some_function() +static void some_function(void) { } diff --git a/Tests/RunCMake/Graphviz/test_project/system_library.c b/Tests/RunCMake/Graphviz/test_project/system_library.c index 5d67079..896bca4 100644 --- a/Tests/RunCMake/Graphviz/test_project/system_library.c +++ b/Tests/RunCMake/Graphviz/test_project/system_library.c @@ -1,3 +1,3 @@ -void initialize_system() +void initialize_system(void) { } diff --git a/Tests/RunCMake/IAR/CMakeLists.txt b/Tests/RunCMake/IAR/CMakeLists.txt new file mode 100644 index 0000000..6a9ce76 --- /dev/null +++ b/Tests/RunCMake/IAR/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/IAR/RunCMakeTest.cmake b/Tests/RunCMake/IAR/RunCMakeTest.cmake new file mode 100644 index 0000000..2049740 --- /dev/null +++ b/Tests/RunCMake/IAR/RunCMakeTest.cmake @@ -0,0 +1,64 @@ +include(RunCMake) + +if(RunCMake_GENERATOR MATCHES "Makefile|Ninja") + file(GLOB _iar_toolchains + "${CMake_TEST_IAR_TOOLCHAINS}/bx*-*/*/bin/icc*" ) + if(_iar_toolchains STREQUAL "") + message(FATAL_ERROR "Could not find any IAR toolchains at: ${CMake_TEST_IAR_TOOLCHAINS}.") + endif() +endif() + +foreach(_iar_toolchain IN LISTS _iar_toolchains) + message(STATUS "Found IAR toolchain: ${_iar_toolchain}") + cmake_path(GET _iar_toolchain PARENT_PATH BIN_DIR) + cmake_path(GET BIN_DIR PARENT_PATH TOOLKIT_DIR) + cmake_path(GET TOOLKIT_DIR FILENAME ARCH) + + # Sets the minimal requirements for linking each target architecture + if(ARCH STREQUAL rl78) + set(LINK_OPTS +"--config_def _STACK_SIZE=256 \ +--config_def _NEAR_HEAP_SIZE=0x400 \ +--config_def _FAR_HEAP_SIZE=4096 \ +--config_def _HUGE_HEAP_SIZE=0 \ +--config_def _NEAR_CONST_LOCATION_START=0x2000 \ +--config_def _NEAR_CONST_LOCATION_SIZE=0x6F00 \ +--define_symbol _NEAR_CONST_LOCATION=0 \ +--config ${TOOLKIT_DIR}/config/lnkrl78_s3.icf" ) + else() + set(LINK_OPTS "") + endif() + + # Set IAR Assembler (ILINK || XLINK) + find_program(IAR_ASSEMBLER + NAMES iasm${ARCH} a${ARCH} + PATHS ${BIN_DIR} + REQUIRED ) + + set(RunCMake_TEST_OPTIONS + -DCMAKE_SYSTEM_NAME=Generic + -DCMAKE_C_COMPILER=${_iar_toolchain} + -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} + ) + run_cmake(iar-c) + + set(RunCMake_TEST_OPTIONS + -DCMAKE_SYSTEM_NAME=Generic + -DCMAKE_CXX_COMPILER=${_iar_toolchain} + -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} + ) + run_cmake(iar-cxx) + + set(RunCMake_TEST_OPTIONS + -DCMAKE_SYSTEM_NAME=Generic + -DCMAKE_ASM_COMPILER=${IAR_ASSEMBLER} + ) + run_cmake(iar-asm) + + set(RunCMake_TEST_OPTIONS + -DCMAKE_SYSTEM_NAME=Generic + -DCMAKE_C_COMPILER=${_iar_toolchain} + -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} + ) + run_cmake(iar-lib) +endforeach() diff --git a/Tests/RunCMake/IAR/iar-asm.cmake b/Tests/RunCMake/IAR/iar-asm.cmake new file mode 100644 index 0000000..7ff64f6 --- /dev/null +++ b/Tests/RunCMake/IAR/iar-asm.cmake @@ -0,0 +1,5 @@ +enable_language(ASM) + +add_executable(exec-asm) +target_sources(exec-asm PRIVATE module.asm) +target_link_options(exec-asm PRIVATE ${LINKER_OPTS}) diff --git a/Tests/RunCMake/IAR/iar-c.cmake b/Tests/RunCMake/IAR/iar-c.cmake new file mode 100644 index 0000000..a36f096 --- /dev/null +++ b/Tests/RunCMake/IAR/iar-c.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +add_executable(exec-c) +target_sources(exec-c PRIVATE module.c) +target_link_options(exec-c PRIVATE ${LINKER_OPTS}) diff --git a/Tests/RunCMake/IAR/iar-cxx.cmake b/Tests/RunCMake/IAR/iar-cxx.cmake new file mode 100644 index 0000000..6b005b5 --- /dev/null +++ b/Tests/RunCMake/IAR/iar-cxx.cmake @@ -0,0 +1,5 @@ +enable_language(CXX) + +add_executable(exec-cxx) +target_sources(exec-cxx PRIVATE module.cxx) +target_link_options(exec-cxx PRIVATE ${LINKER_OPTS}) diff --git a/Tests/RunCMake/IAR/iar-lib.cmake b/Tests/RunCMake/IAR/iar-lib.cmake new file mode 100644 index 0000000..78b3136 --- /dev/null +++ b/Tests/RunCMake/IAR/iar-lib.cmake @@ -0,0 +1,10 @@ +enable_language(C) + +add_library(iar-test-lib) +target_sources(iar-test-lib PRIVATE libmod.c) + +add_executable(exec-lib-c) +target_sources(exec-lib-c PRIVATE module.c) +target_compile_definitions(exec-lib-c PRIVATE __USE_LIBFUN) +target_link_libraries(exec-lib-c LINK_PUBLIC iar-test-lib) +target_link_options(exec-lib-c PRIVATE ${LINKER_OPTS}) diff --git a/Tests/RunCMake/IAR/libmod.c b/Tests/RunCMake/IAR/libmod.c new file mode 100644 index 0000000..d6c3b73 --- /dev/null +++ b/Tests/RunCMake/IAR/libmod.c @@ -0,0 +1,4 @@ +int iar_libfun() +{ + return 42; +} diff --git a/Tests/RunCMake/IAR/module.asm b/Tests/RunCMake/IAR/module.asm new file mode 100644 index 0000000..1e08236 --- /dev/null +++ b/Tests/RunCMake/IAR/module.asm @@ -0,0 +1,41 @@ +#if defined(__IASM8051__) || defined(__IASM430__) + NAME main +#else + MODULE main +#endif + + PUBLIC main + PUBLIC __iar_program_start + PUBLIC __program_start + +#if defined(__IASMSTM8__) + EXTERN CSTACK$$Limit + SECTION `.near_func.text`:CODE:NOROOT(0) +#elif defined(__IASMAVR__) + ORG $0 + RJMP main + RSEG CODE +#elif defined(__IASM8051__) + ORG 0FFFEh + DC16 main + RSEG RCODE +?cmain: +#elif defined(__IASM430__) + ORG 0FFFEh + DC16 init + RSEG CSTACK + RSEG CODE +init: + MOV #SFE(CSTACK), SP +#else + EXTERN __iar_static_base$$GPREL + SECTION CSTACK:DATA:NOROOT(4) + SECTION `.cstartup`:CODE(2) + CODE +#endif + +__program_start: +__iar_program_start: +main: + NOP + END diff --git a/Tests/RunCMake/IAR/module.c b/Tests/RunCMake/IAR/module.c new file mode 100644 index 0000000..2f72a42 --- /dev/null +++ b/Tests/RunCMake/IAR/module.c @@ -0,0 +1,14 @@ +#include "module.h" +#if defined(__USE_LIBFUN) +extern int iar_libfun(); +#endif +__root int i; +__root int main() +{ +#if defined(__USE_LIBFUN) + i = iar_libfun(); +#else + i = INTERNAL; +#endif + return i; +} diff --git a/Tests/RunCMake/IAR/module.cxx b/Tests/RunCMake/IAR/module.cxx new file mode 100644 index 0000000..5604435 --- /dev/null +++ b/Tests/RunCMake/IAR/module.cxx @@ -0,0 +1,7 @@ +#include "module.h" +__root int i; +__root int main() +{ + i = INTERNAL; + return i; +} diff --git a/Tests/RunCMake/IAR/module.h b/Tests/RunCMake/IAR/module.h new file mode 100644 index 0000000..5f9c87b --- /dev/null +++ b/Tests/RunCMake/IAR/module.h @@ -0,0 +1,14 @@ +#ifndef __MODULE_H__ +#define __MODULE_H__ + +#if defined(__cplusplus) +# define INTERNAL 64 +#elif !defined(__cplusplus) && defined(__IAR_SYSTEMS_ICC__) +# define INTERNAL 32 +#elif defined(__IAR_SYSTEMS_ASM__) +# define INTERNAL 16 +#else +# error "Unable to determine INTERNAL symbol." +#endif /* __IAR_SYSTEMS_ICC */ + +#endif /* __MODULE_H__ */ diff --git a/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt b/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt new file mode 100644 index 0000000..5773ae3 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28...3.29) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt new file mode 100644 index 0000000..2566d8f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at InvalidConfiguration1.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + ORDER= + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake new file mode 100644 index 0000000..e79eb45 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER= UNICITY=ALL) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt new file mode 100644 index 0000000..933031d --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at InvalidConfiguration2.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + ORDER + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake new file mode 100644 index 0000000..c9da734 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING UNICITY=ALL ORDER) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt new file mode 100644 index 0000000..eba1eb2 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at InvalidConfiguration3.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + WRONG=REVERSE + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake new file mode 100644 index 0000000..a1311fe --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=ALL) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt new file mode 100644 index 0000000..46d5513 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at InvalidConfiguration4.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + WRONG=REVERSE + UNICITY=WRONG + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake new file mode 100644 index 0000000..9d48f4f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=WRONG) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt new file mode 100644 index 0000000..33a7552 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at Invalid_ORDER.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + ORDER=WRONG + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake new file mode 100644 index 0000000..72a7e02 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=WRONG UNICITY=ALL) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt new file mode 100644 index 0000000..4d759b7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at Invalid_UNICITY.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + UNICITY=WRONG + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake new file mode 100644 index 0000000..f423eef --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=WRONG) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake new file mode 100644 index 0000000..763f48b --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake @@ -0,0 +1,8 @@ +include(RunCMake) + +run_cmake(Invalid_ORDER) +run_cmake(Invalid_UNICITY) +run_cmake(InvalidConfiguration1) +run_cmake(InvalidConfiguration2) +run_cmake(InvalidConfiguration3) +run_cmake(InvalidConfiguration4) diff --git a/Tests/RunCMake/LinkLibrariesProcessing/lib.c b/Tests/RunCMake/LinkLibrariesProcessing/lib.c new file mode 100644 index 0000000..17a4148 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/lib.c @@ -0,0 +1,4 @@ + +void lib(void) +{ +} diff --git a/Tests/RunCMake/LinkLibrariesProcessing/main.c b/Tests/RunCMake/LinkLibrariesProcessing/main.c new file mode 100644 index 0000000..402eac3 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/main.c @@ -0,0 +1,5 @@ + +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkerSelection/AppleClassic.cmake b/Tests/RunCMake/LinkerSelection/AppleClassic.cmake new file mode 100644 index 0000000..62a12ad --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/AppleClassic.cmake @@ -0,0 +1,7 @@ + +enable_language(C) + +set(CMAKE_LINKER_TYPE APPLE_CLASSIC) + +add_executable(main main.c) +target_link_libraries(main PRIVATE m m) diff --git a/Tests/RunCMake/LinkerSelection/CMakeLists.txt b/Tests/RunCMake/LinkerSelection/CMakeLists.txt new file mode 100644 index 0000000..6a9ce76 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake b/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake new file mode 100644 index 0000000..235c38e --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake @@ -0,0 +1,2 @@ + +include("${CMAKE_CURRENT_LIST_DIR}/LinkerType-validation.cmake") diff --git a/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake b/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake new file mode 100644 index 0000000..4bf98b0 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake @@ -0,0 +1,36 @@ + +enable_language(C) + +set(CMAKE_C_USING_LINKER_FOO_C "${CMAKE_C_USING_LINKER_LLD}") + +add_executable(main main.c) +set_property(TARGET main PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>") + +if(CMake_TEST_CUDA) + enable_language(CUDA) + + set(CMAKE_CUDA_USING_LINKER_FOO_CUDA "${CMAKE_CUDA_USING_LINKER_LLD}") + + add_executable(mainCU main.cu) + set_property(TARGET mainCU PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>") +endif() + +# +# Generate file for validation +# +if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL") + cmake_path(GET CMAKE_C_USING_LINKER_FOO_C FILENAME LINKER_TYPE_OPTION) +else() + set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_FOO_C}") +endif() +if(CMake_TEST_CUDA) + if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL") + cmake_path(GET CMAKE_CUDA_USING_LINKER_FOO_CUDA FILENAME CUDA_LINKER) + else() + set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_FOO_CUDA}") + endif() + string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}") +endif() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake" + "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n") diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt new file mode 100644 index 0000000..11aea7a --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists.txt: + LINKER_TYPE 'FOO' is unknown. Did you forgot to define + 'CMAKE_C_USING_LINKER_FOO' variable\? diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake b/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake new file mode 100644 index 0000000..bbe398c --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake @@ -0,0 +1,5 @@ + +enable_language(C) + +set(CMAKE_LINKER_TYPE FOO) +add_executable(main main.c) diff --git a/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake b/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake new file mode 100644 index 0000000..3f82479 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake @@ -0,0 +1,9 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/LINKER_TYPE_OPTION.cmake") + +# In some environment, `=` character is escaped +string(REPLACE "=" "\\\\?=" LINKER_TYPE_OPTION "${LINKER_TYPE_OPTION}") + +if (NOT actual_stdout MATCHES "${LINKER_TYPE_OPTION}") + set (RunCMake_TEST_FAILED "Not found expected '${LINKER_TYPE_OPTION}'.") +endif() diff --git a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake new file mode 100644 index 0000000..cae4ca4 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake @@ -0,0 +1,44 @@ +include(RunCMake) + +if (RunCMake_GENERATOR MATCHES "Visual Studio 9 2008") + run_cmake(UnsupportedLinkerType) + return() +endif() + +run_cmake(InvalidLinkerType) + +# look-up for LLVM linker +if (WIN32) + set (LINKER_NAMES lld-link) +else() + set(LINKER_NAMES ld.lld ld64.lld) +endif() +find_program(LLD_LINKER NAMES ${LINKER_NAMES}) + +macro(run_cmake_and_build test) + run_cmake_with_options(${test} -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_NO_CLEAN 1) + if(CMake_TEST_CUDA) + string(APPEND "|${CMAKE_CUDA_USING_LINKER_LLD}") + endif() + run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release --verbose ${ARGN}) + + unset(RunCMake_TEST_BINARY_DIR) + unset(RunCMake_TEST_NO_CLEAN) +endmacro() + +if(LLD_LINKER) + block(SCOPE_FOR VARIABLES) + set(CMAKE_VERBOSE_MAKEFILE TRUE) + set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE) + set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE) + + run_cmake_and_build(ValidLinkerType) + run_cmake_and_build(CustomLinkerType) + endblock() +endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0") + run_cmake_and_build(AppleClassic) +endif() diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt new file mode 100644 index 0000000..6473451 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt @@ -0,0 +1,3 @@ +CMake Error at UnsupportedLinkerType.cmake:[0-9]+ \(add_executable\): + 'LINKER_TYPE' property, specified on target 'main', is not supported by + this generator. diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake new file mode 100644 index 0000000..1b0703c --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake @@ -0,0 +1,5 @@ + +enable_language(C) + +set(CMAKE_LINKER_TYPE LDD) +add_executable(main main.c) diff --git a/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake b/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake new file mode 100644 index 0000000..235c38e --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake @@ -0,0 +1,2 @@ + +include("${CMAKE_CURRENT_LIST_DIR}/LinkerType-validation.cmake") diff --git a/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake b/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake new file mode 100644 index 0000000..a685ac1 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake @@ -0,0 +1,32 @@ + +enable_language(C) + +set(CMAKE_LINKER_TYPE LLD) + +add_executable(main main.c) + +if(CMake_TEST_CUDA) + enable_language(CUDA) + + add_executable(mainCU main.cu) +endif() + +# +# Generate file for validation +# +if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL") + cmake_path(GET CMAKE_C_USING_LINKER_LLD FILENAME LINKER_TYPE_OPTION) +else() + set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_LLD}") +endif() +if(CMake_TEST_CUDA) + if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL") + cmake_path(GET CMAKE_CUDA_USING_LINKER_LLD FILENAME CUDA_LINKER) + else() + set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_LLD}") + endif() + string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}") +endif() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake" + "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n") diff --git a/Tests/RunCMake/LinkerSelection/main.c b/Tests/RunCMake/LinkerSelection/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkerSelection/main.cu b/Tests/RunCMake/LinkerSelection/main.cu new file mode 100644 index 0000000..766b775 --- /dev/null +++ b/Tests/RunCMake/LinkerSelection/main.cu @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt new file mode 100644 index 0000000..579c722 --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt @@ -0,0 +1,9 @@ +Test project [^ +]*/Tests/RunCMake/Make/CTestJobServer-build + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ +1/6 Test #[0-9]+: test[0-9]+ ............................ Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt new file mode 100644 index 0000000..eafba1c --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt @@ -0,0 +1 @@ +No tests were found!!! diff --git a/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt new file mode 100644 index 0000000..0547dc7 --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt @@ -0,0 +1,3 @@ +Test project [^ +]*/Tests/RunCMake/Make/CTestJobServer-build +Connected to MAKE jobserver diff --git a/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt new file mode 100644 index 0000000..a700999 --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt @@ -0,0 +1,6 @@ +Test project [^ +]*/Tests/RunCMake/Make/CTestJobServer-build +Connected to MAKE jobserver + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ +1/6 Test #[0-9]+: test[0-9]+ ............................ Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt new file mode 100644 index 0000000..5a76bdc --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt @@ -0,0 +1,7 @@ +Test project [^ +]*/Tests/RunCMake/Make/CTestJobServer-build +Connected to MAKE jobserver + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ + Start [0-9]+: test[0-9]+ +1/6 Test #[0-9]+: test[0-9]+ ............................ Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/Make/CTestJobServer.cmake b/Tests/RunCMake/Make/CTestJobServer.cmake new file mode 100644 index 0000000..2ca3d54 --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer.cmake @@ -0,0 +1,4 @@ +enable_testing() +foreach(i RANGE 1 6) + add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true) +endforeach() diff --git a/Tests/RunCMake/Make/CTestJobServer.make b/Tests/RunCMake/Make/CTestJobServer.make new file mode 100644 index 0000000..7fc5e28 --- /dev/null +++ b/Tests/RunCMake/Make/CTestJobServer.make @@ -0,0 +1,11 @@ +NoPipe: + env MAKEFLAGS= $(CMAKE_CTEST_COMMAND) -j6 +.PHONY: NoPipe + +NoTests: + +$(CMAKE_CTEST_COMMAND) -j6 -R NoTests +.PHONY: NoTests + +Tests: + +$(CMAKE_CTEST_COMMAND) -j6 +.PHONY: Tests diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake index 5d1ba48..cfaf759 100644 --- a/Tests/RunCMake/Make/RunCMakeTest.cmake +++ b/Tests/RunCMake/Make/RunCMakeTest.cmake @@ -79,9 +79,29 @@ function(detect_jobserver_present) run_cmake_command(DetectJobServer-present-parallel-build ${CMAKE_COMMAND} --build . -j4) endfunction() +function(run_make_rule case rule job_count) + run_cmake_command(${case}-${rule}-j${job_count} + ${RunCMake_MAKE_PROGRAM} -f "${RunCMake_SOURCE_DIR}/${case}.make" ${rule} -j${job_count} + CMAKE_COMMAND="${CMAKE_COMMAND}" CMAKE_CTEST_COMMAND="${CMAKE_CTEST_COMMAND}" + ) +endfunction() + +function(run_CTestJobServer) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CTestJobServer-build) + run_cmake(CTestJobServer) + set(RunCMake_TEST_NO_CLEAN 1) + run_make_rule(CTestJobServer NoPipe 2) + run_make_rule(CTestJobServer NoTests 2) + run_make_rule(CTestJobServer Tests 2) + run_make_rule(CTestJobServer Tests 3) +endfunction() + # Jobservers are currently only supported by GNU makes, except MSYS2 make if(MAKE_IS_GNU AND NOT RunCMake_GENERATOR MATCHES "MSYS Makefiles") detect_jobserver_present() + if(UNIX) + run_CTestJobServer() + endif() endif() if(MAKE_IS_GNU) diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake index 5a03fcb..09fd7e9 100644 --- a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake +++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake @@ -1,6 +1,6 @@ # Check that the prefix sub-directory is not repeated. -if(EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}") +if(NINJA_OUTPUT_PATH_PREFIX AND EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}") message(FATAL_ERROR "no sub directory named after the CMAKE_NINJA_OUTPUT_PATH_PREFIX " "should be in the binary directory." diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake index edde0c0..3016816 100644 --- a/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake +++ b/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake @@ -10,3 +10,38 @@ if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps hello.copy.c) +if (ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of hello.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps hello.copy2.c) +if (ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of hello.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake new file mode 100644 index 0000000..b2a553b --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake @@ -0,0 +1,47 @@ +set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build/build.ninja") +file(READ "${log}" build_file) + +set(RunCMake_TEST_FAILED) +if(NOT "${build_file}" MATCHES "depfile = test\\.d") + string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_d}") +endif() +if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") + string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") +endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps hello.copy.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps hello.copy2.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake new file mode 100644 index 0000000..cf3b35d --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake @@ -0,0 +1,24 @@ +add_custom_command( + OUTPUT hello.copy.c + BYPRODUCTS "test.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/hello.c" + hello.copy.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test.d" + ) + +add_custom_command( + OUTPUT hello.copy2.c + BYPRODUCTS "test_$<CONFIG>.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/hello.c" + hello.copy2.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test_$<CONFIG>.d" + ) + +add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c" + "${CMAKE_CURRENT_BINARY_DIR}/hello.copy2.c") + +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake new file mode 100644 index 0000000..4d738c8 --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake @@ -0,0 +1,47 @@ +set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build/build.ninja") +file(READ "${log}" build_file) + +set(RunCMake_TEST_FAILED) +if(NOT "${build_file}" MATCHES "depfile = test\\.d") + string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_d}") +endif() +if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") + string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") +endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps hello.copy.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps hello.copy2.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake new file mode 100644 index 0000000..07a12b7 --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake @@ -0,0 +1,24 @@ +add_custom_command( + OUTPUT hello.copy.c + "test.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/hello.c" + hello.copy.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test.d" + ) + +add_custom_command( + OUTPUT hello.copy2.c + "test_$<CONFIG>.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/hello.c" + hello.copy2.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test_$<CONFIG>.d" + ) + +add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c" + "${CMAKE_CURRENT_BINARY_DIR}/hello.copy2.c") + +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 13d9620..f020f0d 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -100,6 +100,8 @@ run_CMP0058(NEW-no) run_CMP0058(NEW-by) run_cmake_with_options(CustomCommandDepfile -DCMAKE_BUILD_TYPE=Debug) +run_cmake_with_options(CustomCommandDepfileAsOutput -DCMAKE_BUILD_TYPE=Debug) +run_cmake_with_options(CustomCommandDepfileAsByproduct -DCMAKE_BUILD_TYPE=Debug) run_cmake(CustomCommandJobPool) run_cmake(JobPoolUsesTerminal) diff --git a/Tests/RunCMake/Ninja/dep.c b/Tests/RunCMake/Ninja/dep.c index 728f031..a576b6e 100644 --- a/Tests/RunCMake/Ninja/dep.c +++ b/Tests/RunCMake/Ninja/dep.c @@ -1,4 +1,4 @@ -int dep() +int dep(void) { return 0; } diff --git a/Tests/RunCMake/Ninja/top.c b/Tests/RunCMake/Ninja/top.c index 4a88eb2..0358244 100644 --- a/Tests/RunCMake/Ninja/top.c +++ b/Tests/RunCMake/Ninja/top.c @@ -1,7 +1,7 @@ #include "command.h" #include "target.h" -int top() +int top(void) { return 0; } diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake index 3674aba..673391c 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake @@ -10,3 +10,38 @@ if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps main.copy.c) +if (ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of main.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps main.copy2.c) +if (ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of main.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake new file mode 100644 index 0000000..ced40ab --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake @@ -0,0 +1,47 @@ +set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build/CMakeFiles/impl-Debug.ninja") +file(READ "${log}" build_file) + +set(RunCMake_TEST_FAILED) +if(NOT "${build_file}" MATCHES "depfile = test\\.d") + string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_d}") +endif() +if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") + string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") +endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps main.copy.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps main.copy2.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake new file mode 100644 index 0000000..e1e49cb --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake @@ -0,0 +1,22 @@ +add_custom_command( + OUTPUT main.copy.c + BYPRODUCTS "test.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/main.c" + main.copy.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test.d" + ) + +add_custom_command( + OUTPUT main.copy2.c + BYPRODUCTS "test_$<CONFIG>.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/main.c" + main.copy2.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test_$<CONFIG>.d" + ) + +add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/main.copy.c" + "${CMAKE_CURRENT_BINARY_DIR}/main.copy2.c") diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake new file mode 100644 index 0000000..d4a87ba --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake @@ -0,0 +1,47 @@ +set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build/CMakeFiles/impl-Debug.ninja") +file(READ "${log}" build_file) + +set(RunCMake_TEST_FAILED) +if(NOT "${build_file}" MATCHES "depfile = test\\.d") + string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_d}") +endif() +if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d") + string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d") + list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}") +endif() + +function(_run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() + set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE) +endfunction() + +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build") +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps main.copy.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy.c in the database") +endif () +_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps main.copy2.c) +if (NOT ninja_stdout MATCHES "deps not found") + list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy2.c in the database") +endif () diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake new file mode 100644 index 0000000..0617970 --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake @@ -0,0 +1,22 @@ +add_custom_command( + OUTPUT main.copy.c + "test.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/main.c" + main.copy.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test.d" + ) + +add_custom_command( + OUTPUT main.copy2.c + "test_$<CONFIG>.d" + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/main.c" + main.copy2.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPFILE "test_$<CONFIG>.d" + ) + +add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/main.copy.c" + "${CMAKE_CURRENT_BINARY_DIR}/main.copy2.c") diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index 0ccf8e8..5c6a22d 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -388,6 +388,8 @@ unset(RunCMake_TEST_NO_CLEAN) unset(RunCMake_TEST_BINARY_DIR) run_cmake(CustomCommandDepfile) +run_cmake(CustomCommandDepfileAsOutput) +run_cmake(CustomCommandDepfileAsByproduct) set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all") run_cmake(PerConfigSources) diff --git a/Tests/RunCMake/ObjectLibrary/exe.c b/Tests/RunCMake/ObjectLibrary/exe.c index 2e08700..25d7784 100644 --- a/Tests/RunCMake/ObjectLibrary/exe.c +++ b/Tests/RunCMake/ObjectLibrary/exe.c @@ -5,7 +5,7 @@ #endif extern IMPORT int b(void); -int main() +int main(void) { return b(); } diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input new file mode 100644 index 0000000..c567f06 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input @@ -0,0 +1,20 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER= +CMAKE_Fortran_COMPILER_ABI= +CMAKE_Fortran_COMPILER_AR= +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=x64 +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=LLVMFlang +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB= +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=18.0.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +CMAKE_Fortran_SIMULATE_ID=MSVC +flang-new version 18.0.0 +Target: x86_64-pc-windows-msvc +Thread model: posix +InstalledDir: C:\DoesNotExist\LLVM\bin + "C:\\DoesNotExist\\LLVM\\bin\\flang-new" -fc1 -triple x86_64-pc-windows-msvc19.36.32543 -emit-obj -mrelocation-model pic -pic-level 2 -target-cpu x86-64 --dependent-lib=clang_rt.builtins-x86_64.lib -D_MT --dependent-lib=libcmt --dependent-lib=Fortran_main.static.lib --dependent-lib=FortranRuntime.static.lib --dependent-lib=FortranDecimal.static.lib -D_MSC_VER=1936 -D_MSC_FULL_VER=193632543 -D_WIN32 -D_M_X64=100 -mframe-pointer=none -o "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-e91f95.o" -x f95-cpp-input CMakeFortranCompilerABI.F + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC\\14.36.32532\\bin\\Hostx64\\x64\\link.exe" -out:a.exe "-libpath:C:\\DoesNotExist\\LLVM\\lib" /WHOLEARCHIVE:Fortran_main.static.lib /subsystem:console "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\18\\lib\\windows" -nologo "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-e91f95.o" diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake index 04998a2..0ede9ee 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake @@ -43,6 +43,7 @@ set(targets openbsd-C-Clang-5.0.1 openbsd-CXX-Clang-5.0.1 sunos-C-SunPro-5.13.0 sunos-CXX-SunPro-5.13.0 sunos-Fortran-SunPro-8.8.0 windows_x86_64-C-Clang-17.0.1-MSVC windows_x86_64-CXX-Clang-17.0.1-MSVC windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC + windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC windows_arm64-C-Clang-17.0.1-MSVC windows_arm64-CXX-Clang-17.0.1-MSVC windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC ) diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output new file mode 100644 index 0000000..c8266a5 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output @@ -0,0 +1,2 @@ +libs= +dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/18/lib/windows diff --git a/Tests/RunCMake/PrecompileHeaders/foo.c b/Tests/RunCMake/PrecompileHeaders/foo.c index eb88726..a4710fd 100644 --- a/Tests/RunCMake/PrecompileHeaders/foo.c +++ b/Tests/RunCMake/PrecompileHeaders/foo.c @@ -2,12 +2,12 @@ #include "foo2.h" -int foo() +int foo(void) { return 0; } -int foo2() +int foo2(void) { return 0; } diff --git a/Tests/RunCMake/PrecompileHeaders/foobar.c b/Tests/RunCMake/PrecompileHeaders/foobar.c index 97d465c..71ebe37 100644 --- a/Tests/RunCMake/PrecompileHeaders/foobar.c +++ b/Tests/RunCMake/PrecompileHeaders/foobar.c @@ -2,7 +2,7 @@ #include "foo.h" #include "foo2.h" -int main() +int main(void) { int zeroSize = 0; diff --git a/Tests/RunCMake/PrecompileHeaders/include/bar.h b/Tests/RunCMake/PrecompileHeaders/include/bar.h index 5feb983..89a156c 100644 --- a/Tests/RunCMake/PrecompileHeaders/include/bar.h +++ b/Tests/RunCMake/PrecompileHeaders/include/bar.h @@ -1,7 +1,7 @@ #ifndef bar_h #define bar_h -static int bar() +static int bar(void) { return 0; } diff --git a/Tests/RunCMake/PrintHelpers/nothing.c b/Tests/RunCMake/PrintHelpers/nothing.c index 32b7b39..1d11f33 100644 --- a/Tests/RunCMake/PrintHelpers/nothing.c +++ b/Tests/RunCMake/PrintHelpers/nothing.c @@ -1,6 +1,6 @@ #include "nothing.h" -void nothing() +void nothing(void) { (void*)0; } diff --git a/Tests/RunCMake/PrintHelpers/something.c b/Tests/RunCMake/PrintHelpers/something.c index a2bc425..90482c9 100644 --- a/Tests/RunCMake/PrintHelpers/something.c +++ b/Tests/RunCMake/PrintHelpers/something.c @@ -1,6 +1,6 @@ #include "something.h" -int main() +int main(void) { nothing(); return 0; diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index fcf904e..179c80e 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -323,5 +323,21 @@ function(ensure_files_match expected_file actual_file) endif() endfunction() +# Get the user id on unix if possible. +function(get_unix_uid var) + set("${var}" "" PARENT_SCOPE) + if(UNIX) + set(ID "id") + if(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND EXISTS "/usr/xpg4/bin/id") + set (ID "/usr/xpg4/bin/id") + endif() + execute_process(COMMAND ${ID} -u $ENV{USER} OUTPUT_VARIABLE uid ERROR_QUIET + RESULT_VARIABLE status OUTPUT_STRIP_TRAILING_WHITESPACE) + if(status EQUAL 0) + set("${var}" "${uid}" PARENT_SCOPE) + endif() + endif() +endfunction() + # Protect RunCMake tests from calling environment. unset(ENV{MAKEFLAGS}) diff --git a/Tests/RunCMake/SourceProperties/empty.c b/Tests/RunCMake/SourceProperties/empty.c index a9ec102..768abc2 100644 --- a/Tests/RunCMake/SourceProperties/empty.c +++ b/Tests/RunCMake/SourceProperties/empty.c @@ -1,5 +1,5 @@ -int empty() +int empty(void) { return 0; } diff --git a/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt new file mode 100644 index 0000000..82adcda --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt @@ -0,0 +1,3 @@ +CMake Warning \(dev\) in CMakeLists.txt: + Unknown Swift_COMPILATION_MODE on target 'greetings_who_knows' +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/Swift/CMP0157-NEW.cmake b/Tests/RunCMake/Swift/CMP0157-NEW.cmake new file mode 100644 index 0000000..96c2ff4 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-NEW.cmake @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.28) + +cmake_policy(SET CMP0157 NEW) +include(CMP0157-common.cmake) diff --git a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt new file mode 100644 index 0000000..0955c61 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt @@ -0,0 +1 @@ +swiftc .* -output-file-map CMakeFiles/greetings_default.dir//output-file-map.json .* diff --git a/Tests/RunCMake/Swift/CMP0157-OLD.cmake b/Tests/RunCMake/Swift/CMP0157-OLD.cmake new file mode 100644 index 0000000..6b0ec94 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-OLD.cmake @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.28) + +cmake_policy(SET CMP0157 OLD) +include(CMP0157-common.cmake) diff --git a/Tests/RunCMake/Swift/CMP0157-WARN.cmake b/Tests/RunCMake/Swift/CMP0157-WARN.cmake new file mode 100644 index 0000000..7d8c01d --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-WARN.cmake @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.28) + +include(CMP0157-common.cmake) diff --git a/Tests/RunCMake/Swift/CMP0157-common.cmake b/Tests/RunCMake/Swift/CMP0157-common.cmake new file mode 100644 index 0000000..53f14f6 --- /dev/null +++ b/Tests/RunCMake/Swift/CMP0157-common.cmake @@ -0,0 +1,19 @@ +enable_language(Swift) + +add_executable(greetings_default hello.swift) + +add_executable(greetings_wmo hello.swift) +set_target_properties(greetings_wmo PROPERTIES + Swift_COMPILATION_MODE "wholemodule") + +add_executable(greetings_incremental hello.swift) +set_target_properties(greetings_incremental PROPERTIES + Swift_COMPILATION_MODE "incremental") + +add_executable(greetings_singlefile hello.swift) +set_target_properties(greetings_singlefile PROPERTIES + Swift_COMPILATION_MODE "singlefile") + +add_executable(greetings_who_knows hello.swift) +set_target_properties(greetings_who_knows PROPERTIES + Swift_COMPILATION_MODE "not-a-real-mode") diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt index bb08a49..d644d6b 100644 --- a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt +++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt @@ -1,3 +1,3 @@ -.*Linking Swift static library libA.a -.*Linking Swift static library libB.a -FAILED: libB.a CMakeFiles/B.dir/b.swift.o B.swiftmodule +.*Building Swift object A.swiftmodule CMakeFiles/A.dir/a.swift.o +.*Building Swift object B.swiftmodule CMakeFiles/B.dir/b.swift.o +FAILED: B.swiftmodule CMakeFiles/B.dir/b.swift.o diff --git a/Tests/RunCMake/Swift/IncrementalSwift.cmake b/Tests/RunCMake/Swift/IncrementalSwift.cmake index 092269f..08f3fff 100644 --- a/Tests/RunCMake/Swift/IncrementalSwift.cmake +++ b/Tests/RunCMake/Swift/IncrementalSwift.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0157 NEW) enable_language(Swift) # Write initial files to build directory diff --git a/Tests/RunCMake/Swift/NoWorkToDo.cmake b/Tests/RunCMake/Swift/NoWorkToDo.cmake index 51c2ff3..02b9195 100644 --- a/Tests/RunCMake/Swift/NoWorkToDo.cmake +++ b/Tests/RunCMake/Swift/NoWorkToDo.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0157 NEW) enable_language(Swift) add_executable(hello1 hello.swift) set_target_properties(hello1 PROPERTIES ENABLE_EXPORTS TRUE) diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index 5537c01..184b461 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake @@ -1,11 +1,15 @@ include(RunCMake) if(RunCMake_GENERATOR STREQUAL Xcode) - if(XCODE_BELOW_6_1) + if(XCODE_VERSION VERSION_LESS 6.1) run_cmake(XcodeTooOld) + elseif(CMake_TEST_Swift) + run_cmake(CMP0157-NEW) + run_cmake(CMP0157-OLD) + run_cmake(CMP0157-WARN) endif() elseif(RunCMake_GENERATOR STREQUAL Ninja) - if(CMAKE_Swift_COMPILER) + if(CMake_TEST_Swift) if (CMAKE_SYSTEM_NAME MATCHES "Windows") run_cmake_with_options(Win32ExecutableDisallowed) else() @@ -45,11 +49,27 @@ elseif(RunCMake_GENERATOR STREQUAL Ninja) run_cmake_command(IncrementalSwift-second ${CMAKE_COMMAND} --build ${IncrementalSwift_TEST_BINARY_DIR} -- -d explain) endblock() + block() + set(CMP0157-OLD_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0157-OLD-build) + set(CMP0157-OLD_TEST_NO_CLEAN 1) + set(CMP0157-OLD_TEST_OUTPUT_MERGE 1) + + run_cmake(CMP0157-NEW) + run_cmake(CMP0157-OLD) + # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command + run_cmake_command(CMP0157-OLD-build ${CMAKE_COMMAND} --build ${CMP0157-OLD_TEST_BINARY_DIR} -- -n -v) + run_cmake(CMP0157-WARN) + endblock() + endif() elseif(RunCMake_GENERATOR STREQUAL "Ninja Multi-Config") - if(CMAKE_Swift_COMPILER) + if(CMake_TEST_Swift) set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release") run_cmake(SwiftSimple) + + run_cmake(CMP0157-NEW) + run_cmake(CMP0157-OLD) + run_cmake(CMP0157-WARN) unset(RunCMake_TEST_OPTIONS) endif() else() diff --git a/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake b/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake index 02d5447..5e52911 100644 --- a/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake +++ b/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0157 NEW) enable_language(Swift) add_executable(E E.swift) set_target_properties(E PROPERTIES diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index c2187ae..11d59b7 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -39,6 +39,8 @@ \* CMP0142 \* CMP0154 \* CMP0155 + \* CMP0156 + \* CMP0157 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt index f3c068a..efa7b32 100644 --- a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt +++ b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt @@ -1 +1,12 @@ -^try_compile CMP0126='OLD' VAR='2' +^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0126 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\) ++ +try_compile CMP0126='OLD' VAR='2' diff --git a/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt b/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt new file mode 100644 index 0000000..8956278 --- /dev/null +++ b/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt @@ -0,0 +1,4 @@ +^CMake Warning: + Expected depfile does not exist. + + .*/Tests/RunCMake/TransformDepfile/noexist.d$ diff --git a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake index e3643c0..0ec8c42 100644 --- a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake +++ b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake @@ -14,6 +14,13 @@ run_cmake(unitybuild_cxx) run_cmake(unitybuild_cxx_group) run_cmake(unitybuild_c_and_cxx) run_cmake(unitybuild_c_and_cxx_group) +if(CMake_TEST_OBJC) + run_cmake(unitybuild_objc) + run_cmake(unitybuild_objc_group) + run_cmake(unitybuild_objcxx) + run_cmake(unitybuild_objcxx_group) + run_cmake(unitybuild_c_and_cxx_and_objc_and_objcxx) +endif() run_cmake(unitybuild_batchsize) run_cmake(unitybuild_default_batchsize) run_cmake(unitybuild_skip) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake new file mode 100644 index 0000000..096a86b --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake @@ -0,0 +1,25 @@ +project(unitybuild_c_and_cxx_and_objc_and_objcxx C CXX OBJC OBJCXX) + +set(srcs "") +foreach(s RANGE 1 8) + set(src_c "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c") + file(WRITE "${src_c}" "int s${s}(void) { return 0; }\n") + + set(src_cxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cxx") + file(WRITE "${src_cxx}" "int s${s}(void) { return 0; }\n") + + set(src_objc "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m") + file(WRITE "${src_objc}" "int s${s}(void) { return 0; }\n") + + set(src_objcxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm") + file(WRITE "${src_objcxx}" "int s${s}(void) { return 0; }\n") + + list(APPEND srcs "${src_c}") + list(APPEND srcs "${src_cxx}") + list(APPEND srcs "${src_objc}") + list(APPEND srcs "${src_objcxx}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake new file mode 100644 index 0000000..cc88d98 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake @@ -0,0 +1,12 @@ +project(unitybuild_objc OBJC) + +set(srcs "") +foreach(s RANGE 1 8) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake new file mode 100644 index 0000000..384c98a --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake @@ -0,0 +1,27 @@ +project(unitybuild_objc_group OBJC) + +set(srcs "") +foreach(s RANGE 1 4) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +foreach(s RANGE 1 2) + set(src "${CMAKE_CURRENT_BINARY_DIR}/odr${s}.m") + file(WRITE "${src}" "namespace odr { int s${s}(void) { return 0; } }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON + UNITY_BUILD_MODE GROUP + ) + +set_source_files_properties(s1.m s2.m odr1.m + PROPERTIES UNITY_GROUP "a" + ) +set_source_files_properties(s3.m s4.m odr2.m + PROPERTIES UNITY_GROUP "b" + ) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake new file mode 100644 index 0000000..fd0f743 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake @@ -0,0 +1,12 @@ +project(unitybuild_objcxx OBJCXX) + +set(srcs "") +foreach(s RANGE 1 8) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake new file mode 100644 index 0000000..517703e --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake @@ -0,0 +1,27 @@ +project(unitybuild_objcxx_group OBJCXX) + +set(srcs "") +foreach(s RANGE 1 4) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +foreach(s RANGE 1 2) + set(src "${CMAKE_CURRENT_BINARY_DIR}/odr${s}.mm") + file(WRITE "${src}" "namespace odr { int s${s}(void) { return 0; } }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON + UNITY_BUILD_MODE GROUP + ) + +set_source_files_properties(s1.mm s2.mm odr1.mm + PROPERTIES UNITY_GROUP "a" + ) +set_source_files_properties(s3.mm s4.mm odr2.mm + PROPERTIES UNITY_GROUP "b" + ) diff --git a/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake b/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake index a26c278..df14d15 100644 --- a/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake +++ b/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake @@ -1,5 +1,5 @@ -cmake_policy(VERSION 3.1) +cmake_policy(VERSION 3.5) file(STRINGS "${RunCMake_TEST_BINARY_DIR}/CMP0122-library-name.txt" prefixes) diff --git a/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake b/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake index 01657d0..6eb5c20 100644 --- a/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake +++ b/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake @@ -1,5 +1,5 @@ -cmake_policy(VERSION 3.1) +cmake_policy(VERSION 3.5) file(STRINGS "${RunCMake_TEST_BINARY_DIR}/CMP0122-library-name.txt" prefixes) diff --git a/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt new file mode 100644 index 0000000..84f2ee7 --- /dev/null +++ b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0122-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0122 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/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake index 669049a..e0d74cf 100644 --- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake +++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake @@ -11,6 +11,9 @@ run_cmake(CustomCommandGenex) if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 1[1-5] ") run_cmake(CustomCommandParallel) endif() +run_cmake_with_options(VsCharacterSet -DSET_CHARSET=MultiByte) +run_cmake_with_options(VsCharacterSet -DSET_CHARSET=Unicode) +run_cmake_with_options(VsCharacterSet -DSET_CHARSET=NotSet) run_cmake(VsCsharpSourceGroup) run_cmake(VsCSharpCompilerOpts) run_cmake(ExplicitCMakeLists) diff --git a/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake index 17e7b46..c904cd0 100644 --- a/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake +++ b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake @@ -27,7 +27,7 @@ foreach(line IN LISTS tgt_projects_strings) endif() endforeach() -string(REPLACE "\\" "/" unity_source_line ${unity_source_line}) +string(REPLACE "\\" "/" unity_source_line "${unity_source_line}") string(FIND "${unity_source_line}" "CMakeFiles/tgt.dir/Unity/unity_0_c.c" unity_source_file_position) if (unity_source_file_position EQUAL "-1") set(RunCMake_TEST_FAILED "Generated project should include the generated unity source file.") diff --git a/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake b/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake new file mode 100644 index 0000000..93770a1 --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake @@ -0,0 +1,49 @@ +macro(check_project_file projectFile outvar) + set(insideConfiguration FALSE) + set(characterSetFound FALSE) + + if(NOT EXISTS "${projectFile}") + set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.") + return() + endif() + + string(REPLACE "${RunCMake_TEST_BINARY_DIR}/" "" projectName ${projectFile}) + + file(STRINGS "${projectFile}" lines) + foreach(line IN LISTS lines) + if(line MATCHES "^ *<PropertyGroup Condition=\"'[$][(]Configuration[)]|[$][(]Platform[)]'=='([^\"])+\" Label=\"Configuration\">.*$") + set(insideConfiguration TRUE) + elseif(insideConfiguration) + if(line MATCHES "^ *</PropertyGroup>.*$") + set(insideConfiguration FALSE) + elseif(line MATCHES "^ *<CharacterSet>(.+)</CharacterSet>*$") + message(STATUS "Found CharacterSet = ${CMAKE_MATCH_1} in PropertyGroup 'Configuration' in ${projectName}") + set(characterSetFound TRUE) + set(${outvar} ${CMAKE_MATCH_1}) + endif() + endif() + endforeach() + if(NOT characterSetFound) + set(RunCMake_TEST_FAILED "CharacterSet not found in \"Configuration\" propertygroup in ${projectName}") + return() # This should intentionally return from the caller, not the macro + endif() +endmacro() + +check_project_file("${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${CMAKE_VERSION}/CompilerIdCXX/CompilerIdCXX.vcxproj" MULTI_BYTE_CHARSET) +check_project_file("${RunCMake_TEST_BINARY_DIR}/foo.vcxproj" OVERRIDDEN_CHARSET) + +if (NOT "${MULTI_BYTE_CHARSET}" STREQUAL "MultiByte") + set(RunCMake_TEST_FAILED "Default character-set (\"MultiByte\") was overridden (it shouldn't)") + return() +endif() + +if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/set_charset.txt") + set(RunCMake_TEST_FAILED "File 'set_charset.txt' with set charset does not exist.") + return() +endif() +file(STRINGS "${RunCMake_TEST_BINARY_DIR}/set_charset.txt" SET_CHARSET) + +if (NOT "${OVERRIDDEN_CHARSET}" STREQUAL "${SET_CHARSET}") + set(RunCMake_TEST_FAILED "Failed to override the character-set with \"${SET_CHARSET}\"") + return() +endif() diff --git a/Tests/RunCMake/VS10Project/VsCharacterSet.cmake b/Tests/RunCMake/VS10Project/VsCharacterSet.cmake new file mode 100644 index 0000000..c8c3e0e --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsCharacterSet.cmake @@ -0,0 +1,17 @@ +enable_language(CXX) + +# Write value of `SET_CHARSET` for comparison later. +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/set_charset.txt" "${SET_CHARSET}") + +# Set macro which determines the character-set. +if("${SET_CHARSET}" STREQUAL "MultiByte") + add_compile_definitions(_MBCS=1) +endif() +if("${SET_CHARSET}" STREQUAL "NotSet") + add_compile_definitions(_SBCS=1) +endif() +if("${SET_CHARSET}" STREQUAL "Unicode") + add_compile_definitions(_UNICODE=1) +endif() + +add_library(foo foo.cpp) diff --git a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake index 9a13892..fad088f 100644 --- a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake @@ -119,3 +119,173 @@ unset(RunCMake_TEST_BINARY_DIR) run_cmake(find-library) run_cmake_command(find-library-script ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/find-library.cmake) + +file(REMOVE_RECURSE ${RunCMake_BINARY_DIR}/export-install) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-macos-build) +run_cmake_with_options(export-macos -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(export-macos-build ${CMAKE_COMMAND} --build . ${_config_arg}) +run_cmake_command(export-macos-install ${CMAKE_COMMAND} --install . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-build) +run_cmake_with_options(export-ios -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos "-DCMAKE_OSX_ARCHITECTURES=arm64" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(export-ios-build ${CMAKE_COMMAND} --build . ${_config_arg}) +run_cmake_command(export-ios-install ${CMAKE_COMMAND} --install . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-simulator-build) +run_cmake_with_options(export-ios-simulator -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(export-ios-simulator-build ${CMAKE_COMMAND} --build . ${_config_arg}) +run_cmake_command(export-ios-simulator-install ${CMAKE_COMMAND} --install . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-no-xcframework-build) +run_cmake_with_options(import-macos-install-specific-no-xcframework -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +set(_config_dir) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) + set(_config_dir /Release) +endif() +run_cmake_command(import-macos-install-specific-no-xcframework-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_NO_CLEAN 1) +if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # 'xcodebuild -create-xcframework' fails on symlinked paths. + file(REAL_PATH "${RunCMake_SOURCE_DIR}" src_dir) + file(REAL_PATH "${RunCMake_BINARY_DIR}" bld_dir) +else() + set(src_dir "${RunCMake_SOURCE_DIR}") + set(bld_dir "${RunCMake_BINARY_DIR}") +endif() +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-install) +run_cmake_command(export-install-xcframework xcodebuild -create-xcframework + -output ${bld_dir}/export-install/lib/mylib.xcframework + -library ${bld_dir}/export-install/lib/macos/libmylib.a + -headers ${src_dir}/mylib/include + -library ${bld_dir}/export-install/lib/ios/libmylib.a + -headers ${src_dir}/mylib/include + -library ${bld_dir}/export-install/lib/ios-simulator/libmylib.a + -headers ${src_dir}/mylib/include + ) +run_cmake_command(export-install-xcframework-genex xcodebuild -create-xcframework + -output ${bld_dir}/export-install/lib2/mylib-genex.xcframework + -library ${bld_dir}/export-install/lib/macos/libmylib-genex.a + -headers ${src_dir}/mylib/include + -library ${bld_dir}/export-install/lib/ios/libmylib-genex.a + -headers ${src_dir}/mylib/include + -library ${bld_dir}/export-install/lib/ios-simulator/libmylib-genex.a + -headers ${src_dir}/mylib/include + ) +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-macos-build) +run_cmake_command(export-build-macos-xcframework xcodebuild -create-xcframework + -output ${bld_dir}/export-macos-build/lib/mylib.xcframework + -library ${bld_dir}/export-macos-build/lib/macos${_config_dir}/libmylib.a + -headers ${src_dir}/mylib/include + ) +run_cmake_command(export-build-macos-xcframework-genex xcodebuild -create-xcframework + -output ${bld_dir}/export-macos-build/lib/mylib-genex.xcframework + -library ${bld_dir}/export-macos-build/lib/macos${_config_dir}/libmylib-genex.a + -headers ${src_dir}/mylib/include + ) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-build) +run_cmake_with_options(import-macos-install-specific -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-install-specific-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-specific-build) +run_cmake_with_options(import-macos-build-specific -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/macos/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-build-specific-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-genex-build) +run_cmake_with_options(import-macos-install-specific-genex -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-install-specific-genex-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-specific-genex-build) +run_cmake_with_options(import-macos-build-specific-genex -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/macos/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-build-specific-genex-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-general-build) +run_cmake_with_options(import-macos-install-general -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-install-general-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-general-build) +run_cmake_with_options(import-macos-build-general -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-macos-build-general-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-ios-install-general-build) +run_cmake_with_options(import-ios-install-general -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_ARCHITECTURES=arm64 -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/cmake/mylib) +set(RunCMake_TEST_NO_CLEAN 1) +set(_config_arg) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) +endif() +run_cmake_command(import-ios-install-general-build ${CMAKE_COMMAND} --build . ${_config_arg}) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) diff --git a/Tests/RunCMake/XcFramework/export-common.cmake b/Tests/RunCMake/XcFramework/export-common.cmake new file mode 100644 index 0000000..ef2659c --- /dev/null +++ b/Tests/RunCMake/XcFramework/export-common.cmake @@ -0,0 +1,46 @@ +enable_language(C) + +include(CMakePackageConfigHelpers) + +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +endif() + +add_library(mylib STATIC mylib/mylib.c) +target_include_directories(mylib INTERFACE $<INSTALL_INTERFACE:include>) +set_property(TARGET mylib PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib/${platform_name}) + +add_library(mylib-genex STATIC mylib/mylib.c) +target_include_directories(mylib-genex INTERFACE $<INSTALL_INTERFACE:include>) +set_property(TARGET mylib-genex PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib/${platform_name}) + +install(TARGETS mylib mylib-genex DESTINATION lib/${platform_name} EXPORT mylib) +install(FILES mylib/include/mylib/mylib.h DESTINATION include/mylib) +export(SETUP mylib + TARGET mylib XCFRAMEWORK_LOCATION lib/mylib.xcframework + TARGET mylib-genex XCFRAMEWORK_LOCATION "$<BUILD_INTERFACE:lib/$<TARGET_PROPERTY:NAME>.xcframework>$<INSTALL_INTERFACE:lib2/$<TARGET_PROPERTY:NAME>.xcframework>" + ) +install(EXPORT mylib DESTINATION lib/${platform_name}/cmake/mylib FILE mylib-targets.cmake) +export(EXPORT mylib FILE lib/${platform_name}/cmake/mylib/mylib-targets.cmake) + +configure_package_config_file(mylib-config.cmake.in mylib-config-sub.cmake INSTALL_DESTINATION lib/${platform_name}/cmake/mylib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-sub.cmake DESTINATION lib/${platform_name}/cmake/mylib RENAME mylib-config.cmake) + +configure_package_config_file(mylib-config.cmake.in lib/${platform_name}/cmake/mylib/mylib-config.cmake INSTALL_DESTINATION lib/${platform_name}/cmake/mylib) + +generate_apple_platform_selection_file(mylib-config-top.cmake + INSTALL_DESTINATION lib/cmake/mylib + MACOS_CONFIG_FILE lib/macos/cmake/mylib/mylib-config.cmake + IOS_CONFIG_FILE lib/ios/cmake/mylib/mylib-config.cmake + IOS_SIMULATOR_CONFIG_FILE lib/ios-simulator/cmake/mylib/mylib-config.cmake + ) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-top.cmake DESTINATION lib/cmake/mylib RENAME mylib-config.cmake) + +generate_apple_platform_selection_file(lib/cmake/mylib/mylib-config.cmake + INSTALL_DESTINATION lib/cmake/mylib + "${platform_arg}" lib/${platform_name}/cmake/mylib/mylib-config.cmake + ) diff --git a/Tests/RunCMake/XcFramework/export-ios-simulator.cmake b/Tests/RunCMake/XcFramework/export-ios-simulator.cmake new file mode 100644 index 0000000..1a4b04e --- /dev/null +++ b/Tests/RunCMake/XcFramework/export-ios-simulator.cmake @@ -0,0 +1,3 @@ +set(platform_name ios-simulator) +set(platform_arg IOS_SIMULATOR_CONFIG_FILE) +include(export-common.cmake) diff --git a/Tests/RunCMake/XcFramework/export-ios.cmake b/Tests/RunCMake/XcFramework/export-ios.cmake new file mode 100644 index 0000000..606b6a5 --- /dev/null +++ b/Tests/RunCMake/XcFramework/export-ios.cmake @@ -0,0 +1,3 @@ +set(platform_name ios) +set(platform_arg IOS_CONFIG_FILE) +include(export-common.cmake) diff --git a/Tests/RunCMake/XcFramework/export-macos.cmake b/Tests/RunCMake/XcFramework/export-macos.cmake new file mode 100644 index 0000000..64ac1c0 --- /dev/null +++ b/Tests/RunCMake/XcFramework/export-macos.cmake @@ -0,0 +1,3 @@ +set(platform_name macos) +set(platform_arg MACOS_CONFIG_FILE) +include(export-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-common.cmake b/Tests/RunCMake/XcFramework/import-common.cmake new file mode 100644 index 0000000..4def6a4 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-common.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +find_package(mylib REQUIRED) + +add_custom_target(print_loc ALL COMMAND ${CMAKE_COMMAND} -E echo "mylib location: $<TARGET_FILE:mylib>") diff --git a/Tests/RunCMake/XcFramework/import-genex-common.cmake b/Tests/RunCMake/XcFramework/import-genex-common.cmake new file mode 100644 index 0000000..e46902b --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-genex-common.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +find_package(mylib REQUIRED) + +add_custom_target(print_loc ALL COMMAND ${CMAKE_COMMAND} -E echo "mylib-genex location: $<TARGET_FILE:mylib-genex>") diff --git a/Tests/RunCMake/XcFramework/import-ios-install-general.cmake b/Tests/RunCMake/XcFramework/import-ios-install-general.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-ios-install-general.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt new file mode 100644 index 0000000..3ac467d --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt @@ -0,0 +1,2 @@ +mylib location: [^ +]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-build-general.cmake b/Tests/RunCMake/XcFramework/import-macos-build-general.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-general.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt new file mode 100644 index 0000000..3ac467d --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt @@ -0,0 +1,2 @@ +mylib location: [^ +]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt new file mode 100644 index 0000000..d3a20e8 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt @@ -0,0 +1,2 @@ +mylib-genex location: [^ +]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib-genex\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib-genex\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake new file mode 100644 index 0000000..a061bd3 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake @@ -0,0 +1 @@ +include(import-genex-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake b/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt new file mode 100644 index 0000000..1421246 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt @@ -0,0 +1,2 @@ +mylib location: [^ +]*/Tests/RunCMake/XcFramework/export-install/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-install-general.cmake b/Tests/RunCMake/XcFramework/import-macos-install-general.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-general.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt new file mode 100644 index 0000000..1421246 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt @@ -0,0 +1,2 @@ +mylib location: [^ +]*/Tests/RunCMake/XcFramework/export-install/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt new file mode 100644 index 0000000..5c88758 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt @@ -0,0 +1,2 @@ +mylib-genex location: [^ +]*/Tests/RunCMake/XcFramework/export-install/lib2/mylib-genex\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib-genex\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake new file mode 100644 index 0000000..a061bd3 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake @@ -0,0 +1 @@ +include(import-genex-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt new file mode 100644 index 0000000..1c92972 --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt @@ -0,0 +1,2 @@ +mylib location: [^ +]*/Tests/RunCMake/XcFramework/export-install/lib/macos/libmylib\.a diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake new file mode 100644 index 0000000..08ef6db --- /dev/null +++ b/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake @@ -0,0 +1 @@ +include(import-common.cmake) diff --git a/Tests/RunCMake/XcFramework/mylib-config.cmake.in b/Tests/RunCMake/XcFramework/mylib-config.cmake.in new file mode 100644 index 0000000..878d6e8 --- /dev/null +++ b/Tests/RunCMake/XcFramework/mylib-config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/mylib-targets.cmake") diff --git a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c index c00fce7..76ca54e 100644 --- a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c +++ b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c @@ -25,6 +25,6 @@ # error unknown OS #endif -void foo() +void foo(void) { } diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake new file mode 100644 index 0000000..706add5 --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake) + +findAttribute(${test} "Embed XPC Services" TRUE) diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake new file mode 100644 index 0000000..5ad0436 --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/EmbedXPCServices.cmake) diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake new file mode 100644 index 0000000..877a685 --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake @@ -0,0 +1,17 @@ +add_executable(xpc_service MACOSX_BUNDLE main.m) +set_target_properties(xpc_service PROPERTIES + BUNDLE_EXTENSION "xpc" + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/XPCService.Info.plist.in" + MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app.xpc_service" +) + +add_executable(app MACOSX_BUNDLE main.m) +add_dependencies(app xpc_service) +set_target_properties(app PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" + XCODE_EMBED_XPC_SERVICES xpc_service + MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app" +) diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake index 3798ddc..77ac63f 100644 --- a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake @@ -122,4 +122,5 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 14.1) TestEmbedCommon(Resources macOS) TestEmbedCommon(Resources iOS) TestEmbedCommon(PlugIns macOS) + TestEmbedCommon(XPCServices macOS) endif() diff --git a/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in b/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in new file mode 100644 index 0000000..abc8db2 --- /dev/null +++ b/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleDisplayName</key> + <string>SomeExtension</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>com.example.app.xpc_service</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>example_app_xpc_service</string> + <key>CFBundlePackageType</key> + <string>XPC!</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> + <key>CFBundleVersion</key> + <string>1.0.0</string> +</dict> +</plist> diff --git a/Tests/RunCMake/XcodeProject/XcodeXCConfig.c b/Tests/RunCMake/XcodeProject/XcodeXCConfig.c index ac59a6b..20aab00 100644 --- a/Tests/RunCMake/XcodeProject/XcodeXCConfig.c +++ b/Tests/RunCMake/XcodeProject/XcodeXCConfig.c @@ -15,6 +15,6 @@ # error TARGET_DEBUG does not match BUILD_DEBUG #endif -void some_symbol() +void some_symbol(void) { } diff --git a/Tests/RunCMake/add_compile_definitions/foo.c b/Tests/RunCMake/add_compile_definitions/foo.c index 74a86e1..7d75e37 100644 --- a/Tests/RunCMake/add_compile_definitions/foo.c +++ b/Tests/RunCMake/add_compile_definitions/foo.c @@ -1,4 +1,4 @@ -void foo() +void foo(void) { } diff --git a/Tests/RunCMake/add_custom_command/a.c b/Tests/RunCMake/add_custom_command/a.c index 707c1c3..4ef3698 100644 --- a/Tests/RunCMake/add_custom_command/a.c +++ b/Tests/RunCMake/add_custom_command/a.c @@ -1,3 +1,3 @@ -void a() +void a(void) { } diff --git a/Tests/RunCMake/add_dependencies/a.c b/Tests/RunCMake/add_dependencies/a.c index 707c1c3..4ef3698 100644 --- a/Tests/RunCMake/add_dependencies/a.c +++ b/Tests/RunCMake/add_dependencies/a.c @@ -1,3 +1,3 @@ -void a() +void a(void) { } diff --git a/Tests/RunCMake/add_dependencies/b.c b/Tests/RunCMake/add_dependencies/b.c index 57b2900..c7c7df4 100644 --- a/Tests/RunCMake/add_dependencies/b.c +++ b/Tests/RunCMake/add_dependencies/b.c @@ -1,3 +1,3 @@ -void b() +void b(void) { } diff --git a/Tests/RunCMake/add_dependencies/c.c b/Tests/RunCMake/add_dependencies/c.c index cbf94ca..e2fa6de 100644 --- a/Tests/RunCMake/add_dependencies/c.c +++ b/Tests/RunCMake/add_dependencies/c.c @@ -1,3 +1,3 @@ -void c() +void c(void) { } diff --git a/Tests/RunCMake/add_test/RunCMakeTest.cmake b/Tests/RunCMake/add_test/RunCMakeTest.cmake index ec6f6dd..8b5c915 100644 --- a/Tests/RunCMake/add_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_test/RunCMakeTest.cmake @@ -41,3 +41,9 @@ block() set(RunCMake_TEST_NO_CLEAN 1) run_cmake_command(EmptyArgument-ctest ${CMAKE_CTEST_COMMAND} -C Debug) endblock() + +set(RunCMake_TEST_OPTIONS + "-DCMAKE_TEST_LAUNCHER=/path/to/pseudo_test_launcher") + +run_cmake(TestLauncherProperty) +run_cmake(TestLauncher) diff --git a/Tests/RunCMake/add_test/TestLauncher-check.cmake b/Tests/RunCMake/add_test/TestLauncher-check.cmake new file mode 100644 index 0000000..78020a6 --- /dev/null +++ b/Tests/RunCMake/add_test/TestLauncher-check.cmake @@ -0,0 +1,28 @@ +set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake") +if(EXISTS "${testfile}") + file(READ "${testfile}" testfile_contents) +else() + message(FATAL_ERROR "Could not find expected CTestTestfile.cmake.") +endif() + +set(error_details "There is a problem with generated test file: ${testfile}") + +if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncher [^\n]+pseudo_test_launcher[^\n]+\n") + message(SEND_ERROR "Used test launcher when it should not be used. ${error_details}") +endif() + +if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncher [^\n]+pseudo_test_launcher[^\n]+\n") + message(SEND_ERROR "Did not use test launcher when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncherWithGenex [^\n]+pseudo_test_launcher[^\n]+\n") + message(SEND_ERROR "Used test launcher when it should not be used. ${error_details}") +endif() + +if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex [^\n]+pseudo_test_launcher[^\n]+\n") + message(SEND_ERROR "Did not use test launcher when it should be used. ${error_details}") +endif() + +if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex [^\n]+pseudo_test_launcher[^\n]+\n") + message(SEND_ERROR "Used test launcher when it should not be used. ${error_details}") +endif() diff --git a/Tests/RunCMake/add_test/TestLauncher.cmake b/Tests/RunCMake/add_test/TestLauncher.cmake new file mode 100644 index 0000000..8ad3be9 --- /dev/null +++ b/Tests/RunCMake/add_test/TestLauncher.cmake @@ -0,0 +1,22 @@ +project(test_launcher LANGUAGES C) + +enable_testing() +add_test(NAME DoesNotUseLauncher + COMMAND ${CMAKE_COMMAND} -E echo "Hi") + +add_executable(generated_exe simple_src_exiterror.cxx) +set_target_properties(generated_exe PROPERTIES LINKER_LANGUAGE C) + +add_test(NAME UsesTestLauncher + COMMAND generated_exe) + +add_test(NAME DoesNotUseTestLauncherWithGenex + COMMAND $<TARGET_FILE:generated_exe>) + +add_subdirectory(TestLauncher) + +add_test(NAME UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex + COMMAND generated_exe_in_subdir_added_to_test_without_genex) + +add_test(NAME DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex + COMMAND $<TARGET_FILE:generated_exe_in_subdir_added_to_test_with_genex>) diff --git a/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt b/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt new file mode 100644 index 0000000..fb40a59 --- /dev/null +++ b/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(generated_exe_in_subdir_added_to_test_without_genex + ${CMAKE_CURRENT_SOURCE_DIR}/../simple_src_exiterror.cxx) +set_target_properties(generated_exe_in_subdir_added_to_test_without_genex + PROPERTIES LINKER_LANGUAGE C) + +add_executable(generated_exe_in_subdir_added_to_test_with_genex + ${CMAKE_CURRENT_SOURCE_DIR}/../simple_src_exiterror.cxx) +set_target_properties(generated_exe_in_subdir_added_to_test_with_genex + PROPERTIES LINKER_LANGUAGE C) diff --git a/Tests/RunCMake/add_test/TestLauncherProperty.cmake b/Tests/RunCMake/add_test/TestLauncherProperty.cmake new file mode 100644 index 0000000..e86f42b --- /dev/null +++ b/Tests/RunCMake/add_test/TestLauncherProperty.cmake @@ -0,0 +1,41 @@ + +# This tests setting the TEST_LAUNCHER target property from the +# CMAKE_TEST_LAUNCHER variable. + +# -DCMAKE_TEST_LAUNCHER=/path/to/pseudo_test_launcher is passed to this +# test + +project(test_launcher LANGUAGES C) + +add_executable(target_with_test_launcher simple_src_exiterror.cxx) +set_target_properties(target_with_test_launcher PROPERTIES LINKER_LANGUAGE C) +get_property(launcher TARGET target_with_test_launcher + PROPERTY TEST_LAUNCHER) +if(NOT "${launcher}" MATCHES "pseudo_test_launcher") + message(SEND_ERROR "Default TEST_LAUNCHER property not set") +endif() + +set_property(TARGET target_with_test_launcher + PROPERTY TEST_LAUNCHER "another_test_launcher") +get_property(launcher TARGET target_with_test_launcher + PROPERTY TEST_LAUNCHER) +if(NOT "${launcher}" MATCHES "another_test_launcher") + message(SEND_ERROR + "set_property/get_property TEST_LAUNCHER is not consistent") +endif() + +unset(CMAKE_TEST_LAUNCHER CACHE) +add_executable(target_without_test_launcher simple_src_exiterror.cxx) +set_target_properties(target_without_test_launcher PROPERTIES LINKER_LANGUAGE C) +get_property(launcher TARGET target_without_test_launcher + PROPERTY TEST_LAUNCHER) +if(NOT "${launcher}" STREQUAL "") + message(SEND_ERROR "Default TEST_LAUNCHER property not set to null") +endif() + +add_executable(target_with_empty_test_launcher simple_src_exiterror.cxx) +set_target_properties(target_with_empty_test_launcher PROPERTIES LINKER_LANGUAGE C) +set_property(TARGET target_with_empty_test_launcher PROPERTY TEST_LAUNCHER "") + +enable_testing() +add_test(NAME test_target_with_empty_test_launcher COMMAND target_with_empty_test_launcher) diff --git a/Tests/RunCMake/add_test/simple_src_exiterror.cxx b/Tests/RunCMake/add_test/simple_src_exiterror.cxx new file mode 100644 index 0000000..6ce7183 --- /dev/null +++ b/Tests/RunCMake/add_test/simple_src_exiterror.cxx @@ -0,0 +1,4 @@ +int main(int, char**) +{ + return 13; +} diff --git a/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in new file mode 100644 index 0000000..408b2f3 --- /dev/null +++ b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in @@ -0,0 +1,2 @@ +cmake_minimum_required(VERSION 3.5) +project(CTestTest@CASE_NAME@ NONE) diff --git a/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt @@ -0,0 +1 @@ +(-1|255) diff --git a/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt new file mode 100644 index 0000000..338ac6d --- /dev/null +++ b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt @@ -0,0 +1,12 @@ +^CMake Error at [^ +]*/Tests/RunCMake/ctest_empty_binary_directory/NoCache/test.cmake:[0-9]+ \(ctest_empty_binary_directory\): + Did not remove the binary directory: + + [^ +]*/Tests/RunCMake/ctest_empty_binary_directory/NoCache-build + + because: + + path does not contain an existing CMakeCache\.txt file ++ +script continues after ctest_empty_binary_directory error$ diff --git a/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake b/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake new file mode 100644 index 0000000..f1d4ca7 --- /dev/null +++ b/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCTest) + +run_ctest(NoCache) diff --git a/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in new file mode 100644 index 0000000..2e0cfe6 --- /dev/null +++ b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.5) +set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@") +set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build") +ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) +message("script continues after ctest_empty_binary_directory error") diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c index babe82d..009162d 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c +++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c @@ -7,7 +7,7 @@ int main(void) return 0; } -int notcalled() +int notcalled(void) { printf(This function doesn't get called.\n"); return 0; diff --git a/Tests/RunCMake/ctest_test/TestLoadWait-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt index fc32958..60d70c9 100644 --- a/Tests/RunCMake/ctest_test/TestLoadWait-stdout.txt +++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt @@ -1,8 +1,8 @@ Test project [^ -]*/Tests/RunCMake/ctest_test/TestLoadWait-build( +]*/Tests/RunCMake/ctest_test/CTestTestLoadWait0-build( [^*][^ ]*)* -\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 2, Smallest test RunCMakeVersion requires 1\*\*\*\*\* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 6\*\*\*\*\* test 1 Start 1: RunCMakeVersion +( diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt new file mode 100644 index 0000000..70d8d3e --- /dev/null +++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt @@ -0,0 +1,15 @@ +Test project [^ +]*/Tests/RunCMake/ctest_test/CTestTestLoadWait1-build( +[^*][^ +]*)* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\* +test 1 + Start 1: RunCMakeVersion ++( +[^*][^ +]*)* +1/1 Test #1: RunCMakeVersion .................. Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec$ diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake index d2f3da3..b1ec9ad 100644 --- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake @@ -14,16 +14,20 @@ run_ctest_test(TestQuiet QUIET) # Tests for the 'Test Load' feature of ctest # # Spoof a load average value to make these tests more reliable. -set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5) +set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7) set(RunCTest_VERBOSE_FLAG -VV) +set(CASE_CMAKELISTS_SUFFIX_CODE [[ +set_property(TEST RunCMakeVersion PROPERTY PROCESSORS 2) +]]) # Verify that new tests are started when the load average falls below # our threshold. -run_ctest_test(TestLoadPass TEST_LOAD 6) +run_ctest_test(TestLoadPass TEST_LOAD 8) # Verify that new tests are not started when the load average exceeds # our threshold and that they then run once the load average drops. -run_ctest_test(TestLoadWait TEST_LOAD 2) +run_ctest_test(TestLoadWait0 TEST_LOAD 4 PARALLEL_LEVEL 8) +run_ctest_test(TestLoadWait1 TEST_LOAD 8 PARALLEL_LEVEL 8) # Verify that when an invalid "TEST_LOAD" value is given, a warning # message is displayed and the value is ignored. @@ -31,13 +35,15 @@ run_ctest_test(TestLoadInvalid TEST_LOAD "ERR1") # Verify that new tests are started when the load average falls below # our threshold. -set(CASE_CTEST_TEST_LOAD 7) +set(CASE_CTEST_TEST_LOAD 9) run_ctest_test(CTestTestLoadPass) # Verify that new tests are not started when the load average exceeds # our threshold and that they then run once the load average drops. -set(CASE_CTEST_TEST_LOAD 4) -run_ctest_test(CTestTestLoadWait) +set(CASE_CTEST_TEST_LOAD 6) +run_ctest_test(CTestTestLoadWait0 PARALLEL_LEVEL 8) +set(CASE_CTEST_TEST_LOAD 8) +run_ctest_test(CTestTestLoadWait1 PARALLEL_LEVEL 8) # Verify that when an invalid "CTEST_TEST_LOAD" value is given, # a warning message is displayed and the value is ignored. @@ -51,6 +57,7 @@ run_ctest_test(TestLoadOrder TEST_LOAD "ERR4") unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING}) unset(CASE_CTEST_TEST_LOAD) +unset(CASE_CMAKELISTS_SUFFIX_CODE) unset(RunCTest_VERBOSE_FLAG) block() diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt index 2f4468f..c7172aa 100644 --- a/Tests/RunCMake/ctest_test/CTestTestLoadWait-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt @@ -1,8 +1,8 @@ Test project [^ -]*/Tests/RunCMake/ctest_test/CTestTestLoadWait-build( +]*/Tests/RunCMake/ctest_test/TestLoadWait0-build( [^*][^ ]*)* -\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 4, Smallest test RunCMakeVersion requires 1\*\*\*\*\* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 4\*\*\*\*\* test 1 Start 1: RunCMakeVersion +( diff --git a/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt new file mode 100644 index 0000000..bca3e54 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt @@ -0,0 +1,15 @@ +Test project [^ +]*/Tests/RunCMake/ctest_test/TestLoadWait1-build( +[^*][^ +]*)* +\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\* +test 1 + Start 1: RunCMakeVersion ++( +[^*][^ +]*)* +1/1 Test #1: RunCMakeVersion .................. Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec$ diff --git a/Tests/RunCMake/export/CMake/FindHasDeps.cmake b/Tests/RunCMake/export/CMake/FindHasDeps.cmake new file mode 100644 index 0000000..86b2abe --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindHasDeps.cmake @@ -0,0 +1,17 @@ +find_package(Threads REQUIRED) +find_package(P4 REQUIRED) + +add_library(HasDeps::interface IMPORTED INTERFACE) +target_link_libraries(HasDeps::interface INTERFACE Threads::Threads l4) + +add_library(HasDeps::A IMPORTED UNKNOWN) +target_link_libraries(HasDeps::A INTERFACE HasDeps::interface) +file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/a.so") +set_property(TARGET HasDeps::A PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/a.so") + +add_library(HasDeps::B IMPORTED UNKNOWN) +target_link_libraries(HasDeps::B INTERFACE HasDeps::interface) +file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/b.so") +set_property(TARGET HasDeps::B PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/b.so") + +set(HASDEPS_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP1.cmake b/Tests/RunCMake/export/CMake/FindP1.cmake new file mode 100644 index 0000000..e33c3d6 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP1.cmake @@ -0,0 +1,2 @@ +add_library(l1 IMPORTED INTERFACE) +set(P1_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP2.cmake b/Tests/RunCMake/export/CMake/FindP2.cmake new file mode 100644 index 0000000..6a360ce --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP2.cmake @@ -0,0 +1,2 @@ +add_library(l2 IMPORTED INTERFACE) +set(P2_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP3.cmake b/Tests/RunCMake/export/CMake/FindP3.cmake new file mode 100644 index 0000000..72bd829 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP3.cmake @@ -0,0 +1,2 @@ +add_library(l3 IMPORTED INTERFACE) +set(P3_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP4.cmake b/Tests/RunCMake/export/CMake/FindP4.cmake new file mode 100644 index 0000000..b62a040 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP4.cmake @@ -0,0 +1,2 @@ +add_library(l4 IMPORTED INTERFACE) +set(P4_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP9.cmake b/Tests/RunCMake/export/CMake/FindP9.cmake new file mode 100644 index 0000000..201b86a --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP9.cmake @@ -0,0 +1,2 @@ +add_library(l9 IMPORTED INTERFACE) +set(P9_FOUND TRUE) diff --git a/Tests/RunCMake/export/FindDependencyExport-check.cmake b/Tests/RunCMake/export/FindDependencyExport-check.cmake new file mode 100644 index 0000000..0cbb195 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExport-check.cmake @@ -0,0 +1,35 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if("${mytargets}" MATCHES "find_dependency\\(P1") + string(APPEND RunCMake_TEST_FAILED "P1 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P2 REQUIRED \"VERSION\" \"1\\.0\"\\)") + string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P4 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n") +endif() +if("${mytargets}" MATCHES "find_dependency\\(P5") + string(APPEND RunCMake_TEST_FAILED "P5 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P6 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P6 dependency should be exported but it is not\n") +endif() +if("${mytargets}" MATCHES "find_dependency\\(P7") + string(APPEND RunCMake_TEST_FAILED "P7 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3[^ +]*\\) +find_dependency\\(P2[^ +]*\\) +find_dependency\\(P8[^ +]*\\) +find_dependency\\(P6[^ +]*\\) +find_dependency\\(P9[^ +]*\\) +find_dependency\\(P4") + string(APPEND RunCMake_TEST_FAILED "Dependencies are not in the correct order\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExport.cmake b/Tests/RunCMake/export/FindDependencyExport.cmake new file mode 100644 index 0000000..59dd4cc --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExport.cmake @@ -0,0 +1,30 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +find_package(P1) +find_package(P2) +find_package(P9) +find_package(P4) +find_package(P3) + +add_library(mylib INTERFACE) +target_link_libraries(mylib INTERFACE l1 l2 l3 l4 l9) + +install(TARGETS mylib EXPORT mytargets) +export(SETUP mytargets + PACKAGE_DEPENDENCY P1 + ENABLED OFF + PACKAGE_DEPENDENCY P3 + ENABLED AUTO + PACKAGE_DEPENDENCY P2 + ENABLED ON + EXTRA_ARGS VERSION 1.0 + PACKAGE_DEPENDENCY P5 + ENABLED FALSE + PACKAGE_DEPENDENCY P8 + ENABLED TRUE + PACKAGE_DEPENDENCY P6 + ENABLED 1 + PACKAGE_DEPENDENCY P7 + ENABLED AUTO + ) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake new file mode 100644 index 0000000..353bb08 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake @@ -0,0 +1,14 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/my_private_targets.cmake" my_private_targets) +if(NOT "${my_private_targets}" MATCHES "find_dependency\\(HasDeps") + string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n") +endif() + +file(READ "${RunCMake_TEST_BINARY_DIR}/my_static_targets.cmake" my_static_targets) +if(NOT "${my_static_targets}" MATCHES "find_dependency\\(MyPrivate") + string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n") +endif() + +file(READ "${RunCMake_TEST_BINARY_DIR}/my_shared_targets.cmake" my_shared_targets) +if(NOT "${my_shared_targets}" MATCHES "find_dependency\\(MyPrivate") + string(APPEND RunCMake_TEST_FAILED "MyStatic dependency should be exported but it is not\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake new file mode 100644 index 0000000..9e6c565 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake @@ -0,0 +1,37 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(HasDeps) + +# replicates FetchContent where a dependency is brought +# in via source. In these cases we need to extend the `install` +# `export` commands to allow markup on what `Find<Project>` will +# map to the export set +add_library(my_private_lib STATIC empty.cpp) +target_link_libraries(my_private_lib PUBLIC HasDeps::A) +set_target_properties(my_private_lib PROPERTIES EXPORT_FIND_PACKAGE_NAME "MyPrivate") + +install(TARGETS my_private_lib EXPORT my_private_targets) +install(EXPORT my_private_targets + FILE my_private.cmake + DESTINATION lib) +export(EXPORT my_private_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_private_targets.cmake) + +add_library(my_static_lib STATIC empty.cpp) +target_link_libraries(my_static_lib PRIVATE my_private_lib) + +install(TARGETS my_static_lib EXPORT my_static_targets) +install(EXPORT my_static_targets + FILE my_static.cmake + DESTINATION lib) +export(EXPORT my_static_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_static_targets.cmake) + +add_library(my_shared_lib SHARED empty.cpp) +target_link_libraries(my_shared_lib PUBLIC my_private_lib) + +install(TARGETS my_shared_lib EXPORT my_shared_targets) +install(EXPORT my_shared_targets + FILE my_shared.cmake + DESTINATION lib) +export(EXPORT my_shared_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_shared_targets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportShared-check.cmake b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake new file mode 100644 index 0000000..d7a32d1 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake @@ -0,0 +1,4 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if("${mytargets}" MATCHES "find_dependency") + string(APPEND RunCMake_TEST_FAILED "No dependencies should not be exported\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportShared.cmake b/Tests/RunCMake/export/FindDependencyExportShared.cmake new file mode 100644 index 0000000..6e662a1 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportShared.cmake @@ -0,0 +1,14 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(P1) +find_package(P2) +find_package(P3) +find_package(P4) + +add_library(mylib SHARED empty.cpp) +target_link_libraries(mylib PRIVATE l1 l2 l3 l4) + +install(TARGETS mylib EXPORT mytargets) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake new file mode 100644 index 0000000..b78bccb --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake @@ -0,0 +1,13 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if(NOT "${mytargets}" MATCHES "find_dependency\\(P1") + string(APPEND RunCMake_TEST_FAILED "P1 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P2") + string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3") + string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P4") + string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportStatic.cmake b/Tests/RunCMake/export/FindDependencyExportStatic.cmake new file mode 100644 index 0000000..2d32eb2 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportStatic.cmake @@ -0,0 +1,14 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(P1) +find_package(P2) +find_package(P3) +find_package(P4) + +add_library(mylib STATIC empty.cpp) +target_link_libraries(mylib PRIVATE l1 l2 l3 l4) + +install(TARGETS mylib EXPORT mytargets) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake index ee00b27..3e1b7d6 100644 --- a/Tests/RunCMake/export/RunCMakeTest.cmake +++ b/Tests/RunCMake/export/RunCMakeTest.cmake @@ -19,3 +19,7 @@ run_cmake(UnknownExport) run_cmake(NamelinkOnlyExport) run_cmake(SeparateNamelinkExport) run_cmake(TryCompileExport) +run_cmake(FindDependencyExport) +run_cmake(FindDependencyExportStatic) +run_cmake(FindDependencyExportShared) +run_cmake(FindDependencyExportFetchContent) diff --git a/Tests/RunCMake/find_file/NO_CACHE-stderr.txt b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt new file mode 100644 index 0000000..6da353b --- /dev/null +++ b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0125 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_library/NO_CACHE-stderr.txt b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt new file mode 100644 index 0000000..6da353b --- /dev/null +++ b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0125 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_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake index 0bed252..f9c8528 100644 --- a/Tests/RunCMake/find_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake @@ -24,6 +24,10 @@ if(CMAKE_HOST_WIN32 AND MINGW) run_cmake(MSYSTEM_PREFIX) endif() +if(CMAKE_HOST_WIN32 AND MSVC) + run_cmake(Windows-MSVC) +endif() + run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp") run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=CREATED_LIBRARY) diff --git a/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt b/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt new file mode 100644 index 0000000..598ede5 --- /dev/null +++ b/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt @@ -0,0 +1,2 @@ +-- STATIC_LIBRARY='[^']*/Tests/RunCMake/find_library/Windows-MSVC/static.lib' +-- RUSTC_IMPORT_LIBRARY='[^']*/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib' diff --git a/Tests/RunCMake/find_library/Windows-MSVC.cmake b/Tests/RunCMake/find_library/Windows-MSVC.cmake new file mode 100644 index 0000000..485952d --- /dev/null +++ b/Tests/RunCMake/find_library/Windows-MSVC.cmake @@ -0,0 +1,7 @@ +enable_language(C) + +find_library(STATIC_LIBRARY NAMES static NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Windows-MSVC) +message(STATUS "STATIC_LIBRARY='${STATIC_LIBRARY}'") + +find_library(RUSTC_IMPORT_LIBRARY NAMES rustc_import NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Windows-MSVC) +message(STATUS "RUSTC_IMPORT_LIBRARY='${RUSTC_IMPORT_LIBRARY}'") diff --git a/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib diff --git a/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib diff --git a/Tests/RunCMake/find_library/Windows-MSVC/static.lib b/Tests/RunCMake/find_library/Windows-MSVC/static.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_library/Windows-MSVC/static.lib diff --git a/Tests/RunCMake/find_path/NO_CACHE-stderr.txt b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt new file mode 100644 index 0000000..6da353b --- /dev/null +++ b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0125 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/NO_CACHE-stderr.txt b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt new file mode 100644 index 0000000..6da353b --- /dev/null +++ b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0125 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/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt new file mode 100644 index 0000000..7a49647 --- /dev/null +++ b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at foreach-var-scope-CMP0124-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0124 will be removed from a future version + of CMake\. + + 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/if/FilePermissions.cmake b/Tests/RunCMake/if/FilePermissions.cmake new file mode 100644 index 0000000..9edbddb --- /dev/null +++ b/Tests/RunCMake/if/FilePermissions.cmake @@ -0,0 +1,179 @@ + +file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/executable.txt") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" "foo") +file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" "foo") +file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" PERMISSIONS OWNER_WRITE GROUP_WRITE) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" "foo") +file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + +if(NOT WIN32) + file(REMOVE_RECURSE + "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" + ) + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/readable-dir") + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/writable-dir") + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" PERMISSIONS OWNER_WRITE GROUP_WRITE) + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/executable-dir") + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) +endif() + +function(cleanup) + if(NOT WIN32) + # CMake versions prior to 3.29 did not know how to remove non-readable directories. + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_WRITE) + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + endif() +endfunction() + +if(WIN32) + # files are always readable and executable + # directories are always, readable, writable and executable + if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed") + endif() + + if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed") + endif() +else() + if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed") + endif() + + if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable.txt\" failed") + endif() + + if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed") + endif() + + + if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable-dir\" failed") + endif() + + if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable-dir\" failed") + endif() + + if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed") + endif() +endif() + +if(UNIX) + # + # Check that file permissions are on the real file, not the symbolic link + # + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir") + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt" + SYMBOLIC) + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt" + SYMBOLIC) + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt" + SYMBOLIC) + + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir" + SYMBOLIC) + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir" + SYMBOLIC) + + file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" + "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir" + SYMBOLIC) + + if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt\" failed") + endif() + + if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt\" failed") + endif() + + if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt\" failed") + endif() + + + if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir\" failed") + endif() + + if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir" + OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir\" failed") + endif() + + if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir" + OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir" + OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir") + cleanup() + message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir\" failed") + endif() +endif() + +cleanup() diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake index efee116..43dfd3c 100644 --- a/Tests/RunCMake/if/RunCMakeTest.cmake +++ b/Tests/RunCMake/if/RunCMakeTest.cmake @@ -2,6 +2,14 @@ include(RunCMake) run_cmake(InvalidArgument1) run_cmake(exists) +if(NOT MSYS) + # permissions and symbolic links are broken on MSYS + # if real user is root, tests are irrelevant + get_unix_uid(uid) + if(NOT uid STREQUAL "0") + run_cmake(FilePermissions) + endif() +endif() run_cmake(IsDirectory) run_cmake(IsDirectoryLong) run_cmake(duplicate-deep-else) diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake new file mode 100644 index 0000000..6a8b2e5 --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake @@ -0,0 +1,4 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/root-all/lib/cmake/mylib/mylib-targets.cmake" contents) +if(NOT contents MATCHES "include\\(CMakeFindDependencyMacro\\)\nfind_dependency\\(P2 REQUIRED\\)\nfind_dependency\\(P1 REQUIRED\\)\n") + set(RunCMake_TEST_FAILED "Dependencies were not properly exported") +endif() diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake new file mode 100644 index 0000000..071414b --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +find_package(P1 REQUIRED) +find_package(P2 REQUIRED) +find_package(P3 REQUIRED) + +add_library(mylib INTERFACE) +target_link_libraries(mylib INTERFACE lib1 lib2 lib3) +install(TARGETS mylib EXPORT mylib-targets) +export(SETUP mylib-targets + PACKAGE_DEPENDENCY P2 + ENABLED AUTO + PACKAGE_DEPENDENCY P3 + ENABLED OFF + ) +install(EXPORT mylib-targets EXPORT_PACKAGE_DEPENDENCIES FILE mylib-targets.cmake DESTINATION lib/cmake/mylib) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index efafdd1..6f63550 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -176,6 +176,7 @@ run_install_test(TARGETS-Parts) run_install_test(FILES-PERMISSIONS) run_install_test(TARGETS-RPATH) run_install_test(InstallRequiredSystemLibraries) +run_install_test(EXPORT-FindDependencyExport) set(RunCMake_TEST_OPTIONS "-DCMAKE_POLICY_DEFAULT_CMP0087:STRING=NEW") run_install_test(SCRIPT) diff --git a/Tests/RunCMake/install/cmake/FindP1.cmake b/Tests/RunCMake/install/cmake/FindP1.cmake new file mode 100644 index 0000000..c772836 --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP1.cmake @@ -0,0 +1,2 @@ +add_library(lib1 IMPORTED INTERFACE) +set(P1_FOUND TRUE) diff --git a/Tests/RunCMake/install/cmake/FindP2.cmake b/Tests/RunCMake/install/cmake/FindP2.cmake new file mode 100644 index 0000000..d81b35d --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP2.cmake @@ -0,0 +1,2 @@ +add_library(lib2 IMPORTED INTERFACE) +set(P2_FOUND TRUE) diff --git a/Tests/RunCMake/install/cmake/FindP3.cmake b/Tests/RunCMake/install/cmake/FindP3.cmake new file mode 100644 index 0000000..2818c3b --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP3.cmake @@ -0,0 +1,2 @@ +add_library(lib3 IMPORTED INTERFACE) +set(P3_FOUND TRUE) diff --git a/Tests/RunCMake/print_stdin.c b/Tests/RunCMake/print_stdin.c index e083e62..76b3a84 100644 --- a/Tests/RunCMake/print_stdin.c +++ b/Tests/RunCMake/print_stdin.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { char buf[1024]; size_t nIn = sizeof(buf); diff --git a/Tests/RunCMake/project/CodeInjection-stdout.txt b/Tests/RunCMake/project/CodeInjection-stdout.txt deleted file mode 100644 index 88ac966..0000000 --- a/Tests/RunCMake/project/CodeInjection-stdout.txt +++ /dev/null @@ -1,10 +0,0 @@ -(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE -(-- )?Included CMAKE_TOOLCHAIN_FILE -.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file -(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file -(-- )?Included CMAKE_PROJECT_INCLUDE -(-- )?Calling sub-project -(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE -(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE -(-- )?Included CMAKE_PROJECT_INCLUDE -(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake deleted file mode 100644 index f3f0a7e..0000000 --- a/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake +++ /dev/null @@ -1 +0,0 @@ -message(STATUS "Included CMAKE_PROJECT_INCLUDE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake deleted file mode 100644 index 01d53c9..0000000 --- a/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake +++ /dev/null @@ -1 +0,0 @@ -message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake new file mode 100644 index 0000000..2bc65cf --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE first file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake new file mode 100644 index 0000000..df7240c --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE second file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake new file mode 100644 index 0000000..20bea78 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE first file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake new file mode 100644 index 0000000..91b59d1 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE second file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake deleted file mode 100644 index d68de6a..0000000 --- a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake +++ /dev/null @@ -1 +0,0 @@ -message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake deleted file mode 100644 index ef3bfc0..0000000 --- a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake +++ /dev/null @@ -1 +0,0 @@ -message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake new file mode 100644 index 0000000..fe0fe4a --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE first file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake new file mode 100644 index 0000000..c36fb52 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE second file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake new file mode 100644 index 0000000..23ae05a --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake new file mode 100644 index 0000000..17f1d29 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file") diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_1.cmake index 6c8995b..43bb817 100644 --- a/Tests/RunCMake/project/CodeInjection/initial_cache.cmake +++ b/Tests/RunCMake/project/CodeInjection/initial_cache_1.cmake @@ -1,10 +1,9 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "") -set(CMAKE_PROJECT_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_include.cmake" CACHE FILEPATH "") -set(CMAKE_PROJECT_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_include_before.cmake" CACHE FILEPATH "") -set(CMAKE_PROJECT_SubProj_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_include.cmake" CACHE FILEPATH "") -set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_include_before.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_SubProj_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake" CACHE FILEPATH "") set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake" - "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_2.cmake" - CACHE STRING "" + CACHE FILEPATH "" ) diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake new file mode 100644 index 0000000..09fcbfd --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake @@ -0,0 +1,27 @@ +set(CMAKE_TOOLCHAIN_FILE + "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_INCLUDE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_2.cmake" + CACHE STRING "" +) +set(CMAKE_PROJECT_INCLUDE_BEFORE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_2.cmake" + CACHE STRING "" +) +set(CMAKE_PROJECT_SubProj_INCLUDE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_2.cmake" + CACHE STRING "" +) +set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_2.cmake" + CACHE STRING "" +) +set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_2.cmake" + CACHE STRING "" +) diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake new file mode 100644 index 0000000..dd299bc --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake @@ -0,0 +1,28 @@ +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}" CACHE STRING "") +set(CMAKE_TOOLCHAIN_FILE + "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "") +set(CMAKE_PROJECT_INCLUDE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake" + "cmake_project_includes_2" + CACHE STRING "" +) +set(CMAKE_PROJECT_INCLUDE_BEFORE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake" + "cmake_project_includes_before_2" + CACHE STRING "" +) +set(CMAKE_PROJECT_SubProj_INCLUDE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake" + "cmake_project_subproj_includes_2" + CACHE STRING "" +) +set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake" + "cmake_project_subproj_includes_before_2" + CACHE STRING "" +) +set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES + "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake" + "cmake_project_top_level_includes_2" + CACHE STRING "" +) diff --git a/Tests/RunCMake/project/CodeInjection1-stdout.txt b/Tests/RunCMake/project/CodeInjection1-stdout.txt new file mode 100644 index 0000000..7a780b7 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection1-stdout.txt @@ -0,0 +1,9 @@ +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_TOOLCHAIN_FILE +.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Calling sub-project +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file diff --git a/Tests/RunCMake/project/CodeInjection.cmake b/Tests/RunCMake/project/CodeInjection1.cmake index dcf56a1..dcf56a1 100644 --- a/Tests/RunCMake/project/CodeInjection.cmake +++ b/Tests/RunCMake/project/CodeInjection1.cmake diff --git a/Tests/RunCMake/project/CodeInjection2-stdout.txt b/Tests/RunCMake/project/CodeInjection2-stdout.txt new file mode 100644 index 0000000..5c18cdf --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection2-stdout.txt @@ -0,0 +1,16 @@ +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file +(-- )?Included CMAKE_TOOLCHAIN_FILE +.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file +(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Included CMAKE_PROJECT_INCLUDE second file +(-- )?Calling sub-project +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Included CMAKE_PROJECT_INCLUDE second file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE second file diff --git a/Tests/RunCMake/project/CodeInjection2.cmake b/Tests/RunCMake/project/CodeInjection2.cmake new file mode 100644 index 0000000..dcf56a1 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection2.cmake @@ -0,0 +1 @@ +add_subdirectory(CodeInjection) diff --git a/Tests/RunCMake/project/CodeInjection3-stdout.txt b/Tests/RunCMake/project/CodeInjection3-stdout.txt new file mode 100644 index 0000000..5c18cdf --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection3-stdout.txt @@ -0,0 +1,16 @@ +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file +(-- )?Included CMAKE_TOOLCHAIN_FILE +.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file +(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Included CMAKE_PROJECT_INCLUDE second file +(-- )?Calling sub-project +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file +(-- )?Included CMAKE_PROJECT_INCLUDE first file +(-- )?Included CMAKE_PROJECT_INCLUDE second file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file +(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE second file diff --git a/Tests/RunCMake/project/CodeInjection3.cmake b/Tests/RunCMake/project/CodeInjection3.cmake new file mode 100644 index 0000000..dcf56a1 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection3.cmake @@ -0,0 +1 @@ +add_subdirectory(CodeInjection) diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index 0f3716f..16f10be 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -5,8 +5,16 @@ include(RunCMake) # which tests some of the individual variables one at a time. # Here, we are focused on testing that the variables are all injected # at the expected points in the expected order. -run_cmake_with_options(CodeInjection - -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache.cmake" +run_cmake_with_options(CodeInjection1 + -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_1.cmake" +) +# This checks that List variables are allowed. +run_cmake_with_options(CodeInjection2 + -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_2.cmake" +) +# This checks that module names are also allowed. +run_cmake_with_options(CodeInjection3 + -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_3.cmake" ) if(CMake_TEST_RESOURCES) diff --git a/Tests/RunCMake/property_init/Executable.cmake b/Tests/RunCMake/property_init/Executable.cmake index ede0e4b..a5e4fb8 100644 --- a/Tests/RunCMake/property_init/Executable.cmake +++ b/Tests/RunCMake/property_init/Executable.cmake @@ -17,6 +17,7 @@ set(properties # Metadata "CROSSCOMPILING_EMULATOR" "emu" "<SAME>" + "TEST_LAUNCHER" "test" "<SAME>" ) prepare_target_types(executable diff --git a/Tests/RunCMake/showIncludes.c b/Tests/RunCMake/showIncludes.c index 7d467cf..5114965 100644 --- a/Tests/RunCMake/showIncludes.c +++ b/Tests/RunCMake/showIncludes.c @@ -10,7 +10,7 @@ #include <stdio.h> #include <stdlib.h> -int main() +int main(void) { /* 'cl /showIncludes' encodes output in the console output code page. */ unsigned int cp = GetConsoleOutputCP(); diff --git a/Tests/RunCMake/target_compile_definitions/foo.c b/Tests/RunCMake/target_compile_definitions/foo.c index 74a86e1..7d75e37 100644 --- a/Tests/RunCMake/target_compile_definitions/foo.c +++ b/Tests/RunCMake/target_compile_definitions/foo.c @@ -1,4 +1,4 @@ -void foo() +void foo(void) { } diff --git a/Tests/RunCMake/target_compile_features/empty.c b/Tests/RunCMake/target_compile_features/empty.c index 11ec041..8d91e77 100644 --- a/Tests/RunCMake/target_compile_features/empty.c +++ b/Tests/RunCMake/target_compile_features/empty.c @@ -1,7 +1,7 @@ #ifdef _WIN32 __declspec(dllexport) #endif - int empty() + int empty(void) { return 0; } diff --git a/Tests/RunCMake/target_compile_options/CMP0101.c b/Tests/RunCMake/target_compile_options/CMP0101.c index 250869a..7ef6117 100644 --- a/Tests/RunCMake/target_compile_options/CMP0101.c +++ b/Tests/RunCMake/target_compile_options/CMP0101.c @@ -3,7 +3,7 @@ # error "BEFORE not honored" #endif -int main() +int main(void) { return 0; } diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/func.c b/Tests/RunCMake/target_link_libraries-ALIAS/func.c index 415a9bf..ebc6a5f 100644 --- a/Tests/RunCMake/target_link_libraries-ALIAS/func.c +++ b/Tests/RunCMake/target_link_libraries-ALIAS/func.c @@ -2,6 +2,6 @@ #if defined(_WIN32) __declspec(dllexport) #endif - void func_c() + void func_c(void) { } diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/lib.c b/Tests/RunCMake/target_link_libraries-ALIAS/lib.c index b2d1b66..95c0e91 100644 --- a/Tests/RunCMake/target_link_libraries-ALIAS/lib.c +++ b/Tests/RunCMake/target_link_libraries-ALIAS/lib.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -void lib() +void lib(void) { func_c(); } diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/main.c b/Tests/RunCMake/target_link_libraries-ALIAS/main.c index a908dea..72a3ddb 100644 --- a/Tests/RunCMake/target_link_libraries-ALIAS/main.c +++ b/Tests/RunCMake/target_link_libraries-ALIAS/main.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -int main() +int main(void) { func_c(); diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c index a5075d4..ed769a0 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c @@ -4,6 +4,6 @@ __declspec(dllexport) # endif #endif - void base() + void base(void) { } diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c index 3399e00..ef11bc2 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c @@ -1,7 +1,7 @@ -extern void func2(); +extern void func2(void); -void func1() +void func1(void) { func2(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c index 0f9aa64..3eab38e 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c @@ -1,7 +1,7 @@ -extern void func3(); +extern void func3(void); -void func2() +void func2(void) { func3(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c index 0b7df64..c109e09 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c @@ -1,6 +1,6 @@ -extern void func3(); +extern void func3(void); -void func3() +void func3(void) { } diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c index 35ab367..21f559c 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c @@ -4,12 +4,12 @@ __declspec(dllimport) # endif #endif - void base(); + void base(void); #if defined(_WIN32) __declspec(dllexport) #endif - void lib() + void lib(void) { base(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c index 403583d..d78b0f6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c +++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c @@ -1,7 +1,7 @@ -extern void func1(); +extern void func1(void); -int main() +int main(void) { func1(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c index 415a9bf..ebc6a5f 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c @@ -2,6 +2,6 @@ #if defined(_WIN32) __declspec(dllexport) #endif - void func_c() + void func_c(void) { } diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c index b2d1b66..95c0e91 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -void lib() +void lib(void) { func_c(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c index 689dbd7..891c4ad 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c @@ -1,14 +1,14 @@ #if defined(C_USE_CXX) -void func_c_cxx(); +void func_c_cxx(void); #else # if defined(_WIN32) __declspec(dllimport) # endif - void func_c(); + void func_c(void); #endif -int main() +int main(void) { #if defined(C_USE_CXX) func_c_cxx(); diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c index 415a9bf..ebc6a5f 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c @@ -2,6 +2,6 @@ #if defined(_WIN32) __declspec(dllexport) #endif - void func_c() + void func_c(void) { } diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c index b2d1b66..95c0e91 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -void lib() +void lib(void) { func_c(); } diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c index a908dea..72a3ddb 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c +++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -int main() +int main(void) { func_c(); diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake new file mode 100644 index 0000000..7003ade --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake @@ -0,0 +1,4 @@ + +if (actual_stdout MATCHES "(/|-)-LIBFLAG${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Found unexpected '--LIBFLAG<base1>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-check.cmake index 255c9a6..255c9a6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-check.cmake index a8e0da7..a8e0da7 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-check.cmake index 54cef2c..54cef2c 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-check.cmake index 7c38134..7c38134 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-check.cmake index 88b5cf6..88b5cf6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-check.cmake index c473637..c473637 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake new file mode 100644 index 0000000..eb1f755 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake new file mode 100644 index 0000000..eb1f755 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake new file mode 100644 index 0000000..783bad9 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP <other2> <other1>'.") + endif() +else() + if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base3> --SUFFIXGROUP <other2> --PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP <other1>'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-check.cmake index 255c9a6..255c9a6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-check.cmake index a8e0da7..a8e0da7 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake new file mode 100644 index 0000000..ce72570 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base3> <other1>'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake new file mode 100644 index 0000000..817b6e2 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}other1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP --LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<other1> --SUFFIXGROUP'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake new file mode 100644 index 0000000..ce72570 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base3> <other1>'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake new file mode 100644 index 0000000..700bcf2 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"?") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP --LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<other1> --SUFFIXGROUP'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake new file mode 100644 index 0000000..3a44bc0 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake @@ -0,0 +1,12 @@ + +include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake") + +if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD") + if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}\"?") + set (RunCMake_TEST_FAILED "Not found expected '<base1> --LIBFLAG<base3> <other1>'.") + endif() +else() + if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}\"?") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> <base1> <other1>'.") + endif() +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-check.cmake index 32b58fe..32b58fe 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-result.txt index 8d98f9d..8d98f9d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-result.txt diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-check.cmake index 32b58fe..32b58fe 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake new file mode 100644 index 0000000..e04526a --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake @@ -0,0 +1,3 @@ +if (actual_stdout MATCHES "(/|-)-LIBFLAG${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Found unexpected '--LIBFLAG<base1>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-check.cmake new file mode 100644 index 0000000..255c9a6 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-check.cmake new file mode 100644 index 0000000..a8e0da7 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-check.cmake new file mode 100644 index 0000000..54cef2c --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP <base1> <other> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-check.cmake new file mode 100644 index 0000000..7c38134 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-check.cmake new file mode 100644 index 0000000..88b5cf6 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAGother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<base1> --LIBFLAG<other> --ITEMFLAG<other> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-check.cmake new file mode 100644 index 0000000..c473637 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<other> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-check.cmake index ab06726..ab06726 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-check.cmake index 858dcfe..ab06726 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-check.cmake @@ -1,4 +1,4 @@ if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") - set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.") endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-check.cmake index 62aa17c..62aa17c 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-check.cmake new file mode 100644 index 0000000..255c9a6 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-check.cmake new file mode 100644 index 0000000..a8e0da7 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP") + set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-check.cmake index a9fba20..a9fba20 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-check.cmake index 58c117e..58c117e 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-check.cmake index a9fba20..a9fba20 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-check.cmake index 58c117e..58c117e 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-check.cmake index d022f7e..d022f7e 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-check.cmake diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-check.cmake new file mode 100644 index 0000000..32b58fe --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-check.cmake new file mode 100644 index 0000000..32b58fe --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-check.cmake @@ -0,0 +1,4 @@ + +if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1") + set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.") +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-result.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-result.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake index f19112a..0aa11be 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake @@ -1,5 +1,12 @@ enable_language(C) +if(CMP0156 STREQUAL "NEW") + cmake_policy(SET CMP0156 NEW) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LIBRARIES_PROCESSING.cmake" "set(CMAKE_C_LINK_LIBRARIES_PROCESSING \"${CMAKE_C_LINK_LIBRARIES_PROCESSING}\")\n") +else() + cmake_policy(SET CMP0156 OLD) +endif() + # ensure command line is always displayed and do not use any response file set(CMAKE_VERBOSE_MAKEFILE TRUE) set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE) @@ -101,3 +108,10 @@ target_link_libraries(LinkLibrary_override_features4 PRIVATE "$<LINK_LIBRARY:fea set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE "feat3,base1,other1") set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_base1 feat2) set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_other1 feat2) + +# testing NTERFACE_LINK_LIBRARIES_DIRECT property +add_library(lib_with_LINK_LIBRARIES_DIRECT SHARED base.c) +set_property(TARGET lib_with_LINK_LIBRARIES_DIRECT PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT base1) + +add_library(LinkLibrary_consuming_LINK_LIBRARIES_DIRECT SHARED lib.c) +target_link_libraries(LinkLibrary_consuming_LINK_LIBRARIES_DIRECT PRIVATE $<LINK_LIBRARY:feat1,lib_with_LINK_LIBRARIES_DIRECT>) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 0f3a6b7..88a7e63 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake @@ -37,29 +37,35 @@ if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode" set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}") endif() - run_cmake(LINK_LIBRARY) - - run_cmake_target(LINK_LIBRARY simple1 LinkLibrary_simple1) - run_cmake_target(LINK_LIBRARY simple2 LinkLibrary_simple2) - run_cmake_target(LINK_LIBRARY group1 LinkLibrary_group1) - run_cmake_target(LINK_LIBRARY group2 LinkLibrary_group2) - run_cmake_target(LINK_LIBRARY nested-feature1 LinkLibrary_nested_feature1) - run_cmake_target(LINK_LIBRARY nested-feature2 LinkLibrary_nested_feature2) - run_cmake_target(LINK_LIBRARY link-items1 LinkLibrary_link_items1) - run_cmake_target(LINK_LIBRARY link-items2 LinkLibrary_link_items2) - run_cmake_target(LINK_LIBRARY link-items3 LinkLibrary_link_items3) - run_cmake_target(LINK_LIBRARY link-items4 LinkLibrary_link_items4) - run_cmake_target(LINK_LIBRARY mix-features1 LinkLibrary_mix_features1) - run_cmake_target(LINK_LIBRARY mix-features2 LinkLibrary_mix_features2) - run_cmake_target(LINK_LIBRARY mix-features3 LinkLibrary_mix_features3) - - # testing target property LINK_LIBRARY_OVERRIDE - run_cmake_target(LINK_LIBRARY override-features1 LinkLibrary_override_features1) - run_cmake_target(LINK_LIBRARY override-features2 LinkLibrary_override_features2) - run_cmake_target(LINK_LIBRARY override-with-DEFAULT LinkLibrary_override_with_default) - # testing target property LINK_LIBRARY_OVERRIDE_<LIBRARY> - run_cmake_target(LINK_LIBRARY override-features3 LinkLibrary_override_features3) - run_cmake_target(LINK_LIBRARY override-features4 LinkLibrary_override_features4) + foreach(policy IN ITEMS OLD NEW) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-${policy}-build) + run_cmake_with_options(LINK_LIBRARY -DCMP0156=${policy}) + + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} simple1 LinkLibrary_simple1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} simple2 LinkLibrary_simple2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} group1 LinkLibrary_group1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} group2 LinkLibrary_group2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} nested-feature1 LinkLibrary_nested_feature1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} nested-feature2 LinkLibrary_nested_feature2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items1 LinkLibrary_link_items1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items2 LinkLibrary_link_items2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items3 LinkLibrary_link_items3) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items4 LinkLibrary_link_items4) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features1 LinkLibrary_mix_features1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features2 LinkLibrary_mix_features2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features3 LinkLibrary_mix_features3) + + # testing target property LINK_LIBRARY_OVERRIDE + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features1 LinkLibrary_override_features1) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features2 LinkLibrary_override_features2) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-with-DEFAULT LinkLibrary_override_with_default) + # testing target property LINK_LIBRARY_OVERRIDE_<LIBRARY> + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features3 LinkLibrary_override_features3) + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features4 LinkLibrary_override_features4) + + # testing target property INTERFACE_LINK_LIBRARIES_DIRECT + run_cmake_target(LINK_LIBRARY-CMP0156-${policy} consuming_LINK_LIBRARIES_DIRECT LinkLibrary_consuming_LINK_LIBRARIES_DIRECT) + endforeach() run_cmake(imported-target) diff --git a/Tests/RunCMake/target_link_libraries/lib.c b/Tests/RunCMake/target_link_libraries/lib.c index b2d1b66..95c0e91 100644 --- a/Tests/RunCMake/target_link_libraries/lib.c +++ b/Tests/RunCMake/target_link_libraries/lib.c @@ -2,9 +2,9 @@ #if defined(_WIN32) __declspec(dllimport) #endif - void func_c(); + void func_c(void); -void lib() +void lib(void) { func_c(); } diff --git a/Tests/RunCMake/try_compile/LinkOptions.cmake b/Tests/RunCMake/try_compile/LinkOptions.cmake index 7fae35c..45cbedf 100644 --- a/Tests/RunCMake/try_compile/LinkOptions.cmake +++ b/Tests/RunCMake/try_compile/LinkOptions.cmake @@ -1,8 +1,5 @@ - enable_language(C) -cmake_policy(SET CMP0054 NEW) - set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}") if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (RunCMake_C_COMPILER_ID STREQUAL "MSVC" diff --git a/Tests/RunCMake/try_compile/lib.c b/Tests/RunCMake/try_compile/lib.c index b00c576..ce64a13 100644 --- a/Tests/RunCMake/try_compile/lib.c +++ b/Tests/RunCMake/try_compile/lib.c @@ -1,4 +1,4 @@ -void func() +void func(void) { } diff --git a/Tests/RunCMake/try_compile/main.c b/Tests/RunCMake/try_compile/main.c index 2128ead..6b1e682 100644 --- a/Tests/RunCMake/try_compile/main.c +++ b/Tests/RunCMake/try_compile/main.c @@ -1,4 +1,4 @@ -extern void func(); +extern void func(void); int main(void) { diff --git a/Tests/RunCMake/try_run/ConfigureLog-test.c b/Tests/RunCMake/try_run/ConfigureLog-test.c index 6a8f125..465069d 100644 --- a/Tests/RunCMake/try_run/ConfigureLog-test.c +++ b/Tests/RunCMake/try_run/ConfigureLog-test.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { fprintf(stderr, "Output, with backslash '\\', on stderr!\n"); fflush(stderr); /* make output deterministic even if stderr is buffered */ diff --git a/Tests/RunCMake/try_run/Inspect.cmake b/Tests/RunCMake/try_run/Inspect.cmake new file mode 100644 index 0000000..66698d6 --- /dev/null +++ b/Tests/RunCMake/try_run/Inspect.cmake @@ -0,0 +1,22 @@ +enable_language(C) +enable_language(CXX) +if(CMake_TEST_Fortran) + enable_language(Fortran) +endif() + +set(info "") +foreach(var + CMAKE_SYSTEM_NAME + CMAKE_C_COMPILER_ID + CMAKE_C_COMPILER_VERSION + CMAKE_CXX_COMPILER_ID + CMAKE_CXX_COMPILER_VERSION + CMAKE_Fortran_COMPILER_ID + CMAKE_Fortran_COMPILER_VERSION + ) + if(DEFINED ${var}) + string(APPEND info "set(${var} \"${${var}}\")\n") + endif() +endforeach() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/try_run/LinkOptions.cmake b/Tests/RunCMake/try_run/LinkOptions.cmake index b9a87f3..b19141c 100644 --- a/Tests/RunCMake/try_run/LinkOptions.cmake +++ b/Tests/RunCMake/try_run/LinkOptions.cmake @@ -1,8 +1,5 @@ - enable_language(C) -cmake_policy(SET CMP0054 NEW) - set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}") if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (RunCMake_C_COMPILER_ID STREQUAL "MSVC" diff --git a/Tests/RunCMake/try_run/LinkerLanguage.cmake b/Tests/RunCMake/try_run/LinkerLanguage.cmake new file mode 100644 index 0000000..137e198 --- /dev/null +++ b/Tests/RunCMake/try_run/LinkerLanguage.cmake @@ -0,0 +1,29 @@ +enable_language(CXX) +enable_language(Fortran) + +set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}") +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set (undef_flag -u _func) + else() + set (undef_flag -u func) + endif() +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set (undef_flag -u _func) +else() + set (undef_flag -u func) +endif() + +set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) +try_run(run_result compile_result + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/lib.cxx ${CMAKE_CURRENT_SOURCE_DIR}/main.f90 + COMPILE_OUTPUT_VARIABLE compile_out + RUN_OUTPUT_VARIABLE run_out + LINKER_LANGUAGE Fortran) + +if(NOT compile_result) + message(FATAL_ERROR "try_run(... LINKER_LANGUAGE Fortran) compilation failed:\n${compile_out}") +endif() +if(run_result STREQUAL "FAILED_TO_RUN") + message(FATAL_ERROR "try_run(... LINKER_LANGUAGE Fortran) execution failed:\n${run_out}") +endif() diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake index 62e3caf..7e9b2d1 100644 --- a/Tests/RunCMake/try_run/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake @@ -1,5 +1,16 @@ include(RunCMake) +# Detect information from the toolchain: +# - CMAKE_SYSTEM_NAME +# - CMAKE_C_COMPILER_ID +# - CMAKE_C_COMPILER_VERSION +# - CMAKE_CXX_COMPILER_ID +# - CMAKE_CXX_COMPILER_VERSION +run_cmake_with_options(Inspect + -DCMake_TEST_Fortran=${CMake_TEST_Fortran} + ) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + run_cmake(BinDirEmpty) run_cmake(BinDirRelative) run_cmake(NoOutputVariable) @@ -19,3 +30,9 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND run_cmake(LinkOptions) unset (RunCMake_TEST_OPTIONS) endif() + +if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND + CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$" AND + (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3)) + run_cmake(LinkerLanguage) +endif() diff --git a/Tests/RunCMake/try_run/lib.c b/Tests/RunCMake/try_run/lib.c index b00c576..ce64a13 100644 --- a/Tests/RunCMake/try_run/lib.c +++ b/Tests/RunCMake/try_run/lib.c @@ -1,4 +1,4 @@ -void func() +void func(void) { } diff --git a/Tests/RunCMake/try_run/lib.cxx b/Tests/RunCMake/try_run/lib.cxx new file mode 100644 index 0000000..b01a075 --- /dev/null +++ b/Tests/RunCMake/try_run/lib.cxx @@ -0,0 +1,4 @@ + +extern "C" void func() +{ +} diff --git a/Tests/RunCMake/try_run/main.c b/Tests/RunCMake/try_run/main.c index 2128ead..6b1e682 100644 --- a/Tests/RunCMake/try_run/main.c +++ b/Tests/RunCMake/try_run/main.c @@ -1,4 +1,4 @@ -extern void func(); +extern void func(void); int main(void) { diff --git a/Tests/RunCMake/try_run/main.f90 b/Tests/RunCMake/try_run/main.f90 new file mode 100644 index 0000000..29b933e --- /dev/null +++ b/Tests/RunCMake/try_run/main.f90 @@ -0,0 +1,12 @@ +program main + +implicit none + +interface +subroutine func() bind(C) +end subroutine +end interface + +call func() + +end program diff --git a/Tests/RuntimePath/bar1.c b/Tests/RuntimePath/bar1.c index 5dc5ec9..11b3a74 100644 --- a/Tests/RuntimePath/bar1.c +++ b/Tests/RuntimePath/bar1.c @@ -1,5 +1,5 @@ -extern int foo1(); -int bar1() +extern int foo1(void); +int bar1(void) { return foo1(); } diff --git a/Tests/RuntimePath/bar2.c b/Tests/RuntimePath/bar2.c index 9035489..a117f6f 100644 --- a/Tests/RuntimePath/bar2.c +++ b/Tests/RuntimePath/bar2.c @@ -1,5 +1,5 @@ -extern int foo2(); -int bar2() +extern int foo2(void); +int bar2(void) { return foo2(); } diff --git a/Tests/RuntimePath/foo1.c b/Tests/RuntimePath/foo1.c index dfcb0b7..14370ac 100644 --- a/Tests/RuntimePath/foo1.c +++ b/Tests/RuntimePath/foo1.c @@ -1,4 +1,4 @@ -int foo1() +int foo1(void) { return 0; } diff --git a/Tests/RuntimePath/foo2.c b/Tests/RuntimePath/foo2.c index 12a3e77..a105df9 100644 --- a/Tests/RuntimePath/foo2.c +++ b/Tests/RuntimePath/foo2.c @@ -1,4 +1,4 @@ -int foo2() +int foo2(void) { return 0; } diff --git a/Tests/RuntimePath/main.c b/Tests/RuntimePath/main.c index c71ee06..f25b493 100644 --- a/Tests/RuntimePath/main.c +++ b/Tests/RuntimePath/main.c @@ -1,5 +1,5 @@ -extern int bar1(); -int main() +extern int bar1(void); +int main(void) { return bar1(); } diff --git a/Tests/SetLang/bar.c b/Tests/SetLang/bar.c index 515e8c2..3a299dc 100644 --- a/Tests/SetLang/bar.c +++ b/Tests/SetLang/bar.c @@ -20,7 +20,7 @@ public: int i; }; -int main() +int main(void) { A a; if (a.i == 21) { diff --git a/Tests/Simple/simpleCLib.c b/Tests/Simple/simpleCLib.c index 6509865..90c4440 100644 --- a/Tests/Simple/simpleCLib.c +++ b/Tests/Simple/simpleCLib.c @@ -1,6 +1,6 @@ #include <stdio.h> -int FooBar() +int FooBar(void) { int class; int private = 10; diff --git a/Tests/SimpleCOnly/bar.c b/Tests/SimpleCOnly/bar.c index 570fee9..6c7605a 100644 --- a/Tests/SimpleCOnly/bar.c +++ b/Tests/SimpleCOnly/bar.c @@ -1,4 +1,4 @@ -int bar() +int bar(void) { return 5; } diff --git a/Tests/SimpleCOnly/foo.c b/Tests/SimpleCOnly/foo.c index c61d212..c12b865 100644 --- a/Tests/SimpleCOnly/foo.c +++ b/Tests/SimpleCOnly/foo.c @@ -1,4 +1,4 @@ -int foo() +int foo(void) { return 12; } diff --git a/Tests/SimpleCOnly/main.c b/Tests/SimpleCOnly/main.c index 54a7312..ad4c75c 100644 --- a/Tests/SimpleCOnly/main.c +++ b/Tests/SimpleCOnly/main.c @@ -3,7 +3,7 @@ extern int foo(); extern int bar(); -int main() +int main(void) { int i = foo(); int k = bar(); diff --git a/Tests/SourceFileIncludeDirProperty/main.c b/Tests/SourceFileIncludeDirProperty/main.c index 36144ca..d32e2ad 100644 --- a/Tests/SourceFileIncludeDirProperty/main.c +++ b/Tests/SourceFileIncludeDirProperty/main.c @@ -1,7 +1,7 @@ #include "header.h" -int main() +int main(void) { return 0; } diff --git a/Tests/SourceFileProperty/ICaseTest.c b/Tests/SourceFileProperty/ICaseTest.c index 454c721..70ae53e 100644 --- a/Tests/SourceFileProperty/ICaseTest.c +++ b/Tests/SourceFileProperty/ICaseTest.c @@ -1,6 +1,6 @@ #ifdef NEEDED_TO_WORK -int icasetest() +int icasetest(void) { return 0; } diff --git a/Tests/SourceGroups/main.c b/Tests/SourceGroups/main.c index f646b49..85a6c8b 100644 --- a/Tests/SourceGroups/main.c +++ b/Tests/SourceGroups/main.c @@ -14,7 +14,7 @@ extern int tree_foobar(void); extern int tree_baz(void); extern int nested(void); -int main() +int main(void) { printf("foo: %d bar: %d foobar: %d barbar: %d baz: %d\n", foo(), bar(), foobar(), barbar(), baz()); diff --git a/Tests/SourceGroups/sub2/main.c b/Tests/SourceGroups/sub2/main.c index 4cd8ae0..6a195bc 100644 --- a/Tests/SourceGroups/sub2/main.c +++ b/Tests/SourceGroups/sub2/main.c @@ -3,7 +3,7 @@ extern int qax(void); extern int qux(void); -int main() +int main(void) { printf("qux: %d qax: %d\n", qux(), qax()); diff --git a/Tests/SubDir/AnotherSubdir/pair+int.int.c b/Tests/SubDir/AnotherSubdir/pair+int.int.c index b7a6237..6e719fe 100644 --- a/Tests/SubDir/AnotherSubdir/pair+int.int.c +++ b/Tests/SubDir/AnotherSubdir/pair+int.int.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDir/AnotherSubdir/pair_int.int.c b/Tests/SubDir/AnotherSubdir/pair_int.int.c index b7a6237..6e719fe 100644 --- a/Tests/SubDir/AnotherSubdir/pair_int.int.c +++ b/Tests/SubDir/AnotherSubdir/pair_int.int.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDir/AnotherSubdir/secondone.c b/Tests/SubDir/AnotherSubdir/secondone.c index 3e9e5af..0dd9c6b 100644 --- a/Tests/SubDir/AnotherSubdir/secondone.c +++ b/Tests/SubDir/AnotherSubdir/secondone.c @@ -1,6 +1,6 @@ #include <stdio.h> -void secondone() +void secondone(void) { printf("Hello again\n"); } diff --git a/Tests/SubDir/AnotherSubdir/testfromsubdir.c b/Tests/SubDir/AnotherSubdir/testfromsubdir.c index 34b6e7a..5e72140 100644 --- a/Tests/SubDir/AnotherSubdir/testfromsubdir.c +++ b/Tests/SubDir/AnotherSubdir/testfromsubdir.c @@ -4,7 +4,7 @@ void secondone(); void pair_stuff(); void vcl_stuff(); -int main() +int main(void) { printf("Hello from subdirectory\n"); secondone(); diff --git a/Tests/SubDir/ThirdSubDir/pair+int.int1.c b/Tests/SubDir/ThirdSubDir/pair+int.int1.c index b7a6237..6e719fe 100644 --- a/Tests/SubDir/ThirdSubDir/pair+int.int1.c +++ b/Tests/SubDir/ThirdSubDir/pair+int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDir/ThirdSubDir/pair_int.int1.c b/Tests/SubDir/ThirdSubDir/pair_int.int1.c index b7a6237..6e719fe 100644 --- a/Tests/SubDir/ThirdSubDir/pair_int.int1.c +++ b/Tests/SubDir/ThirdSubDir/pair_int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c b/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c index 95a66ee..3cf9442 100644 --- a/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c +++ b/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_p_stuff() +void pair_p_stuff(void) { printf("Placeholder for another strange file in subdirectory\n"); } diff --git a/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c b/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c index d162084..448ef52 100644 --- a/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c +++ b/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c @@ -5,7 +5,7 @@ void pair_stuff(); void pair_p_stuff(); void vcl_stuff(); -int main() +int main(void) { printf("Hello from subdirectory\n"); secondone(); diff --git a/Tests/SubDir/ThirdSubDir/thirdone.c b/Tests/SubDir/ThirdSubDir/thirdone.c index 3e9e5af..0dd9c6b 100644 --- a/Tests/SubDir/ThirdSubDir/thirdone.c +++ b/Tests/SubDir/ThirdSubDir/thirdone.c @@ -1,6 +1,6 @@ #include <stdio.h> -void secondone() +void secondone(void) { printf("Hello again\n"); } diff --git a/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c b/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c index a0c60f7..689c827 100644 --- a/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c +++ b/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c @@ -1,6 +1,6 @@ #include <stdio.h> -void vcl_stuff() +void vcl_stuff(void) { printf("Placeholder for a file with strange name\n"); } diff --git a/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c b/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c index a0c60f7..689c827 100644 --- a/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c +++ b/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c @@ -1,6 +1,6 @@ #include <stdio.h> -void vcl_stuff() +void vcl_stuff(void) { printf("Placeholder for a file with strange name\n"); } diff --git a/Tests/SubDirSpaces/Another Subdir/pair+int.int.c b/Tests/SubDirSpaces/Another Subdir/pair+int.int.c index b7a6237..6e719fe 100644 --- a/Tests/SubDirSpaces/Another Subdir/pair+int.int.c +++ b/Tests/SubDirSpaces/Another Subdir/pair+int.int.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDirSpaces/Another Subdir/pair_int.int.c b/Tests/SubDirSpaces/Another Subdir/pair_int.int.c index b7a6237..6e719fe 100644 --- a/Tests/SubDirSpaces/Another Subdir/pair_int.int.c +++ b/Tests/SubDirSpaces/Another Subdir/pair_int.int.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDirSpaces/Another Subdir/secondone.c b/Tests/SubDirSpaces/Another Subdir/secondone.c index 3e9e5af..0dd9c6b 100644 --- a/Tests/SubDirSpaces/Another Subdir/secondone.c +++ b/Tests/SubDirSpaces/Another Subdir/secondone.c @@ -1,6 +1,6 @@ #include <stdio.h> -void secondone() +void secondone(void) { printf("Hello again\n"); } diff --git a/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c b/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c index 34b6e7a..5e72140 100644 --- a/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c +++ b/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c @@ -4,7 +4,7 @@ void secondone(); void pair_stuff(); void vcl_stuff(); -int main() +int main(void) { printf("Hello from subdirectory\n"); secondone(); diff --git a/Tests/SubDirSpaces/Some(x86) Sources/test.c b/Tests/SubDirSpaces/Some(x86) Sources/test.c index 66568d4..5baf240 100644 --- a/Tests/SubDirSpaces/Some(x86) Sources/test.c +++ b/Tests/SubDirSpaces/Some(x86) Sources/test.c @@ -1,3 +1,3 @@ -void testOdd() +void testOdd(void) { } diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c index b7a6237..6e719fe 100644 --- a/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c +++ b/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c index b7a6237..6e719fe 100644 --- a/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c +++ b/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_stuff() +void pair_stuff(void) { printf("Placeholder for a strange file in subdirectory\n"); } diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c index 95a66ee..3cf9442 100644 --- a/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c +++ b/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c @@ -1,6 +1,6 @@ #include <stdio.h> -void pair_p_stuff() +void pair_p_stuff(void) { printf("Placeholder for another strange file in subdirectory\n"); } diff --git a/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c b/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c index fa6c33c..2bff654 100644 --- a/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c +++ b/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c @@ -7,7 +7,7 @@ void vcl_stuff(); #ifdef CMAKE_PAREN void testOdd(); #endif -int main() +int main(void) { printf("Hello from subdirectory\n"); secondone(); diff --git a/Tests/SubDirSpaces/ThirdSubDir/thirdone.c b/Tests/SubDirSpaces/ThirdSubDir/thirdone.c index 3e9e5af..0dd9c6b 100644 --- a/Tests/SubDirSpaces/ThirdSubDir/thirdone.c +++ b/Tests/SubDirSpaces/ThirdSubDir/thirdone.c @@ -1,6 +1,6 @@ #include <stdio.h> -void secondone() +void secondone(void) { printf("Hello again\n"); } diff --git a/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c b/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c index a0c60f7..689c827 100644 --- a/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c +++ b/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c @@ -1,6 +1,6 @@ #include <stdio.h> -void vcl_stuff() +void vcl_stuff(void) { printf("Placeholder for a file with strange name\n"); } diff --git a/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c b/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c index a0c60f7..689c827 100644 --- a/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c +++ b/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c @@ -1,6 +1,6 @@ #include <stdio.h> -void vcl_stuff() +void vcl_stuff(void) { printf("Placeholder for a file with strange name\n"); } diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index 13cf2b1..2aa5710 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.3) if(POLICY CMP0126) cmake_policy(SET CMP0126 NEW) endif() +if(POLICY CMP0157) + cmake_policy(SET CMP0157 NEW) +endif() # NOTE: Force the Release mode configuration as there are some issues with the # debug information handling on macOS on certain Xcode builds. @@ -40,6 +43,13 @@ add_library(N N.swift) target_link_libraries(N PUBLIC M) +if(NOT XCODE_VERSION OR XCODE_VERSION VERSION_GREATER_EQUAL 9.0) + # TODO: Add a wholemodule object-library test once that is working + add_library(O OBJECT O.swift L.swift) + target_link_libraries(N PUBLIC O) + set_target_properties(O PROPERTIES Swift_COMPILATION_MODE "incremental") +endif() + # Dummy to make sure generation works with such targets. add_library(SwiftIface INTERFACE) target_link_libraries(SwiftOnly PRIVATE SwiftIface) @@ -54,3 +64,21 @@ target_link_libraries(SwiftOnly PRIVATE SwiftIface) if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2) add_subdirectory("SwiftPlugin") endif() + +function(test_cmp0157_default mode) + if(POLICY CMP0157) + cmake_policy(GET CMP0157 cmp0157_wmo) + if(cmp0157_wmo STREQUAL "NEW") + set(CMAKE_Swift_COMPILATION_MODE "${mode}") + add_executable(hi_${mode} main.swift) + get_target_property(${mode}_swift_comp_mode hi_${mode} "Swift_COMPILATION_MODE") + if(NOT ${mode}_swift_comp_mode STREQUAL ${mode}) + message(SEND_ERROR "expected ${mode} -- found ${${mode}_swift_comp_mode}") + endif() + endif() + endif() +endfunction() + +test_cmp0157_default("wholemodule") +test_cmp0157_default("incremental") +test_cmp0157_default("singlefile") diff --git a/Tests/SwiftOnly/O.swift b/Tests/SwiftOnly/O.swift new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/SwiftOnly/O.swift diff --git a/Tests/TryCompile/Inner/innerexe.c b/Tests/TryCompile/Inner/innerexe.c index e329c5f..6d1e3da 100644 --- a/Tests/TryCompile/Inner/innerexe.c +++ b/Tests/TryCompile/Inner/innerexe.c @@ -1,5 +1,5 @@ extern int innerlib(void); -int main() +int main(void) { return innerlib(); } diff --git a/Tests/TryCompile/check_a_b.c b/Tests/TryCompile/check_a_b.c index 05fba0f..65535ac 100644 --- a/Tests/TryCompile/check_a_b.c +++ b/Tests/TryCompile/check_a_b.c @@ -4,7 +4,7 @@ #ifndef DEF_B # error DEF_B not defined #endif -int main() +int main(void) { return 0; } diff --git a/Tests/TryCompile/exit_success.c b/Tests/TryCompile/exit_success.c index 82f5b5f..302d926 100644 --- a/Tests/TryCompile/exit_success.c +++ b/Tests/TryCompile/exit_success.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { printf("hello world\n"); return 0; diff --git a/Tests/TryCompile/exit_with_error.c b/Tests/TryCompile/exit_with_error.c index dbddcf5..dfa36fc 100644 --- a/Tests/TryCompile/exit_with_error.c +++ b/Tests/TryCompile/exit_with_error.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { printf("hello world\n"); return 1; diff --git a/Tests/TryCompile/pass.c b/Tests/TryCompile/pass.c index f8b643a..8488f4e 100644 --- a/Tests/TryCompile/pass.c +++ b/Tests/TryCompile/pass.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/TryCompile/pass2a.c b/Tests/TryCompile/pass2a.c index 27c377b..8375a8e 100644 --- a/Tests/TryCompile/pass2a.c +++ b/Tests/TryCompile/pass2a.c @@ -1,5 +1,5 @@ extern int pass2b(void); -int main() +int main(void) { return pass2b(); } diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c index 84ded1f..c25b7d9 100644 --- a/Tests/TryCompile/stdout_and_stderr.c +++ b/Tests/TryCompile/stdout_and_stderr.c @@ -1,6 +1,6 @@ #include <stdio.h> -int main() +int main(void) { fputs("error\n", stderr); puts("hello world\n"); diff --git a/Tests/Unset/unset.c b/Tests/Unset/unset.c index f8b643a..8488f4e 100644 --- a/Tests/Unset/unset.c +++ b/Tests/Unset/unset.c @@ -1,4 +1,4 @@ -int main() +int main(void) { return 0; } diff --git a/Tests/VSGNUFortran/c_code/main.c b/Tests/VSGNUFortran/c_code/main.c index 60c1120..be868c3 100644 --- a/Tests/VSGNUFortran/c_code/main.c +++ b/Tests/VSGNUFortran/c_code/main.c @@ -1,6 +1,6 @@ #include <HelloWorldFCMangle.h> /* created by FortranCInterface */ extern void FC_hello(void); -int main() +int main(void) { FC_hello(); return 0; diff --git a/Tests/Visibility/bar.c b/Tests/Visibility/bar.c index b72a1a5..e1f4df6 100644 --- a/Tests/Visibility/bar.c +++ b/Tests/Visibility/bar.c @@ -1,3 +1,3 @@ -void bar() +void bar(void) { } diff --git a/Tests/X11/X11.c b/Tests/X11/X11.c index 3a6f9f0..9f002f6 100644 --- a/Tests/X11/X11.c +++ b/Tests/X11/X11.c @@ -4,7 +4,7 @@ # include <X11/Xlib.h> # include <X11/Xutil.h> -int main() +int main(void) { printf("There is X on this computer\n"); return 0; @@ -12,7 +12,7 @@ int main() #else -int main() +int main(void) { printf("No X on this computer\n"); return 0; diff --git a/Tests/XCTest/FrameworkExample/FrameworkExample.c b/Tests/XCTest/FrameworkExample/FrameworkExample.c index 77361c8..9da1bf2 100644 --- a/Tests/XCTest/FrameworkExample/FrameworkExample.c +++ b/Tests/XCTest/FrameworkExample/FrameworkExample.c @@ -1,6 +1,6 @@ #include "FrameworkExample.h" -int FourtyTwo() +int FourtyTwo(void) { return 42; } diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index 12b5407..52a31eb 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.26 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.27 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 a944daf..e5b63c9 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream <curl-library@lists.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-8_4_0" +readonly tag="curl-8_5_0" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash index 5a4f11a..724303e 100755 --- a/Utilities/Scripts/update-libarchive.bash +++ b/Utilities/Scripts/update-libarchive.bash @@ -8,7 +8,7 @@ readonly name="LibArchive" readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>" readonly subtree="Utilities/cmlibarchive" readonly repo="https://github.com/libarchive/libarchive.git" -readonly tag="v3.6.2" +readonly tag="v3.7.2" readonly shortlog=false readonly paths=" CMakeLists.txt diff --git a/Utilities/Scripts/update-librhash.bash b/Utilities/Scripts/update-librhash.bash index ea7e655..b3d078b 100755 --- a/Utilities/Scripts/update-librhash.bash +++ b/Utilities/Scripts/update-librhash.bash @@ -8,7 +8,7 @@ readonly name="librhash" readonly ownership="librhash upstream <kwrobot@kitware.com>" readonly subtree="Utilities/cmlibrhash" readonly repo="https://github.com/rhash/rhash.git" -readonly tag="v1.3.9" +readonly tag="v1.4.4" readonly shortlog=false readonly paths=" COPYING @@ -31,6 +31,7 @@ readonly paths=" librhash/sha512.c librhash/sha512.h librhash/ustd.h + librhash/util.c librhash/util.h " diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index 694ba3c..4ffcdd7 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.26 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index f8651e1..20e1340 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in @@ -88,7 +88,11 @@ html_favicon = '@conf_path@/static/cmake-favicon.ico' # qthelp_namespace = "org.cmake" # qthelp_qch_name = "CMake.qch" -linkcheck_ignore = [r'about:|https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack'] +linkcheck_ignore = [ + r'about:', + r'https://gitlab\.kitware\.com/cmake/community/-/wikis/doc/cpack', + r'https://www.intel.com/', +] linkcheck_allowed_redirects = { r'https://cdash\.org': r'https://www\.cdash\.org/', diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake index 8f68777..311d58e 100644 --- a/Utilities/cmThirdPartyChecks.cmake +++ b/Utilities/cmThirdPartyChecks.cmake @@ -43,8 +43,8 @@ if(WIN32) set(HAVE_CHROOT 0) set(HAVE_COPYFILE_H 0) set(HAVE_CRYPTO_H 0) - set(HAVE__CTIME64_S 1) set(HAVE_CTIME_R 0) + set(HAVE_CTIME_S 1) set(HAVE_CYGWIN_CONV_PATH 0) set(HAVE_DES_H 0) set(HAVE_DIRECT_H 1) @@ -64,6 +64,8 @@ if(WIN32) set(HAVE_FCNTL_H 1) set(HAVE_FCNTL_O_NONBLOCK 0) set(HAVE_FDOPENDIR 0) + set(HAVE_FNMATCH 0) + set(HAVE_FNMATCH_H 0) set(HAVE_FORK 0) set(HAVE_FREEADDRINFO 1) set(HAVE_FREEIFADDRS 0) @@ -82,6 +84,7 @@ if(WIN32) set(HAVE_GETGRGID_R 0) set(HAVE_GETGRNAM_R 0) set(HAVE_GETHOSTBYNAME 1) + set(HAVE_GETLINE 0) set(HAVE_GETPAGESIZE 0) set(HAVE_GETPEERNAME 1) set(HAVE_GETPID 1) @@ -94,8 +97,8 @@ if(WIN32) set(HAVE_GETSOCKNAME 1) set(HAVE_GETVFSBYNAME 0) set(HAVE_GLIBC_STRERROR_R 0) - set(HAVE__GMTIME64_S 1) set(HAVE_GMTIME_R 0) + set(HAVE_GMTIME_S 1) set(HAVE_GRP_H 0) set(HAVE_IDN2_H 0) set(HAVE_IFADDRS_H 0) @@ -126,8 +129,8 @@ if(WIN32) set(HAVE_LINUX_FS_H 0) set(HAVE_LINUX_MAGIC_H 0) set(HAVE_LINUX_TYPES_H 0) - set(HAVE__LOCALTIME64_S 1) set(HAVE_LOCALTIME_R 0) + set(HAVE_LOCALTIME_S 0) set(HAVE_LSTAT 0) set(HAVE_LUTIMES 0) set(HAVE_MACH_ABSOLUTE_TIME 0) @@ -136,7 +139,7 @@ if(WIN32) set(HAVE_MEMORY_H 1) set(HAVE_MKDIR 1) set(HAVE_MKFIFO 0) - set(HAVE__MKGMTIME64 1) + set(HAVE__MKGMTIME 1) set(HAVE_MKNOD 0) set(HAVE_MMAP 0) set(HAVE_MSG_NOSIGNAL 0) @@ -216,6 +219,7 @@ if(WIN32) set(HAVE_SYS_MKDEV_H 0) set(HAVE_SYS_MOUNT_H 0) set(HAVE_SYS_POLL_H 0) + set(HAVE_SYS_QUEUE_H 0) set(HAVE_SYS_RESOURCE_H 0) set(HAVE_SYS_RICHACL_H 0) set(HAVE_SYS_SELECT_H 0) diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index ea80ec8..e546286 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -23,7 +23,6 @@ ***************************************************************************/ #ifdef HAVE_FCNTL_O_NONBLOCK - /* headers for FCNTL_O_NONBLOCK test */ #include <sys/types.h> #include <unistd.h> @@ -45,14 +44,13 @@ #error "O_NONBLOCK does not work on this platform" #endif -int -main () +int main(void) { - /* O_NONBLOCK source test */ - int flags = 0; - if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) - return 1; - return 0; + /* O_NONBLOCK source test */ + int flags = 0; + if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) + return 1; + return 0; } #endif @@ -108,36 +106,16 @@ int main(void) } #endif -#ifdef HAVE_SOCKLEN_T -#ifdef _WIN32 -#include <ws2tcpip.h> -#else -#include <sys/types.h> -#include <sys/socket.h> -#endif -int -main () -{ -if ((socklen_t *) 0) - return 0; -if (sizeof (socklen_t)) - return 0; - ; - return 0; -} -#endif #ifdef HAVE_IN_ADDR_T #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> - -int -main () +int main(void) { -if ((in_addr_t *) 0) - return 0; -if (sizeof (in_addr_t)) - return 0; + if((in_addr_t *) 0) + return 0; + if(sizeof(in_addr_t)) + return 0; ; return 0; } @@ -150,11 +128,10 @@ if (sizeof (in_addr_t)) #ifdef HAVE_STDBOOL_H #include <stdbool.h> #endif -int -main () +int main(void) { -if (sizeof (bool *) ) - return 0; + if(sizeof(bool *)) + return 0; ; return 0; } @@ -165,8 +142,9 @@ if (sizeof (bool *) ) #include <stdarg.h> #include <string.h> #include <float.h> -int main() { return 0; } +int main(void) { return 0; } #endif + #ifdef HAVE_FILE_OFFSET_BITS #ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS @@ -181,104 +159,95 @@ int main() { return 0; } int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; -int main () { ; return 0; } +int main(void) { ; return 0; } #endif + #ifdef HAVE_IOCTLSOCKET /* includes start */ #ifdef HAVE_WINDOWS_H # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif +# include <windows.h> #endif - -int -main () +int main(void) { - -/* ioctlsocket source code */ - int socket; - unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); - + /* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); ; return 0; } #endif + #ifdef HAVE_IOCTLSOCKET_CAMEL /* includes start */ #ifdef HAVE_WINDOWS_H # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif +# include <windows.h> #endif - -int -main () +int main(void) { - -/* IoctlSocket source code */ - if(0 != IoctlSocket(0, 0, 0)) - return 1; + /* IoctlSocket source code */ + if(0 != IoctlSocket(0, 0, 0)) + return 1; ; return 0; } #endif + #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO /* includes start */ #ifdef HAVE_WINDOWS_H # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif +# include <windows.h> #endif - -int -main () +int main(void) { - -/* IoctlSocket source code */ - long flags = 0; - if(0 != IoctlSocket(0, FIONBIO, &flags)) - return 1; + /* IoctlSocket source code */ + long flags = 0; + if(0 != IoctlSocket(0, FIONBIO, &flags)) + return 1; ; return 0; } #endif + #ifdef HAVE_IOCTLSOCKET_FIONBIO /* includes start */ #ifdef HAVE_WINDOWS_H # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif +# include <windows.h> #endif - -int -main () +int main(void) { - - int flags = 0; - if(0 != ioctlsocket(0, FIONBIO, &flags)) - return 1; - + int flags = 0; + if(0 != ioctlsocket(0, FIONBIO, &flags)) + return 1; ; return 0; } #endif + #ifdef HAVE_IOCTL_FIONBIO /* headers for FIONBIO test */ /* includes start */ @@ -297,19 +266,16 @@ main () #ifdef HAVE_STROPTS_H # include <stropts.h> #endif - -int -main () +int main(void) { - - int flags = 0; - if(0 != ioctl(0, FIONBIO, &flags)) - return 1; - + int flags = 0; + if(0 != ioctl(0, FIONBIO, &flags)) + return 1; ; return 0; } #endif + #ifdef HAVE_IOCTL_SIOCGIFADDR /* headers for FIONBIO test */ /* includes start */ @@ -329,28 +295,26 @@ main () # include <stropts.h> #endif #include <net/if.h> - -int -main () +int main(void) { - struct ifreq ifr; - if(0 != ioctl(0, SIOCGIFADDR, &ifr)) - return 1; - + struct ifreq ifr; + if(0 != ioctl(0, SIOCGIFADDR, &ifr)) + return 1; ; return 0; } #endif + #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK /* includes start */ #ifdef HAVE_WINDOWS_H # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif +# include <windows.h> #endif /* includes start */ #ifdef HAVE_SYS_TYPES_H @@ -360,30 +324,30 @@ main () # include <sys/socket.h> #endif /* includes end */ - -int -main () +int main(void) { - if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) - return 1; + if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0)) + return 1; ; return 0; } #endif + #ifdef HAVE_GLIBC_STRERROR_R #include <string.h> #include <errno.h> void check(char c) {} -int -main () { +int main(void) +{ char buffer[1024]; /* This will not compile if strerror_r does not return a char* */ check(strerror_r(EACCES, buffer, sizeof(buffer))[0]); return 0; } #endif + #ifdef HAVE_POSIX_STRERROR_R #include <string.h> #include <errno.h> @@ -391,92 +355,51 @@ main () { /* float, because a pointer can't be implicitly cast to float */ void check(float f) {} -int -main () { +int main(void) +{ char buffer[1024]; /* This will not compile if strerror_r does not return an int */ check(strerror_r(EACCES, buffer, sizeof(buffer))); return 0; } #endif + #ifdef HAVE_FSETXATTR_6 #include <sys/xattr.h> /* header from libc, not from libattr */ -int -main() { +int main(void) +{ fsetxattr(0, 0, 0, 0, 0, 0); return 0; } #endif + #ifdef HAVE_FSETXATTR_5 #include <sys/xattr.h> /* header from libc, not from libattr */ -int -main() { +int main(void) +{ fsetxattr(0, 0, 0, 0, 0); return 0; } #endif + #ifdef HAVE_CLOCK_GETTIME_MONOTONIC #include <time.h> -int -main() { +int main(void) +{ struct timespec ts = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &ts); return 0; } #endif + #ifdef HAVE_BUILTIN_AVAILABLE -int -main() { +int main(void) +{ if(__builtin_available(macOS 10.12, *)) {} return 0; } #endif -#ifdef HAVE_VARIADIC_MACROS_C99 -#define c99_vmacro3(first, ...) fun3(first, __VA_ARGS__) -#define c99_vmacro2(first, ...) fun2(first, __VA_ARGS__) -int fun3(int arg1, int arg2, int arg3); -int fun2(int arg1, int arg2); - -int fun3(int arg1, int arg2, int arg3) { - return arg1 + arg2 + arg3; -} -int fun2(int arg1, int arg2) { - return arg1 + arg2; -} - -int -main() { - int res3 = c99_vmacro3(1, 2, 3); - int res2 = c99_vmacro2(1, 2); - (void)res3; - (void)res2; - return 0; -} -#endif -#ifdef HAVE_VARIADIC_MACROS_GCC -#define gcc_vmacro3(first, args...) fun3(first, args) -#define gcc_vmacro2(first, args...) fun2(first, args) - -int fun3(int arg1, int arg2, int arg3); -int fun2(int arg1, int arg2); - -int fun3(int arg1, int arg2, int arg3) { - return arg1 + arg2 + arg3; -} -int fun2(int arg1, int arg2) { - return arg1 + arg2; -} - -int -main() { - int res3 = gcc_vmacro3(1, 2, 3); - int res2 = gcc_vmacro2(1, 2); - (void)res3; - (void)res2; - return 0; -} -#endif #ifdef HAVE_ATOMIC /* includes start */ #ifdef HAVE_SYS_TYPES_H @@ -490,17 +413,24 @@ main() { #endif /* includes end */ -int -main() { +int main(void) +{ _Atomic int i = 1; i = 0; /* Force an atomic-write operation. */ return i; } #endif + #ifdef HAVE_WIN32_WINNT /* includes start */ -#ifdef WIN32 -# include "../lib/setup-win32.h" +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOGDI +# define NOGDI +# endif +# include <windows.h> #endif /* includes end */ @@ -508,8 +438,8 @@ main() { #define expand(x) enquote(x) #pragma message("_WIN32_WINNT=" expand(_WIN32_WINNT)) -int -main() { +int main(void) +{ return 0; } #endif diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake index 973e6ad..0ea9e0c 100644 --- a/Utilities/cmcurl/CMake/FindZstd.cmake +++ b/Utilities/cmcurl/CMake/FindZstd.cmake @@ -56,11 +56,18 @@ find_library(Zstd_LIBRARY NAMES zstd ${PC_Zstd_LIBRARY_DIRS} ) +if(Zstd_INCLUDE_DIR) + file(READ "${Zstd_INCLUDE_DIR}/zstd.h" _zstd_header) + string(REGEX MATCH ".*define ZSTD_VERSION_MAJOR *([0-9]+).*define ZSTD_VERSION_MINOR *([0-9]+).*define ZSTD_VERSION_RELEASE *([0-9]+)" _zstd_ver "${_zstd_header}") + set(Zstd_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") +endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Zstd REQUIRED_VARS Zstd_LIBRARY Zstd_INCLUDE_DIR + VERSION_VAR Zstd_VERSION ) if(Zstd_FOUND) diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index e12bf30..7ad2f5c 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake @@ -23,19 +23,6 @@ ########################################################################### #File defines convenience macros for available feature testing -# This macro checks if the symbol exists in the library and if it -# does, it prepends library to the list. It is intended to be called -# multiple times with a sequence of possibly dependent libraries in -# order of least-to-most-dependent. Some libraries depend on others -# to link correctly. -macro(check_library_exists_concat LIBRARY SYMBOL VARIABLE) - check_library_exists("${LIBRARY};${CURL_LIBS}" ${SYMBOL} "${CMAKE_LIBRARY_PATH}" - ${VARIABLE}) - if(${VARIABLE}) - set(CURL_LIBS ${LIBRARY} ${CURL_LIBS}) - endif() -endmacro() - # Check if header file exists and add it to the list. # This macro is intended to be called multiple times with a sequence of # possibly dependent header files. Some headers depend on others to be diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index d67a905..a613f6e 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -35,13 +35,13 @@ endmacro() set(signature_call_conv) if(HAVE_WINDOWS_H) - add_header_include(HAVE_WINSOCK2_H "winsock2.h") - add_header_include(HAVE_WINDOWS_H "windows.h") set(_source_epilogue "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif") + add_header_include(HAVE_WINSOCK2_H "winsock2.h") + add_header_include(HAVE_WINDOWS_H "windows.h") set(signature_call_conv "PASCAL") if(WIN32) - set(CMAKE_REQUIRED_LIBRARIES ws2_32) + set(CMAKE_REQUIRED_LIBRARIES "ws2_32") endif() else() add_header_include(HAVE_SYS_TYPES_H "sys/types.h") @@ -71,11 +71,11 @@ int main(void) { }" HAVE_STRUCT_TIMEVAL) if(HAVE_WINDOWS_H) - set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h) + set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") else() set(CMAKE_EXTRA_INCLUDE_FILES) if(HAVE_SYS_SOCKET_H) - set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) + set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") endif() endif() @@ -172,7 +172,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) }" HAVE_H_ERRNO) if(NOT HAVE_H_ERRNO) - check_c_source_runs("${_source_epilogue} + check_c_source_compiles("${_source_epilogue} int main(void) { h_errno = 2; @@ -201,7 +201,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) set(_source_epilogue "${_save_epilogue}") endif() -if(NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) set(_save_epilogue "${_source_epilogue}") set(_source_epilogue "#undef inline") diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake index 1310cb4..5a0d156 100644 --- a/Utilities/cmcurl/CMake/PickyWarnings.cmake +++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake @@ -52,8 +52,8 @@ if(PICKY_COMPILER) # Assume these options always exist with both clang and gcc. # Require clang 3.0 / gcc 2.95 or later. list(APPEND WPICKY_ENABLE - -Wbad-function-cast # clang 3.0 gcc 2.95 - -Wconversion # clang 3.0 gcc 2.95 + -Wbad-function-cast # clang 2.7 gcc 2.95 + -Wconversion # clang 2.7 gcc 2.95 -Winline # clang 1.0 gcc 1.0 -Wmissing-declarations # clang 1.0 gcc 2.7 -Wmissing-prototypes # clang 1.0 gcc 1.0 @@ -70,23 +70,37 @@ if(PICKY_COMPILER) # Always enable with clang, version dependent with gcc set(WPICKY_COMMON_OLD + -Waddress # clang 2.7 gcc 4.3 + -Wattributes # clang 2.7 gcc 4.1 -Wcast-align # clang 1.0 gcc 4.2 -Wdeclaration-after-statement # clang 1.0 gcc 3.4 - -Wempty-body # clang 3.0 gcc 4.3 + -Wdiv-by-zero # clang 2.7 gcc 4.1 + -Wempty-body # clang 2.7 gcc 4.3 -Wendif-labels # clang 1.0 gcc 3.3 -Wfloat-equal # clang 1.0 gcc 2.96 (3.0) - -Wignored-qualifiers # clang 3.0 gcc 4.3 + -Wformat-security # clang 2.7 gcc 4.1 + -Wignored-qualifiers # clang 2.8 gcc 4.3 + -Wmissing-field-initializers # clang 2.7 gcc 4.1 + -Wmissing-noreturn # clang 2.7 gcc 4.1 -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) - -Wno-sign-conversion # clang 3.0 gcc 4.3 + -Wno-sign-conversion # clang 2.9 gcc 4.3 -Wno-system-headers # clang 1.0 gcc 3.0 + # -Wpadded # clang 2.9 gcc 4.1 # Not used because we cannot change public structs + -Wredundant-decls # clang 2.7 gcc 4.1 + -Wold-style-definition # clang 2.7 gcc 3.4 -Wstrict-prototypes # clang 1.0 gcc 3.3 - -Wtype-limits # clang 3.0 gcc 4.3 + # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used because this basically disallows default case + -Wtype-limits # clang 2.7 gcc 4.3 + -Wunreachable-code # clang 2.7 gcc 4.1 + # -Wunused-macros # clang 2.7 gcc 4.1 # Not practical + -Wunused-parameter # clang 2.7 gcc 4.1 -Wvla # clang 2.8 gcc 4.3 ) set(WPICKY_COMMON -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 + -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) @@ -95,12 +109,16 @@ if(PICKY_COMPILER) ${WPICKY_COMMON_OLD} -Wshift-sign-overflow # clang 2.9 -Wshorten-64-to-32 # clang 1.0 + -Wlanguage-extension-token # clang 3.0 ) # Enable based on compiler version if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON} + -Wunreachable-code-break # clang 3.5 appleclang 6.0 + -Wheader-guard # clang 3.4 appleclang 5.1 + -Wsometimes-uninitialized # clang 3.2 appleclang 4.6 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR @@ -125,6 +143,7 @@ if(PICKY_COMPILER) if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3) list(APPEND WPICKY_ENABLE ${WPICKY_COMMON_OLD} + -Wclobbered # gcc 4.3 -Wmissing-parameter-type # gcc 4.3 -Wold-style-declaration # gcc 4.3 -Wstrict-aliasing=3 # gcc 4.0 @@ -159,7 +178,7 @@ if(PICKY_COMPILER) -Walloc-zero # gcc 7.0 -Wduplicated-branches # gcc 7.0 -Wformat-overflow=2 # gcc 7.0 - -Wformat-truncation=1 # gcc 7.0 + -Wformat-truncation=2 # gcc 7.0 -Wrestrict # gcc 7.0 ) endif() @@ -174,11 +193,11 @@ if(PICKY_COMPILER) unset(WPICKY) - foreach(_CCOPT ${WPICKY_ENABLE}) + foreach(_CCOPT IN LISTS WPICKY_ENABLE) set(WPICKY "${WPICKY} ${_CCOPT}") endforeach() - foreach(_CCOPT ${WPICKY_DETECT}) + foreach(_CCOPT IN LISTS WPICKY_DETECT) # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new # test result in. string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index 5daec0e..ec09fd4 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -21,113 +21,167 @@ # SPDX-License-Identifier: curl # ########################################################################### -if(NOT UNIX) - if(WIN32) +if(NOT WIN32) + message(FATAL_ERROR "This file should be included on Windows platform only") +endif() - set(HAVE_WINDOWS_H 1) - set(HAVE_WS2TCPIP_H 1) - set(HAVE_WINSOCK2_H 1) +set(HAVE_LOCALE_H 1) - if(MINGW) - set(HAVE_SNPRINTF 1) - set(HAVE_UNISTD_H 1) - set(HAVE_INTTYPES_H 1) +if(MINGW) + set(HAVE_SNPRINTF 1) + set(HAVE_UNISTD_H 1) + set(HAVE_LIBGEN_H 1) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDBOOL_H 1) + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + set(HAVE_STRTOLL 1) + set(HAVE_BASENAME 1) + set(HAVE_STRCASECMP 1) + set(HAVE_FTRUNCATE 1) + set(HAVE_SYS_PARAM_H 1) + set(HAVE_SYS_TIME_H 1) + set(HAVE_GETTIMEOFDAY 1) +else() + set(HAVE_LIBGEN_H 0) + set(HAVE_STRCASECMP 0) + set(HAVE_FTRUNCATE 0) + set(HAVE_SYS_PARAM_H 0) + set(HAVE_SYS_TIME_H 0) + set(HAVE_GETTIMEOFDAY 0) + if(MSVC) + set(HAVE_UNISTD_H 0) + set(HAVE_LOCALE_H 1) + set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() + set(HAVE_STDATOMIC_H 0) + if(NOT MSVC_VERSION LESS 1800) + set(HAVE_STDBOOL_H 1) set(HAVE_STRTOLL 1) - set(HAVE_BASENAME 1) - elseif(MSVC) - if(NOT MSVC_VERSION LESS 1800) - set(HAVE_INTTYPES_H 1) - set(HAVE_STRTOLL 1) - else() - set(HAVE_INTTYPES_H 0) - set(HAVE_STRTOLL 0) - endif() - if(NOT MSVC_VERSION LESS 1900) - set(HAVE_SNPRINTF 1) - else() - set(HAVE_SNPRINTF 0) - endif() - set(HAVE_BASENAME 0) + else() + set(HAVE_STDBOOL_H 0) + set(HAVE_STRTOLL 0) endif() + set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") + if(NOT MSVC_VERSION LESS 1900) + set(HAVE_SNPRINTF 1) + else() + set(HAVE_SNPRINTF 0) + endif() + set(HAVE_BASENAME 0) + set(HAVE_STRTOK_R 0) + set(HAVE_FILE_OFFSET_BITS 0) + set(HAVE_ATOMIC 0) + endif() +endif() - set(HAVE_LIBSOCKET 0) - set(HAVE_GETHOSTNAME 1) - set(HAVE_LIBZ 0) +# Available in Windows XP and newer +set(HAVE_GETADDRINFO 1) +set(HAVE_FREEADDRINFO 1) - set(HAVE_ARC4RANDOM 0) - set(HAVE_FNMATCH 0) - set(HAVE_SCHED_YIELD 0) - set(HAVE_ARPA_INET_H 0) - set(HAVE_FCNTL_H 1) - set(HAVE_IFADDRS_H 0) - set(HAVE_IO_H 1) - set(HAVE_NETDB_H 0) - set(HAVE_NETINET_IN_H 0) - set(HAVE_NETINET_TCP_H 0) - set(HAVE_NETINET_UDP_H 0) - set(HAVE_NET_IF_H 0) - set(HAVE_IOCTL_SIOCGIFADDR 0) - set(HAVE_POLL_H 0) - set(HAVE_POLL_FINE 0) - set(HAVE_PWD_H 0) - set(HAVE_STRINGS_H 0) - set(HAVE_SYS_FILIO_H 0) - set(HAVE_SYS_WAIT_H 0) - set(HAVE_SYS_IOCTL_H 0) - set(HAVE_SYS_PARAM_H 0) - set(HAVE_SYS_POLL_H 0) - set(HAVE_SYS_RESOURCE_H 0) - set(HAVE_SYS_SELECT_H 0) - set(HAVE_SYS_SOCKET_H 0) - set(HAVE_SYS_SOCKIO_H 0) - set(HAVE_SYS_STAT_H 1) - set(HAVE_SYS_TIME_H 0) - set(HAVE_SYS_TYPES_H 1) - set(HAVE_SYS_UN_H 0) - set(HAVE_SYS_UTIME_H 1) - set(HAVE_TERMIOS_H 0) - set(HAVE_TERMIO_H 0) - set(HAVE_UTIME_H 0) +set(HAVE_FCHMOD 0) +set(HAVE_SOCKETPAIR 0) +set(HAVE_SENDMSG 0) +set(HAVE_ALARM 0) +set(HAVE_FCNTL 0) +set(HAVE_GETPPID 0) +set(HAVE_UTIMES 0) +set(HAVE_GETPWUID_R 0) +set(HAVE_STRERROR_R 0) +set(HAVE_SIGINTERRUPT 0) +set(HAVE_PIPE 0) +set(HAVE_IF_NAMETOINDEX 0) +set(HAVE_GETRLIMIT 0) +set(HAVE_SETRLIMIT 0) +set(HAVE_FSETXATTR 0) +set(HAVE_LIBSOCKET 0) +set(HAVE_SETLOCALE 1) +set(HAVE_SETMODE 1) +set(HAVE_GETPEERNAME 1) +set(HAVE_GETSOCKNAME 1) +set(HAVE_GETHOSTNAME 1) +set(HAVE_LIBZ 0) - set(HAVE_FSEEKO 0) - set(HAVE__FSEEKI64 1) - set(HAVE_SOCKET 1) - set(HAVE_SELECT 1) - set(HAVE_STRDUP 1) - set(HAVE_STRICMP 1) - set(HAVE_STRCMPI 1) - set(HAVE_MEMRCHR 0) - set(HAVE_GETTIMEOFDAY 0) - set(HAVE_CLOSESOCKET 1) - set(HAVE_SIGSETJMP 0) - set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) - set(HAVE_GETPASS_R 0) - set(HAVE_GETPWUID 0) - set(HAVE_GETEUID 0) - set(HAVE_UTIME 1) - set(HAVE_GMTIME_R 0) - set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 0) - set(HAVE_GETHOSTBYNAME_R 0) - set(HAVE_SIGNAL 1) - set(HAVE_LINUX_TCP_H 0) - set(HAVE_GLIBC_STRERROR_R 0) - set(HAVE_MACH_ABSOLUTE_TIME 0) - set(HAVE_GETIFADDRS 0) +set(HAVE_RECV 1) +set(HAVE_SEND 1) +set(HAVE_STROPTS_H 0) +set(HAVE_SYS_XATTR_H 0) +set(HAVE_ARC4RANDOM 0) +set(HAVE_FNMATCH 0) +set(HAVE_SCHED_YIELD 0) +set(HAVE_ARPA_INET_H 0) +set(HAVE_FCNTL_H 1) +set(HAVE_IFADDRS_H 0) +set(HAVE_IO_H 1) +set(HAVE_NETDB_H 0) +set(HAVE_NETINET_IN_H 0) +set(HAVE_NETINET_TCP_H 0) +set(HAVE_NETINET_UDP_H 0) +set(HAVE_NET_IF_H 0) +set(HAVE_IOCTL_SIOCGIFADDR 0) +set(HAVE_POLL_H 0) +set(HAVE_POLL_FINE 0) +set(HAVE_PWD_H 0) +set(HAVE_STRINGS_H 0) # mingw-w64 has it (wrapper to string.h) +set(HAVE_SYS_FILIO_H 0) +set(HAVE_SYS_WAIT_H 0) +set(HAVE_SYS_IOCTL_H 0) +set(HAVE_SYS_POLL_H 0) +set(HAVE_SYS_RESOURCE_H 0) +set(HAVE_SYS_SELECT_H 0) +set(HAVE_SYS_SOCKET_H 0) +set(HAVE_SYS_SOCKIO_H 0) +set(HAVE_SYS_STAT_H 1) +set(HAVE_SYS_TYPES_H 1) +set(HAVE_SYS_UN_H 0) +set(HAVE_SYS_UTIME_H 1) +set(HAVE_TERMIOS_H 0) +set(HAVE_TERMIO_H 0) +set(HAVE_UTIME_H 0) # mingw-w64 has it (wrapper to sys/utime.h) - set(HAVE_GETHOSTBYNAME_R_3 0) - set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) - set(HAVE_GETHOSTBYNAME_R_5 0) - set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) - set(HAVE_GETHOSTBYNAME_R_6 0) - set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0) +set(HAVE_FSEEKO 0) +set(HAVE__FSEEKI64 1) +set(HAVE_SOCKET 1) +set(HAVE_SELECT 1) +set(HAVE_STRDUP 1) +set(HAVE_STRICMP 1) +set(HAVE_STRCMPI 1) +set(HAVE_MEMRCHR 0) +set(HAVE_CLOSESOCKET 1) +set(HAVE_SIGSETJMP 0) +set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) +set(HAVE_GETPASS_R 0) +set(HAVE_GETPWUID 0) +set(HAVE_GETEUID 0) +set(HAVE_UTIME 1) +set(HAVE_GMTIME_R 0) +set(HAVE_GETHOSTBYNAME_R 0) +set(HAVE_SIGNAL 1) +set(HAVE_SIGACTION 0) +set(HAVE_LINUX_TCP_H 0) +set(HAVE_GLIBC_STRERROR_R 0) +set(HAVE_MACH_ABSOLUTE_TIME 0) +set(HAVE_GETIFADDRS 0) +set(HAVE_FCNTL_O_NONBLOCK 0) +set(HAVE_IOCTLSOCKET 1) +set(HAVE_IOCTLSOCKET_CAMEL 0) +set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0) +set(HAVE_IOCTLSOCKET_FIONBIO 1) +set(HAVE_IOCTL_FIONBIO 0) +set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) +set(HAVE_POSIX_STRERROR_R 0) +set(HAVE_BUILTIN_AVAILABLE 0) +set(HAVE_MSG_NOSIGNAL 0) +set(HAVE_STRUCT_TIMEVAL 1) - set(HAVE_O_NONBLOCK 0) - set(HAVE_IN_ADDR_T 0) - set(STDC_HEADERS 1) +set(HAVE_GETHOSTBYNAME_R_3 0) +set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_5 0) +set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) +set(HAVE_GETHOSTBYNAME_R_6 0) +set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0) - set(HAVE_SIGACTION 0) - set(HAVE_MACRO_SIGSETJMP 0) - else() - message("This file should be included on Windows platform only") - endif() -endif() +set(HAVE_O_NONBLOCK 0) +set(HAVE_IN_ADDR_T 0) +set(STDC_HEADERS 1) + +set(HAVE_SIZEOF_SUSECONDS_T 0) +set(HAVE_SIZEOF_SA_FAMILY_T 0) diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index 056907c..9adb96e 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -35,4 +35,6 @@ include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") check_required_components("@PROJECT_NAME@") # Alias for either shared or static library -add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@) +if(NOT TARGET @PROJECT_NAME@::libcurl) + add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@) +endif() diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 9387247..e6c4f1b 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -22,6 +22,7 @@ set(CURL_DISABLE_ALTSVC ON) set(CURL_DISABLE_AWS OFF) set(CURL_DISABLE_BASIC_AUTH OFF) set(CURL_DISABLE_BEARER_AUTH OFF) +set(CURL_DISABLE_BINDLOCAL OFF) set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support") set(CURL_DISABLE_DICT ON CACHE INTERNAL "Disable curl dict protocol?") set(CURL_DISABLE_DIGEST_AUTH OFF) @@ -31,10 +32,12 @@ set(CURL_DISABLE_FORM_API OFF) set(CURL_DISABLE_FTP OFF CACHE INTERNAL "Disable curl ftp protocol?") set(CURL_DISABLE_GETOPTIONS OFF) set(CURL_DISABLE_GOPHER ON CACHE INTERNAL "Disable curl gopher protocol?") +set(CURL_DISABLE_HEADERS_API OFF) set(CURL_DISABLE_HSTS OFF) set(CURL_DISABLE_HTTP_AUTH OFF) set(CURL_DISABLE_HTTP OFF CACHE INTERNAL "Disable curl http protocol?") set(CURL_DISABLE_IMAP ON CACHE INTERNAL "Disable curl imap protocol?") +set(CURL_DISABLE_INSTALL ON) set(CURL_DISABLE_KERBEROS_AUTH OFF) set(CURL_DISABLE_LDAP ON CACHE INTERNAL "Disable curl ldap protocol?") set(CURL_DISABLE_LDAPS ON CACHE INTERNAL "Disable curl ldaps protocol?") @@ -209,7 +212,6 @@ endif() # HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS # HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE -# HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd # # For each of the above variables, if the variable is DEFINED (either # to ON or OFF), the symbol detection will be skipped. If the @@ -260,6 +262,8 @@ option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(BUILD_STATIC_LIBS "Build static libraries" OFF) option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF) option(ENABLE_ARES "Set to ON to enable c-ares support" OFF) +option(CURL_DISABLE_INSTALL "Set to ON to disable instalation targets" OFF) + if(WIN32) option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF) option(ENABLE_UNICODE "Set to ON to use the Unicode version of the Windows API functions" OFF) @@ -385,6 +389,8 @@ option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing opti mark_as_advanced(CURL_DISABLE_GETOPTIONS) option(CURL_DISABLE_GOPHER "disables Gopher" OFF) mark_as_advanced(CURL_DISABLE_GOPHER) +option(CURL_DISABLE_HEADERS_API "disables headers-api support" OFF) +mark_as_advanced(CURL_DISABLE_HEADERS_API) option(CURL_DISABLE_HSTS "disables HSTS support" OFF) mark_as_advanced(CURL_DISABLE_HSTS) option(CURL_DISABLE_HTTP "disables HTTP" OFF) @@ -402,6 +408,8 @@ mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION) option(CURL_DISABLE_MIME "disables MIME support" OFF) mark_as_advanced(CURL_DISABLE_MIME) option(CURL_DISABLE_MQTT "disables MQTT" OFF) +mark_as_advanced(CURL_DISABLE_BINDLOCAL) +option(CURL_DISABLE_BINDLOCAL "disables local binding support" OFF) mark_as_advanced(CURL_DISABLE_MQTT) option(CURL_DISABLE_NETRC "disables netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) @@ -529,28 +537,33 @@ include(CheckCSourceCompiles) # On windows preload settings if(WIN32) - list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=) + set(HAVE_WINDOWS_H 1) + set(HAVE_WS2TCPIP_H 1) + set(HAVE_WINSOCK2_H 1) include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) endif() if(ENABLE_THREADED_RESOLVER) - find_package(Threads REQUIRED) if(WIN32) set(USE_THREADS_WIN32 ON) else() + find_package(Threads REQUIRED) set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) + set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) endif() - set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) endif() # Check for all needed libraries -check_library_exists_concat("socket" connect HAVE_LIBSOCKET) +check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) +if(HAVE_LIBSOCKET) + set(CURL_LIBS "socket;${CURL_LIBS}") +endif() check_function_exists(gethostname HAVE_GETHOSTNAME) if(WIN32) - list(APPEND CURL_LIBS "ws2_32") + list(APPEND CURL_LIBS "ws2_32" "bcrypt") if(USE_LIBRTMP) list(APPEND CURL_LIBS "winmm") endif() @@ -581,7 +594,7 @@ set(openssl_default ON) if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL) set(openssl_default OFF) endif() -cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${openssl_default} CURL_ENABLE_SSL OFF) option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) endif() @@ -653,11 +666,6 @@ if(CURL_USE_OPENSSL) list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) - if(WIN32) - list(APPEND CURL_LIBS "ws2_32") - list(APPEND CURL_LIBS "bcrypt") # for OpenSSL/LibreSSL - endif() - if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl") set(valid_default_ssl_backend TRUE) endif() @@ -783,17 +791,12 @@ option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF) set(HAVE_ZSTD OFF) if(CURL_ZSTD) find_package(Zstd REQUIRED) - if(NOT DEFINED HAVE_ZSTD_CREATEDSTREAM) - cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES}) - check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM) - cmake_pop_check_state() - endif() - if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM) + if(Zstd_FOUND AND NOT Zstd_VERSION VERSION_LESS "1.0.0") set(HAVE_ZSTD ON) list(APPEND CURL_LIBS ${Zstd_LIBRARIES}) include_directories(${Zstd_INCLUDE_DIRS}) + else() + message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.") endif() endif() @@ -826,6 +829,20 @@ macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE) cmake_pop_check_state() endmacro() +# Ensure that the OpenSSL fork actually supports QUIC. +macro(openssl_check_quic) + if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) + if(USE_OPENSSL) + openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + elseif(USE_WOLFSSL) + openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + endif() + endif() + if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) + message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR") + endif() +endmacro() + if(USE_OPENSSL OR USE_WOLFSSL) if(NOT DEFINED HAVE_SSL_SET0_WBIO) openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO) @@ -852,18 +869,7 @@ if(USE_NGTCP2) else() find_package(NGTCP2 REQUIRED quictls) endif() - - # Be sure that the OpenSSL/wolfSSL library actually supports QUIC. - if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) - if(USE_OPENSSL) - openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) - elseif(USE_WOLFSSL) - openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) - endif() - endif() - if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) - message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR") - endif() + openssl_check_quic() elseif(USE_GNUTLS) find_package(NGTCP2 REQUIRED GnuTLS) else() @@ -885,7 +891,10 @@ if(USE_QUICHE) message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") endif() find_package(QUICHE REQUIRED) - CheckQuicSupportInOpenSSL() + if(NOT HAVE_BORINGSSL) + message(FATAL_ERROR "quiche requires BoringSSL") + endif() + openssl_check_quic() set(USE_QUICHE ON) include_directories(${QUICHE_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) @@ -930,8 +939,12 @@ if(NOT CURL_DISABLE_LDAP) if(NOT USE_WIN32_LDAP) # Check for LDAP set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) - check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP) - check_library_exists_concat(${CMAKE_LBER_LIB} ber_init HAVE_LIBLBER) + check_library_exists("${CMAKE_LDAP_LIB}" "ldap_init" "" HAVE_LIBLDAP) + if(HAVE_LIBLDAP) + check_library_exists("${CMAKE_LDAP_LIB};${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + else() + check_library_exists("${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + endif() set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES}) set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory") @@ -970,8 +983,10 @@ if(NOT CURL_DISABLE_LDAP) list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) + set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}") if(HAVE_LIBLBER) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB}) + set(CURL_LIBS "${CMAKE_LBER_LIB};${CURL_LIBS}") endif() check_c_source_compiles(" @@ -1018,7 +1033,11 @@ endif() # Check for idn2 option(USE_LIBIDN2 "Use libidn2 for IDN support" ON) if(USE_LIBIDN2) - check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2) + check_library_exists("idn2" "idn2_lookup_ul" "" HAVE_LIBIDN2) + if(HAVE_LIBIDN2) + set(CURL_LIBS "idn2;${CURL_LIBS}") + check_include_file_concat("idn2.h" HAVE_IDN2_H) + endif() else() set(HAVE_LIBIDN2 OFF) endif() @@ -1233,17 +1252,50 @@ endif() endif() # Check for header files -if(NOT UNIX) - check_include_file_concat("windows.h" HAVE_WINDOWS_H) - check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) +if(WIN32) check_include_file_concat("winsock2.h" HAVE_WINSOCK2_H) + check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) + check_include_file_concat("windows.h" HAVE_WINDOWS_H) else() - set(HAVE_WINDOWS_H 0) - set(HAVE_WS2TCPIP_H 0) set(HAVE_WINSOCK2_H 0) + set(HAVE_WS2TCPIP_H 0) + set(HAVE_WINDOWS_H 0) +endif() + +if(WIN32) + # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT + curl_internal_test(HAVE_WIN32_WINNT) + if(HAVE_WIN32_WINNT) + string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}") + string(REGEX REPLACE ".*_WIN32_WINNT=" "" OUTPUT "${OUTPUT}") + string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" OUTPUT "${OUTPUT}") # pad to 4 digits + string(TOLOWER "${OUTPUT}" HAVE_WIN32_WINNT) + message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") + endif() + # avoid storing HAVE_WIN32_WINNT in CMake cache + unset(HAVE_WIN32_WINNT CACHE) + + if(HAVE_WIN32_WINNT) + if(HAVE_WIN32_WINNT STRLESS "0x0501") + # Windows XP is required for freeaddrinfo, getaddrinfo + message(FATAL_ERROR "Building for Windows XP or newer is required.") + endif() + + # pre-fill detection results based on target OS version + if(MINGW OR MSVC) + if(HAVE_WIN32_WINNT STRLESS "0x0600") + set(HAVE_INET_NTOP 0) + set(HAVE_INET_PTON 0) + else() # Windows Vista or newer + set(HAVE_INET_NTOP 1) + set(HAVE_INET_PTON 1) + endif() + unset(HAVE_INET_NTOP CACHE) + unset(HAVE_INET_PTON CACHE) + endif() + endif() endif() -check_include_file_concat("inttypes.h" HAVE_INTTYPES_H) check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H) check_include_file_concat("sys/wait.h" HAVE_SYS_WAIT_H) check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H) @@ -1261,7 +1313,6 @@ check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H) check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H) check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file_concat("fcntl.h" HAVE_FCNTL_H) -check_include_file_concat("idn2.h" HAVE_IDN2_H) check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) check_include_file_concat("io.h" HAVE_IO_H) check_include_file_concat("libgen.h" HAVE_LIBGEN_H) @@ -1277,7 +1328,6 @@ check_include_file_concat("poll.h" HAVE_POLL_H) check_include_file_concat("pwd.h" HAVE_PWD_H) check_include_file_concat("stdatomic.h" HAVE_STDATOMIC_H) check_include_file_concat("stdbool.h" HAVE_STDBOOL_H) -check_include_file_concat("stdint.h" HAVE_STDINT_H) check_include_file_concat("strings.h" HAVE_STRINGS_H) check_include_file_concat("stropts.h" HAVE_STROPTS_H) check_include_file_concat("termio.h" HAVE_TERMIO_H) @@ -1308,7 +1358,6 @@ elseif(HAVE_LIBNETWORK) set(CMAKE_REQUIRED_LIBRARIES network) endif() -check_symbol_exists(fchmod "${CURL_INCLUDES}" HAVE_FCHMOD) check_symbol_exists(fnmatch "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) check_symbol_exists(basename "${CURL_INCLUDES};string.h" HAVE_BASENAME) check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) @@ -1345,6 +1394,7 @@ check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) check_symbol_exists(signal "${CURL_INCLUDES};signal.h" HAVE_SIGNAL) check_symbol_exists(strtoll "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL) check_symbol_exists(strerror_r "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R) +check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION) check_symbol_exists(siginterrupt "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT) check_symbol_exists(getaddrinfo "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) check_symbol_exists(getifaddrs "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) @@ -1361,6 +1411,10 @@ check_symbol_exists(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE) check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE) check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT) +if(HAVE_FSEEKO) + set(HAVE_DECL_FSEEKO 1) +endif() + if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) # earlier MSVC compilers had faulty snprintf implementations check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) @@ -1384,20 +1438,11 @@ check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) set(CMAKE_EXTRA_INCLUDE_FILES "") -set(CMAKE_EXTRA_INCLUDE_FILES "ws2def.h") -check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) -set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) -set(CMAKE_EXTRA_INCLUDE_FILES "") - -# sigaction and sigsetjmp are special. Use special mechanism for -# detecting those, but only if previous attempt failed. -check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION) - -if(NOT HAVE_SIGSETJMP) - check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP) - if(HAVE_MACRO_SIGSETJMP) - set(HAVE_SIGSETJMP 1) - endif() +if(WIN32) + set(CMAKE_EXTRA_INCLUDE_FILES "ws2def.h") + check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) + set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) + set(CMAKE_EXTRA_INCLUDE_FILES "") endif() # Do curl specific tests @@ -1421,8 +1466,6 @@ foreach(CURL_TEST HAVE_BOOL_T STDC_HEADERS HAVE_FILE_OFFSET_BITS - HAVE_VARIADIC_MACROS_C99 - HAVE_VARIADIC_MACROS_GCC HAVE_ATOMIC ) curl_internal_test(${CURL_TEST}) @@ -1442,18 +1485,6 @@ set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) set(CMAKE_EXTRA_INCLUDE_FILES "") -if(WIN32) - # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT - curl_internal_test(HAVE_WIN32_WINNT) - if(HAVE_WIN32_WINNT) - string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}") - string(REGEX REPLACE ".*_WIN32_WINNT=" "" HAVE_WIN32_WINNT "${OUTPUT}") - message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") - endif() - # avoid storing HAVE_WIN32_WINNT in CMake cache - unset(HAVE_WIN32_WINNT CACHE) -endif() - if(0) # This code not needed for building within CMake. if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) # on not-Windows and not-crosscompiling, check for writable argv[] @@ -1511,8 +1542,10 @@ if(NEED_REENTRANT) endforeach() endif() -# Check clock_gettime(CLOCK_MONOTONIC, x) support -curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC) +if(NOT WIN32) + # Check clock_gettime(CLOCK_MONOTONIC, x) support + curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC) +endif() # Check compiler support of __builtin_available() curl_internal_test(HAVE_BUILTIN_AVAILABLE) @@ -1577,8 +1610,6 @@ if(WIN32) if(USE_WIN32_CRYPTO OR USE_SCHANNEL) list(APPEND CURL_LIBS "advapi32" "crypt32") endif() - - list(APPEND CURL_LIBS "bcrypt") endif() if(MSVC) @@ -1683,258 +1714,262 @@ if(BUILD_TESTING) add_subdirectory(tests) endif() -# Helper to populate a list (_items) with a label when conditions (the remaining -# args) are satisfied -macro(_add_if label) - # needs to be a macro to allow this indirection - if(${ARGN}) - set(_items ${_items} "${label}") - endif() -endmacro() +if(NOT CURL_DISABLE_INSTALL) -# NTLM support requires crypto function adaptions from various SSL libs -# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS -if(NOT (CURL_DISABLE_NTLM) AND - (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS)) - set(use_curl_ntlm_core ON) -endif() - -# Clear list and try to detect available features -set(_items) -_add_if("SSL" SSL_ENABLED) -_add_if("IPv6" ENABLE_IPV6) -_add_if("unixsockets" USE_UNIX_SOCKETS) -_add_if("libz" HAVE_LIBZ) -_add_if("brotli" HAVE_BROTLI) -_add_if("zstd" HAVE_ZSTD) -_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) -_add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN) -_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND - ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) -# TODO SSP1 (Schannel) check is missing -_add_if("SSPI" USE_WINDOWS_SSPI) -_add_if("GSS-API" HAVE_GSSAPI) -_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) -_add_if("HSTS" NOT CURL_DISABLE_HSTS) -# TODO SSP1 missing for SPNEGO -_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -# NTLM support requires crypto function adaptions from various SSL libs -# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS -_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND - (use_curl_ntlm_core OR USE_WINDOWS_SSPI)) -# TODO missing option (autoconf: --enable-ntlm-wb) -_add_if("NTLM_WB" NOT (CURL_DISABLE_NTLM) AND - (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND - NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) -_add_if("TLS-SRP" USE_TLS_SRP) -# TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header -_add_if("HTTP2" USE_NGHTTP2) -_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) -_add_if("MultiSSL" CURL_WITH_MULTI_SSL) -# TODO wolfSSL only support this from v5.0.0 onwards -_add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS - OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR - USE_MBEDTLS OR USE_SECTRANSP)) -_add_if("unicode" ENABLE_UNICODE) -_add_if("threadsafe" HAVE_ATOMIC OR (WIN32 AND - HAVE_WIN32_WINNT GREATER_EQUAL 0x600)) -_add_if("PSL" USE_LIBPSL) -string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") -message(STATUS "Enabled features: ${SUPPORT_FEATURES}") - -# Clear list and try to detect available protocols -set(_items) -_add_if("HTTP" NOT CURL_DISABLE_HTTP) -_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED) -_add_if("FTP" NOT CURL_DISABLE_FTP) -_add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED) -_add_if("FILE" NOT CURL_DISABLE_FILE) -_add_if("TELNET" NOT CURL_DISABLE_TELNET) -_add_if("LDAP" NOT CURL_DISABLE_LDAP) -# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS -_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND - ((USE_OPENLDAP AND SSL_ENABLED) OR - (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) -_add_if("DICT" NOT CURL_DISABLE_DICT) -_add_if("TFTP" NOT CURL_DISABLE_TFTP) -_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) -_add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND SSL_ENABLED) -_add_if("POP3" NOT CURL_DISABLE_POP3) -_add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED) -_add_if("IMAP" NOT CURL_DISABLE_IMAP) -_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND SSL_ENABLED) -_add_if("SMB" NOT CURL_DISABLE_SMB AND - use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -_add_if("SMBS" NOT CURL_DISABLE_SMB AND SSL_ENABLED AND - use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -_add_if("SMTP" NOT CURL_DISABLE_SMTP) -_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND SSL_ENABLED) -_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH) -_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH) -_add_if("RTSP" NOT CURL_DISABLE_RTSP) -_add_if("RTMP" USE_LIBRTMP) -_add_if("MQTT" NOT CURL_DISABLE_MQTT) -_add_if("WS" USE_WEBSOCKETS) -_add_if("WSS" USE_WEBSOCKETS) -if(_items) - list(SORT _items) -endif() -string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") -message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}") - -# Clear list and collect SSL backends -set(_items) -_add_if("Schannel" SSL_ENABLED AND USE_SCHANNEL) -_add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL) -_add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP) -_add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS) -_add_if("BearSSL" SSL_ENABLED AND USE_BEARSSL) -_add_if("wolfSSL" SSL_ENABLED AND USE_WOLFSSL) -_add_if("GnuTLS" SSL_ENABLED AND USE_GNUTLS) - -if(_items) - list(SORT _items) -endif() -string(REPLACE ";" " " SSL_BACKENDS "${_items}") -message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") -if(CURL_DEFAULT_SSL_BACKEND) - message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}") -endif() - -# curl-config needs the following options to be set. -set(CC "${CMAKE_C_COMPILER}") -# TODO probably put a -D... options here? -set(CONFIGURE_OPTIONS "") -set(CURLVERSION "${CURL_VERSION}") -set(exec_prefix "\${prefix}") -set(includedir "\${prefix}/include") -set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") -set(LIBCURL_LIBS "") -set(libdir "${CMAKE_INSTALL_PREFIX}/lib") -foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) - if(TARGET "${_lib}") - set(_libname "${_lib}") - get_target_property(_imported "${_libname}" IMPORTED) - if(NOT _imported) - # Reading the LOCATION property on non-imported target will error out. - # Assume the user won't need this information in the .pc file. - continue() + # Helper to populate a list (_items) with a label when conditions (the remaining + # args) are satisfied + macro(_add_if label) + # needs to be a macro to allow this indirection + if(${ARGN}) + set(_items ${_items} "${label}") endif() - get_target_property(_lib "${_libname}" LOCATION) - if(NOT _lib) - message(WARNING "Bad lib in library list: ${_libname}") - continue() + endmacro() + + # NTLM support requires crypto function adaptions from various SSL libs + # TODO alternative SSL libs tests for SSP1, GnuTLS, NSS + if(NOT (CURL_DISABLE_NTLM) AND + (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS)) + set(use_curl_ntlm_core ON) + endif() + + # Clear list and try to detect available features + set(_items) + _add_if("SSL" SSL_ENABLED) + _add_if("IPv6" ENABLE_IPV6) + _add_if("UnixSockets" USE_UNIX_SOCKETS) + _add_if("libz" HAVE_LIBZ) + _add_if("brotli" HAVE_BROTLI) + _add_if("zstd" HAVE_ZSTD) + _add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) + _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN) + _add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND + ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) + # TODO SSP1 (Schannel) check is missing + _add_if("SSPI" USE_WINDOWS_SSPI) + _add_if("GSS-API" HAVE_GSSAPI) + _add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) + _add_if("HSTS" NOT CURL_DISABLE_HSTS) + # TODO SSP1 missing for SPNEGO + _add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) + _add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) + # NTLM support requires crypto function adaptions from various SSL libs + # TODO alternative SSL libs tests for SSP1, GnuTLS, NSS + _add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND + (use_curl_ntlm_core OR USE_WINDOWS_SSPI)) + # TODO missing option (autoconf: --enable-ntlm-wb) + _add_if("NTLM_WB" NOT (CURL_DISABLE_NTLM) AND + (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND + NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) + _add_if("TLS-SRP" USE_TLS_SRP) + # TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header + _add_if("HTTP2" USE_NGHTTP2) + _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) + _add_if("MultiSSL" CURL_WITH_MULTI_SSL) + # TODO wolfSSL only support this from v5.0.0 onwards + _add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS + OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR + USE_MBEDTLS OR USE_SECTRANSP)) + _add_if("unicode" ENABLE_UNICODE) + _add_if("threadsafe" HAVE_ATOMIC OR + (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR + (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x600)) + _add_if("PSL" USE_LIBPSL) + string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") + message(STATUS "Enabled features: ${SUPPORT_FEATURES}") + + # Clear list and try to detect available protocols + set(_items) + _add_if("HTTP" NOT CURL_DISABLE_HTTP) + _add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED) + _add_if("FTP" NOT CURL_DISABLE_FTP) + _add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED) + _add_if("FILE" NOT CURL_DISABLE_FILE) + _add_if("TELNET" NOT CURL_DISABLE_TELNET) + _add_if("LDAP" NOT CURL_DISABLE_LDAP) + # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS + _add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND + ((USE_OPENLDAP AND SSL_ENABLED) OR + (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) + _add_if("DICT" NOT CURL_DISABLE_DICT) + _add_if("TFTP" NOT CURL_DISABLE_TFTP) + _add_if("GOPHER" NOT CURL_DISABLE_GOPHER) + _add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND SSL_ENABLED) + _add_if("POP3" NOT CURL_DISABLE_POP3) + _add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED) + _add_if("IMAP" NOT CURL_DISABLE_IMAP) + _add_if("IMAPS" NOT CURL_DISABLE_IMAP AND SSL_ENABLED) + _add_if("SMB" NOT CURL_DISABLE_SMB AND + use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) + _add_if("SMBS" NOT CURL_DISABLE_SMB AND SSL_ENABLED AND + use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) + _add_if("SMTP" NOT CURL_DISABLE_SMTP) + _add_if("SMTPS" NOT CURL_DISABLE_SMTP AND SSL_ENABLED) + _add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH) + _add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH) + _add_if("RTSP" NOT CURL_DISABLE_RTSP) + _add_if("RTMP" USE_LIBRTMP) + _add_if("MQTT" NOT CURL_DISABLE_MQTT) + _add_if("WS" USE_WEBSOCKETS) + _add_if("WSS" USE_WEBSOCKETS) + if(_items) + list(SORT _items) + endif() + string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") + message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}") + + # Clear list and collect SSL backends + set(_items) + _add_if("Schannel" SSL_ENABLED AND USE_SCHANNEL) + _add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL) + _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP) + _add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS) + _add_if("BearSSL" SSL_ENABLED AND USE_BEARSSL) + _add_if("wolfSSL" SSL_ENABLED AND USE_WOLFSSL) + _add_if("GnuTLS" SSL_ENABLED AND USE_GNUTLS) + + if(_items) + list(SORT _items) + endif() + string(REPLACE ";" " " SSL_BACKENDS "${_items}") + message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") + if(CURL_DEFAULT_SSL_BACKEND) + message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}") + endif() + + # curl-config needs the following options to be set. + set(CC "${CMAKE_C_COMPILER}") + # TODO probably put a -D... options here? + set(CONFIGURE_OPTIONS "") + set(CURLVERSION "${CURL_VERSION}") + set(exec_prefix "\${prefix}") + set(includedir "\${prefix}/include") + set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + set(LIBCURL_LIBS "") + set(libdir "${CMAKE_INSTALL_PREFIX}/lib") + foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) + if(TARGET "${_lib}") + set(_libname "${_lib}") + get_target_property(_imported "${_libname}" IMPORTED) + if(NOT _imported) + # Reading the LOCATION property on non-imported target will error out. + # Assume the user won't need this information in the .pc file. + continue() + endif() + get_target_property(_lib "${_libname}" LOCATION) + if(NOT _lib) + message(WARNING "Bad lib in library list: ${_libname}") + continue() + endif() endif() - endif() - if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-") - set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-") + set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + else() + set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") + endif() + endforeach() + if(BUILD_SHARED_LIBS) + set(ENABLE_SHARED "yes") + set(LIBCURL_NO_SHARED "") + set(CPPFLAG_CURL_STATICLIB "") else() - set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") + set(ENABLE_SHARED "no") + set(LIBCURL_NO_SHARED "${LIBCURL_LIBS}") + set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB") endif() -endforeach() -if(BUILD_SHARED_LIBS) - set(ENABLE_SHARED "yes") - set(LIBCURL_NO_SHARED "") - set(CPPFLAG_CURL_STATICLIB "") -else() - set(ENABLE_SHARED "no") - set(LIBCURL_NO_SHARED "${LIBCURL_LIBS}") - set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB") -endif() -if(BUILD_STATIC_LIBS) - set(ENABLE_STATIC "yes") -else() - set(ENABLE_STATIC "no") -endif() -# "a" (Linux) or "lib" (Windows) -string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") -set(prefix "${CMAKE_INSTALL_PREFIX}") -# Set this to "yes" to append all libraries on which -lcurl is dependent -set(REQUIRE_LIB_DEPS "no") -# SUPPORT_FEATURES -# SUPPORT_PROTOCOLS -set(VERSIONNUM "${CURL_VERSION_NUM}") - -# Finally generate a "curl-config" matching this config -# Use: -# * ENABLE_SHARED -# * ENABLE_STATIC -configure_file("${CURL_SOURCE_DIR}/curl-config.in" - "${CURL_BINARY_DIR}/curl-config" @ONLY) -install(FILES "${CURL_BINARY_DIR}/curl-config" - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) - -# Finally generate a pkg-config file matching this config -configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in" - "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) -install(FILES "${CURL_BINARY_DIR}/libcurl.pc" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - -# install headers -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.h") - -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - "${version_config}" - VERSION ${CURL_VERSION} - COMPATIBILITY SameMajorVersion -) -file(READ "${version_config}" generated_version_config) -file(WRITE "${version_config}" -"if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\") - # Version 8 satisfies version 7... requirements - set(PACKAGE_FIND_VERSION_MAJOR 8) - set(PACKAGE_FIND_VERSION_COUNT 1) -endif() -${generated_version_config}" -) + if(BUILD_STATIC_LIBS) + set(ENABLE_STATIC "yes") + else() + set(ENABLE_STATIC "no") + endif() + # "a" (Linux) or "lib" (Windows) + string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(prefix "${CMAKE_INSTALL_PREFIX}") + # Set this to "yes" to append all libraries on which -lcurl is dependent + set(REQUIRE_LIB_DEPS "no") + # SUPPORT_FEATURES + # SUPPORT_PROTOCOLS + set(VERSIONNUM "${CURL_VERSION_NUM}") + + # Finally generate a "curl-config" matching this config + # Use: + # * ENABLE_SHARED + # * ENABLE_STATIC + configure_file("${CURL_SOURCE_DIR}/curl-config.in" + "${CURL_BINARY_DIR}/curl-config" @ONLY) + install(FILES "${CURL_BINARY_DIR}/curl-config" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + + # Finally generate a pkg-config file matching this config + configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in" + "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) + install(FILES "${CURL_BINARY_DIR}/libcurl.pc" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + + # install headers + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h") + + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${version_config}" + VERSION ${CURL_VERSION} + COMPATIBILITY SameMajorVersion + ) + file(READ "${version_config}" generated_version_config) + file(WRITE "${version_config}" + "if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\") + # Version 8 satisfies version 7... requirements + set(PACKAGE_FIND_VERSION_MAJOR 8) + set(PACKAGE_FIND_VERSION_COUNT 1) + endif() + ${generated_version_config}" + ) -# Use: -# * TARGETS_EXPORT_NAME -# * PROJECT_NAME -configure_package_config_file(CMake/curl-config.cmake.in - "${project_config}" - INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} -) + # Use: + # * TARGETS_EXPORT_NAME + # * PROJECT_NAME + configure_package_config_file(CMake/curl-config.cmake.in + "${project_config}" + INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} + ) + + if(CURL_ENABLE_EXPORT_TARGET) + install( + EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${CURL_INSTALL_CMAKE_DIR} + ) + endif() -if(CURL_ENABLE_EXPORT_TARGET) install( - EXPORT "${TARGETS_EXPORT_NAME}" - NAMESPACE "${PROJECT_NAME}::" + FILES ${version_config} ${project_config} DESTINATION ${CURL_INSTALL_CMAKE_DIR} ) -endif() - -install( - FILES ${version_config} ${project_config} - DESTINATION ${CURL_INSTALL_CMAKE_DIR} -) -# Workaround for MSVS10 to avoid the Dialog Hell -# FIXME: This could be removed with future version of CMake. -if(MSVC_VERSION EQUAL 1600) - set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") - if(EXISTS "${CURL_SLN_FILENAME}") - file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n") + # Workaround for MSVS10 to avoid the Dialog Hell + # FIXME: This could be removed with future version of CMake. + if(MSVC_VERSION EQUAL 1600) + set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") + if(EXISTS "${CURL_SLN_FILENAME}") + file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n") + endif() endif() -endif() -if(NOT TARGET curl_uninstall) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake - IMMEDIATE @ONLY) + if(NOT TARGET curl_uninstall) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake + IMMEDIATE @ONLY) - add_custom_target(curl_uninstall - COMMAND ${CMAKE_COMMAND} -P - ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake) + add_custom_target(curl_uninstall + COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake) + endif() endif() diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 0496570..58de4b5 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -53,28 +53,19 @@ #include "curlver.h" /* libcurl version defines */ #include "system.h" /* determine things run-time */ -/* - * Define CURL_WIN32 when build target is Win32 API - */ - -#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && \ - !defined(__SYMBIAN32__) -#define CURL_WIN32 -#endif - #include <stdio.h> #include <limits.h> -#if (defined(__FreeBSD__) && (__FreeBSD__ >= 2)) || defined(__MidnightBSD__) +#if defined(__FreeBSD__) || defined(__MidnightBSD__) /* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */ -#include <osreldate.h> +#include <sys/param.h> #endif /* The include stuff here below is mainly for time_t! */ #include <sys/types.h> #include <time.h> -#if defined(CURL_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) #if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) /* The check above prevents the winsock2 inclusion if winsock.h already was @@ -88,7 +79,7 @@ libc5-based Linux systems. Only include it on systems that are known to require it! */ #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ - defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(__minix) || defined(__INTEGRITY) || \ defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ @@ -97,11 +88,11 @@ #include <sys/select.h> #endif -#if !defined(CURL_WIN32) && !defined(_WIN32_WCE) +#if !defined(_WIN32) && !defined(_WIN32_WCE) #include <sys/socket.h> #endif -#if !defined(CURL_WIN32) +#if !defined(_WIN32) #include <sys/time.h> #endif @@ -128,7 +119,7 @@ typedef void CURLSH; #ifdef CURL_STATICLIB # define CURL_EXTERN -#elif defined(CURL_WIN32) || defined(__SYMBIAN32__) || \ +#elif defined(_WIN32) || \ (__has_declspec_attribute(dllexport) && \ __has_declspec_attribute(dllimport)) # if defined(BUILDING_LIBCURL) @@ -144,7 +135,7 @@ typedef void CURLSH; #ifndef curl_socket_typedef /* socket typedef */ -#if defined(CURL_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) +#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else @@ -3220,6 +3211,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #include "options.h" #include "header.h" #include "websockets.h" +#include "mprintf.h" /* the typechecker doesn't work in C++ (yet) */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 3c3f992..097317a 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -32,12 +32,12 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.4.0" +#define LIBCURL_VERSION "8.5.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 4 +#define LIBCURL_VERSION_MINOR 5 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080400 +#define LIBCURL_VERSION_NUM 0x080500 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 97e0d03..f2554b4 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -141,29 +141,6 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int # endif -#elif defined(__SYMBIAN32__) -# if defined(__EABI__) /* Treat all ARM compilers equally */ -# 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 -# elif defined(__CW32__) -# pragma longlong on -# 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 -# elif defined(__VC32__) -# define CURL_TYPEOF_CURL_OFF_T __int64 -# 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 -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int - #elif defined(macintosh) # include <ConditionalMacros.h> # if TYPE_LONGLONG @@ -201,9 +178,10 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__MINGW32__) +# include <inttypes.h> # define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "I64d" -# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_FORMAT_CURL_OFF_T PRId64 +# define CURL_FORMAT_CURL_OFF_TU PRIu64 # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL # define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t @@ -370,7 +348,14 @@ /* ===================================== */ #elif defined(_MSC_VER) -# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# if (_MSC_VER >= 1800) +# include <inttypes.h> +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T PRId64 +# define CURL_FORMAT_CURL_OFF_TU PRIu64 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) # define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 9899b9d..bf25d89 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -85,20 +85,25 @@ endif() return() # The rest of this file is not needed for building within CMake. #----------------------------------------------------------------------------- -add_library( - curlu # special libcurlu library just for unittests - STATIC - EXCLUDE_FROM_ALL - ${HHEADERS} ${CSOURCES} -) -target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) +if(BUILD_TESTING) + add_library( + curlu # special libcurlu library just for unittests + STATIC + EXCLUDE_FROM_ALL + ${HHEADERS} ${CSOURCES} + ) + target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) +endif() if(ENABLE_CURLDEBUG) # We must compile these sources separately to avoid memdebug.h redefinitions # applying to them. set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() -target_link_libraries(curlu PRIVATE ${CURL_LIBS}) + +if(BUILD_TESTING) + target_link_libraries(curlu PRIVATE ${CURL_LIBS}) +endif() transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 22b0b69..35450d6 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -97,7 +97,7 @@ static struct altsvc *altsvc_createid(const char *srchost, unsigned int srcport, unsigned int dstport) { - struct altsvc *as = calloc(sizeof(struct altsvc), 1); + struct altsvc *as = calloc(1, sizeof(struct altsvc)); size_t hlen; size_t dlen; if(!as) @@ -123,15 +123,13 @@ static struct altsvc *altsvc_createid(const char *srchost, dlen -= 2; } - as->src.host = Curl_memdup(srchost, hlen + 1); + as->src.host = Curl_strndup(srchost, hlen); if(!as->src.host) goto error; - as->src.host[hlen] = 0; - as->dst.host = Curl_memdup(dsthost, dlen + 1); + as->dst.host = Curl_strndup(dsthost, dlen); if(!as->dst.host) goto error; - as->dst.host[dlen] = 0; as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -301,7 +299,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) */ struct altsvcinfo *Curl_altsvc_init(void) { - struct altsvcinfo *asi = calloc(sizeof(struct altsvcinfo), 1); + struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo)); if(!asi) return NULL; Curl_llist_init(&asi->list, NULL); diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index de13738..228b446 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h @@ -56,12 +56,14 @@ static const char * const telnetoptions[]= "TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; +#define CURL_TELOPT(x) telnetoptions[x] +#else +#define CURL_TELOPT(x) "" #endif #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON #define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM) -#define CURL_TELOPT(x) telnetoptions[x] #define CURL_NTELOPTS 40 @@ -103,7 +105,12 @@ static const char * const telnetcmds[]= #define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \ ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) ) + +#ifndef CURL_DISABLE_VERBOSE_STRINGS #define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM] +#else +#define CURL_TELCMD(x) "" +#endif #endif /* CURL_DISABLE_TELNET */ diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index e73e41d..437c933 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -60,13 +60,13 @@ #include "progress.h" #include "timediff.h" -# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) -# define CARES_STATICLIB -# endif -# include <ares.h> -# include <ares_version.h> /* really old c-ares didn't include this by - itself */ +#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ + defined(_WIN32) +# define CARES_STATICLIB +#endif +#include <ares.h> +#include <ares_version.h> /* really old c-ares didn't include this by + itself */ #if ARES_VERSION >= 0x010500 /* c-ares 1.5.0 or later, the callback proto is modified */ @@ -228,9 +228,9 @@ static void destroy_async_data(struct Curl_async *async); void Curl_resolver_cancel(struct Curl_easy *data) { DEBUGASSERT(data); - if(data->state.async.resolver) - ares_cancel((ares_channel)data->state.async.resolver); - destroy_async_data(&data->state.async); + if(data->conn->resolve_async.resolver) + ares_cancel((ares_channel)data->conn->resolve_async.resolver); + destroy_async_data(&data->conn->resolve_async); } /* @@ -278,14 +278,14 @@ int Curl_resolver_getsock(struct Curl_easy *data, struct timeval timebuf; struct timeval *timeout; long milli; - int max = ares_getsock((ares_channel)data->state.async.resolver, + int max = ares_getsock((ares_channel)data->conn->resolve_async.resolver, (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; maxtime.tv_usec = 0; - timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, - &timebuf); + timeout = ares_timeout((ares_channel)data->conn->resolve_async.resolver, + &maxtime, &timebuf); milli = (long)curlx_tvtoms(timeout); if(milli == 0) milli += 10; @@ -313,8 +313,8 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) int i; int num = 0; - bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks, - ARES_GETSOCK_MAXNUM); + bitmask = ares_getsock((ares_channel)data->conn->resolve_async.resolver, + socks, ARES_GETSOCK_MAXNUM); for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { pfd[i].events = 0; @@ -344,12 +344,12 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) if(!nfds) /* Call ares_process() unconditionally here, even if we simply timed out above, as otherwise the ares name resolve won't timeout! */ - ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, - ARES_SOCKET_BAD); + ares_process_fd((ares_channel)data->conn->resolve_async.resolver, + ARES_SOCKET_BAD, ARES_SOCKET_BAD); else { /* move through the descriptors and ask for processing on them */ for(i = 0; i < num; i++) - ares_process_fd((ares_channel)data->state.async.resolver, + ares_process_fd((ares_channel)data->conn->resolve_async.resolver, (pfd[i].revents & (POLLRDNORM|POLLIN))? pfd[i].fd:ARES_SOCKET_BAD, (pfd[i].revents & (POLLWRNORM|POLLOUT))? @@ -368,7 +368,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dns) { - struct thread_data *res = data->state.async.tdata; + struct thread_data *res = data->conn->resolve_async.tdata; CURLcode result = CURLE_OK; DEBUGASSERT(dns); @@ -397,7 +397,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, ARES_ECANCELLED synchronously for all pending responses. This will leave us with res->num_pending == 0, which is perfect for the next block. */ - ares_cancel((ares_channel)data->state.async.resolver); + ares_cancel((ares_channel)data->conn->resolve_async.resolver); DEBUGASSERT(res->num_pending == 0); } #endif @@ -408,12 +408,12 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, them */ res->temp_ai = NULL; - if(!data->state.async.dns) + if(!data->conn->resolve_async.dns) result = Curl_resolver_error(data); else - *dns = data->state.async.dns; + *dns = data->conn->resolve_async.dns; - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); } return result; @@ -464,7 +464,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, store.tv_sec = itimeout/1000; store.tv_usec = (itimeout%1000)*1000; - tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv); + tvp = ares_timeout((ares_channel)data->conn->resolve_async.resolver, + &store, &tv); /* use the timeout period ares returned to us above if less than one second is left, otherwise just use 1000ms to make sure the progress @@ -478,7 +479,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, return CURLE_UNRECOVERABLE_POLL; result = Curl_resolver_is_resolved(data, entry); - if(result || data->state.async.done) + if(result || data->conn->resolve_async.done) break; if(Curl_pgrsUpdate(data)) @@ -499,12 +500,12 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, } if(result) /* failure, so we cancel the ares operation */ - ares_cancel((ares_channel)data->state.async.resolver); + ares_cancel((ares_channel)data->conn->resolve_async.resolver); /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) - *entry = data->state.async.dns; + *entry = data->conn->resolve_async.dns; if(result) /* close the connection, since we can't return failure here without @@ -571,12 +572,13 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ be valid so only defer it when we know the 'status' says its fine! */ return; - res = data->state.async.tdata; + res = data->conn->resolve_async.tdata; if(res) { res->num_pending--; if(CURL_ASYNC_SUCCESS == status) { - struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port); + struct Curl_addrinfo *ai = Curl_he2ai(hostent, + data->conn->resolve_async.port); if(ai) { compound_results(res, ai); } @@ -727,14 +729,16 @@ static void addrinfo_cb(void *arg, int status, int timeouts, struct ares_addrinfo *result) { struct Curl_easy *data = (struct Curl_easy *)arg; - struct thread_data *res = data->state.async.tdata; - (void)timeouts; - if(ARES_SUCCESS == status) { - res->temp_ai = ares2addr(result->nodes); - res->last_status = CURL_ASYNC_SUCCESS; - ares_freeaddrinfo(result); + if(data->conn) { + struct thread_data *res = data->conn->resolve_async.tdata; + (void)timeouts; + if(ARES_SUCCESS == status) { + res->temp_ai = ares2addr(result->nodes); + res->last_status = CURL_ASYNC_SUCCESS; + ares_freeaddrinfo(result); + } + res->num_pending--; } - res->num_pending--; } #endif @@ -755,15 +759,15 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, size_t namelen = strlen(hostname); *waitp = 0; /* default to synchronous response */ - res = calloc(sizeof(struct thread_data) + namelen, 1); + res = calloc(1, sizeof(struct thread_data) + namelen); if(res) { strcpy(res->hostname, hostname); - data->state.async.hostname = res->hostname; - data->state.async.port = port; - data->state.async.done = FALSE; /* not done */ - data->state.async.status = 0; /* clear */ - data->state.async.dns = NULL; /* clear */ - data->state.async.tdata = res; + data->conn->resolve_async.hostname = res->hostname; + data->conn->resolve_async.port = port; + data->conn->resolve_async.done = FALSE; /* not done */ + data->conn->resolve_async.status = 0; /* clear */ + data->conn->resolve_async.dns = NULL; /* clear */ + data->conn->resolve_async.tdata = res; /* initial status - failed */ res->last_status = ARES_ENOTFOUND; @@ -793,8 +797,8 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, hints.ai_flags = ARES_AI_NUMERICSERV; msnprintf(service, sizeof(service), "%d", port); res->num_pending = 1; - ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname, - service, &hints, addrinfo_cb, data); + ares_getaddrinfo((ares_channel)data->conn->resolve_async.resolver, + hostname, service, &hints, addrinfo_cb, data); } #else @@ -804,10 +808,10 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, res->num_pending = 2; /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET, query_completed_cb, data); - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET6, query_completed_cb, data); + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, + hostname, PF_INET, query_completed_cb, data); + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, + hostname, PF_INET6, query_completed_cb, data); } else #endif @@ -815,7 +819,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, res->num_pending = 1; /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, + ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver, hostname, PF_INET, query_completed_cb, data); } @@ -829,6 +833,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers) { CURLcode result = CURLE_NOT_BUILT_IN; + ares_channel channel, lchannel = NULL; int ares_result; /* If server is NULL or empty, this would purge all DNS servers @@ -841,11 +846,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, return CURLE_OK; #ifdef HAVE_CARES_SERVERS_CSV + if(data->conn) + channel = data->conn->resolve_async.resolver; + else { + /* we are called by setopt on a data without a connection (yet). In that + * case we set the value on a local instance for checking. + * The configured data options are set when the connection for this + * transfer is created. */ + result = Curl_resolver_init(data, (void **)&lchannel); + if(result) + goto out; + channel = lchannel; + } + #ifdef HAVE_CARES_PORTS_CSV - ares_result = ares_set_servers_ports_csv(data->state.async.resolver, - servers); + ares_result = ares_set_servers_ports_csv(channel, servers); #else - ares_result = ares_set_servers_csv(data->state.async.resolver, servers); + ares_result = ares_set_servers_csv(channel, servers); #endif switch(ares_result) { case ARES_SUCCESS: @@ -861,6 +878,9 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, result = CURLE_BAD_FUNCTION_ARGUMENT; break; } +out: + if(lchannel) + Curl_resolver_cleanup(lchannel); #else /* too old c-ares version! */ (void)data; (void)(ares_result); @@ -872,11 +892,14 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data, const char *interf) { #ifdef HAVE_CARES_LOCAL_DEV - if(!interf) - interf = ""; - - ares_set_local_dev((ares_channel)data->state.async.resolver, interf); + if(data->conn) { + /* not a setopt test run, set the value */ + if(!interf) + interf = ""; + ares_set_local_dev((ares_channel)data->conn->resolve_async.resolver, + interf); + } return CURLE_OK; #else /* c-ares version too old! */ (void)data; @@ -900,8 +923,11 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, } } - ares_set_local_ip4((ares_channel)data->state.async.resolver, - ntohl(a4.s_addr)); + if(data->conn) { + /* not a setopt test run, set the value */ + ares_set_local_ip4((ares_channel)data->conn->resolve_async.resolver, + ntohl(a4.s_addr)); + } return CURLE_OK; #else /* c-ares version too old! */ @@ -927,7 +953,10 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, } } - ares_set_local_ip6((ares_channel)data->state.async.resolver, a6); + if(data->conn) { + /* not a setopt test run, set the value */ + ares_set_local_ip6((ares_channel)data->conn->resolve_async.resolver, a6); + } return CURLE_OK; #else /* c-ares version too old! */ diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index a2e294f..63414b6 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -136,7 +136,7 @@ static void destroy_async_data(struct Curl_async *); */ void Curl_resolver_cancel(struct Curl_easy *data) { - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); } /* This function is used to init a threaded resolve */ @@ -173,7 +173,7 @@ struct thread_data { static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) { - return &(data->state.async.tdata->tsd); + return &(data->conn->resolve_async.tdata->tsd); } /* Destroy resolver thread synchronization data */ @@ -196,7 +196,7 @@ void destroy_thread_sync_data(struct thread_sync_data *tsd) * the other end (for reading) is always closed in the parent thread. */ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[1]); + wakeup_close(tsd->sock_pair[1]); } #endif memset(tsd, 0, sizeof(*tsd)); @@ -233,8 +233,8 @@ int init_thread_sync_data(struct thread_data *td, Curl_mutex_init(tsd->mtx); #ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */ - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) { + /* create socket pair or pipe */ + if(wakeup_create(&tsd->sock_pair[0]) < 0) { tsd->sock_pair[0] = CURL_SOCKET_BAD; tsd->sock_pair[1] = CURL_SOCKET_BAD; goto err_exit; @@ -254,7 +254,7 @@ int init_thread_sync_data(struct thread_data *td, err_exit: #ifndef CURL_DISABLE_SOCKETPAIR if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[0]); + wakeup_close(tsd->sock_pair[0]); tsd->sock_pair[0] = CURL_SOCKET_BAD; } #endif @@ -320,7 +320,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { /* DNS has been resolved, signal client task */ buf[0] = 1; - if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { + if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { /* update sock_erro to errno */ tsd->sock_error = SOCKERRNO; } @@ -428,9 +428,9 @@ static bool init_resolve_thread(struct Curl_easy *data, { struct thread_data *td = calloc(1, sizeof(struct thread_data)); int err = ENOMEM; - struct Curl_async *asp = &data->state.async; + struct Curl_async *asp = &data->conn->resolve_async; - data->state.async.tdata = td; + data->conn->resolve_async.tdata = td; if(!td) goto errno_exit; @@ -488,7 +488,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, CURLcode result = CURLE_OK; DEBUGASSERT(data); - td = data->state.async.tdata; + td = data->conn->resolve_async.tdata; DEBUGASSERT(td); DEBUGASSERT(td->thread_hnd != curl_thread_t_null); @@ -500,18 +500,18 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, else DEBUGASSERT(0); - data->state.async.done = TRUE; + data->conn->resolve_async.done = TRUE; if(entry) - *entry = data->state.async.dns; + *entry = data->conn->resolve_async.dns; - if(!data->state.async.dns && report) + if(!data->conn->resolve_async.dns && report) /* a name was not resolved, report error */ result = Curl_resolver_error(data); - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); - if(!data->state.async.dns && report) + if(!data->conn->resolve_async.dns && report) connclose(data->conn, "asynch resolve failed"); return result; @@ -524,7 +524,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, */ void Curl_resolver_kill(struct Curl_easy *data) { - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; /* If we're still resolving, we must wait for the threads to fully clean up, unfortunately. Otherwise, we can simply cancel to clean up any resolver @@ -563,7 +563,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **entry) { - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; int done = 0; DEBUGASSERT(entry); @@ -581,13 +581,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, if(done) { getaddrinfo_complete(data); - if(!data->state.async.dns) { + if(!data->conn->resolve_async.dns) { CURLcode result = Curl_resolver_error(data); - destroy_async_data(&data->state.async); + destroy_async_data(&data->conn->resolve_async); return result; } - destroy_async_data(&data->state.async); - *entry = data->state.async.dns; + destroy_async_data(&data->conn->resolve_async); + *entry = data->conn->resolve_async.dns; } else { /* poll for name lookup done with exponential backoff up to 250ms */ @@ -619,9 +619,9 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) int ret_val = 0; timediff_t milli; timediff_t ms; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; #ifndef CURL_DISABLE_SOCKETPAIR - struct thread_data *td = data->state.async.tdata; + struct thread_data *td = data->conn->resolve_async.tdata; #else (void)socks; #endif @@ -662,7 +662,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, int port, int *waitp) { - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; *waitp = 0; /* default to synchronous response */ @@ -691,7 +691,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, { struct addrinfo hints; int pf = PF_INET; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; + struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver; *waitp = 0; /* default to synchronous response */ diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c index 2a49b5a..919eb62 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c @@ -31,6 +31,7 @@ !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_POP3) || \ !defined(CURL_DISABLE_IMAP) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL) #include "curl/curl.h" #include "warnless.h" diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index 5726ff1..787d6bb 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -22,6 +22,10 @@ * ***************************************************************************/ +/* Curl's integration with Hyper. This replaces certain functions in http.c, + * based on configuration #defines. This implementation supports HTTP/1.1 but + * not HTTP/2. + */ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) @@ -172,17 +176,15 @@ static int hyper_each_header(void *userdata, Curl_debug(data, CURLINFO_HEADER_IN, headp, len); - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER; - if(data->state.hconnect) - writetype |= CLIENTWRITE_CONNECT; - if(data->req.httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; - result = Curl_client_write(data, writetype, headp, len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; - } + writetype = CLIENTWRITE_HEADER; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + if(data->req.httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; + result = Curl_client_write(data, writetype, headp, len); + if(result) { + data->state.hresult = CURLE_ABORTED_BY_CALLBACK; + return HYPER_ITER_BREAK; } result = Curl_bump_headersize(data, len, FALSE); @@ -201,7 +203,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) struct SingleRequest *k = &data->req; CURLcode result = CURLE_OK; - if(0 == k->bodywrites++) { + if(0 == k->bodywrites) { bool done = FALSE; #if defined(USE_NTLM) struct connectdata *conn = data->conn; @@ -241,11 +243,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) return HYPER_ITER_BREAK; } } - if(k->ignorebody) - return HYPER_ITER_CONTINUE; - if(0 == len) - return HYPER_ITER_CONTINUE; - Curl_debug(data, CURLINFO_DATA_IN, buf, len); result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); if(result) { @@ -253,12 +250,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) return HYPER_ITER_BREAK; } - data->req.bytecount += len; - result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } return HYPER_ITER_CONTINUE; } @@ -310,13 +301,14 @@ static CURLcode status_line(struct Curl_easy *data, Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), len); - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), len); - if(result) - return result; - } + writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; + if(data->state.hconnect) + writetype |= CLIENTWRITE_CONNECT; + result = Curl_client_write(data, writetype, + Curl_dyn_ptr(&data->state.headerb), len); + if(result) + return result; + result = Curl_bump_headersize(data, len, FALSE); return result; } @@ -551,11 +543,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, static CURLcode debug_request(struct Curl_easy *data, const char *method, - const char *path, - bool h2) + const char *path) { - char *req = aprintf("%s %s HTTP/%s\r\n", method, path, - h2?"2":"1.1"); + char *req = aprintf("%s %s HTTP/1.1\r\n", method, path); if(!req) return CURLE_OUT_OF_MEMORY; Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req)); @@ -637,7 +627,6 @@ CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, static CURLcode request_target(struct Curl_easy *data, struct connectdata *conn, const char *method, - bool h2, hyper_request *req) { CURLcode result; @@ -649,26 +638,13 @@ static CURLcode request_target(struct Curl_easy *data, if(result) return result; - if(h2 && hyper_request_set_uri_parts(req, - /* scheme */ - (uint8_t *)data->state.up.scheme, - strlen(data->state.up.scheme), - /* authority */ - (uint8_t *)conn->host.name, - strlen(conn->host.name), - /* path_and_query */ - (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting uri parts to hyper"); - result = CURLE_OUT_OF_MEMORY; - } - else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), + if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), Curl_dyn_len(&r))) { failf(data, "error setting uri to hyper"); result = CURLE_OUT_OF_MEMORY; } else - result = debug_request(data, method, Curl_dyn_ptr(&r), h2); + result = debug_request(data, method, Curl_dyn_ptr(&r)); Curl_dyn_free(&r); @@ -899,7 +875,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) const char *p_accept; /* Accept: string */ const char *method; Curl_HttpReq httpreq; - bool h2 = FALSE; const char *te = NULL; /* transfer-encoding */ hyper_code rc; @@ -907,6 +882,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) may be parts of the request that is not yet sent, since we can deal with the rest of the request in the PERFORM phase. */ *done = TRUE; + Curl_client_cleanup(data); infof(data, "Time for the Hyper dance"); memset(h, 0, sizeof(struct hyptransfer)); @@ -917,6 +893,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) Curl_http_method(data, conn, &method, &httpreq); + DEBUGASSERT(data->req.bytecount == 0); + /* setup the authentication headers */ { char *pq = NULL; @@ -972,8 +950,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } if(conn->alpn == CURL_HTTP_VERSION_2) { - hyper_clientconn_options_http2(options, 1); - h2 = TRUE; + failf(data, "ALPN protocol h2 not supported with Hyper"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto error; } hyper_clientconn_options_set_preserve_header_case(options, 1); hyper_clientconn_options_set_preserve_header_order(options, 1); @@ -1024,7 +1003,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } else { - if(!h2 && !data->state.disableexpect) { + if(!data->state.disableexpect) { data->state.expect100header = TRUE; } } @@ -1035,7 +1014,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } - result = request_target(data, conn, method, h2, req); + result = request_target(data, conn, method, req); if(result) goto error; @@ -1056,19 +1035,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) goto error; - if(!h2) { - if(data->state.aptr.host) { - result = Curl_hyper_header(data, headers, data->state.aptr.host); - if(result) - goto error; - } - } - else { - /* For HTTP/2, we show the Host: header as if we sent it, to make it look - like for HTTP/1 but it isn't actually sent since :authority is then - used. */ - Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host, - strlen(data->state.aptr.host)); + if(data->state.aptr.host) { + result = Curl_hyper_header(data, headers, data->state.aptr.host); + if(result) + goto error; } if(data->state.aptr.proxyuserpwd) { diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index 6748021..2e23b0b 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c @@ -374,7 +374,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); char *linep; size_t perline; - int error; + int error, writetype; #define SELECT_OK 0 #define SELECT_ERROR 1 @@ -386,12 +386,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return CURLE_OK; while(ts->keepon) { - ssize_t gotbytes; + ssize_t nread; char byte; /* Read one byte at a time to avoid a race condition. Wait at most one second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); + result = Curl_read(data, tunnelsocket, &byte, 1, &nread); if(result == CURLE_AGAIN) /* socket buffer drained, return */ return CURLE_OK; @@ -404,7 +404,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, break; } - if(gotbytes <= 0) { + if(nread <= 0) { if(data->set.proxyauth && data->state.authproxy.avail && data->state.aptr.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, @@ -437,11 +437,11 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, properly to know when the end of the body is reached */ CHUNKcode r; CURLcode extra; - ssize_t tookcareof = 0; + size_t consumed = 0; /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); + r = Curl_httpchunk_read(data, &byte, 1, &consumed, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE"); @@ -467,15 +467,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); - if(!data->set.suppress_connect_headers) { - /* send the header to the callback */ - int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | - (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); - - result = Curl_client_write(data, writetype, linep, perline); - if(result) - return result; - } + /* send the header to the callback */ + writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); + result = Curl_client_write(data, writetype, linep, perline); + if(result) + return result; result = Curl_bump_headersize(data, perline, TRUE); if(result) @@ -502,6 +499,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, else if(ts->chunked_encoding) { CHUNKcode r; CURLcode extra; + size_t consumed = 0; infof(data, "Ignore chunked response-body"); @@ -516,8 +514,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, - &extra); + r = Curl_httpchunk_read(data, linep + 1, 1, &consumed, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE"); @@ -1038,31 +1035,29 @@ out: return result; } -static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, +static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct h1_tunnel_state *ts = cf->ctx; - int fds; - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { + if(!cf->connected) { /* If we are not connected, but the filter "below" is * and not waiting on something, we are tunneling. */ - socks[0] = Curl_conn_cf_get_socket(cf, data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); if(ts) { /* when we've sent a CONNECT to a proxy, we should rather either wait for the socket to become readable to be able to get the response headers or if we're still sending the request, wait for write. */ - if(ts->CONNECT.sending == HTTPSEND_REQUEST) { - return GETSOCK_WRITESOCK(0); - } - return GETSOCK_READSOCK(0); + if(ts->CONNECT.sending == HTTPSEND_REQUEST) + Curl_pollset_set_out_only(data, ps, sock); + else + Curl_pollset_set_in_only(data, ps, sock); } - return GETSOCK_WRITESOCK(0); + else + Curl_pollset_set_out_only(data, ps, sock); } - return fds; } static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, @@ -1093,7 +1088,7 @@ struct Curl_cftype Curl_cft_h1_proxy = { cf_h1_proxy_connect, cf_h1_proxy_close, Curl_cf_http_proxy_get_host, - cf_h1_proxy_get_select_socks, + cf_h1_proxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index dbc895d..147acdc 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c @@ -688,12 +688,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; + if(CURL_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); - CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", - stream_id); } break; case NGHTTP2_GOAWAY: @@ -727,12 +723,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, } break; case NGHTTP2_WINDOW_UPDATE: - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - CURL_TRC_CF(data, cf, "[%d] unpausing after win update", - stream_id); + if(CURL_WANT_SEND(data)) { + drain_tunnel(cf, data, &ctx->tunnel); } break; default: @@ -909,7 +901,6 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id, { struct dynhds h2_headers; nghttp2_nv *nva = NULL; - unsigned int i; int32_t stream_id = -1; size_t nheader; CURLcode result; @@ -920,22 +911,12 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id, if(result) goto out; - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto out; } - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - if(read_callback) { nghttp2_data_provider data_prd; @@ -1187,25 +1168,31 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; } -static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) +static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); - bitmap |= GETSOCK_READSOCK(0); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + bool want_recv, want_send; - /* HTTP/2 layer wants to send data) AND there's a window to send data in */ - if(nghttp2_session_want_write(ctx->h2) && - nghttp2_session_get_remote_window_size(ctx->h2)) - bitmap |= GETSOCK_WRITESOCK(0); + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(ctx->h2 && (want_recv || want_send)) { + struct cf_call_data save; + bool c_exhaust, s_exhaust; - CF_DATA_RESTORE(cf, save); - return bitmap; + CF_DATA_SAVE(save, cf, data); + c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = ctx->tunnel.stream_id >= 0 && + !nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } } static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, @@ -1542,7 +1529,7 @@ struct Curl_cftype Curl_cft_h2_proxy = { cf_h2_proxy_connect, cf_h2_proxy_close, Curl_cf_http_proxy_get_host, - cf_h2_proxy_get_select_socks, + cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, cf_h2_proxy_send, cf_h2_proxy_recv, @@ -1560,7 +1547,7 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index 39ac415..1ca4393 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c @@ -171,23 +171,17 @@ static void cf_haproxy_close(struct Curl_cfilter *cf, cf->next->cft->do_close(cf->next, data); } -static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { - int fds; - - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected) { + if(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); + Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); } - return fds; } - struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", 0, @@ -196,7 +190,7 @@ struct Curl_cftype Curl_cft_haproxy = { cf_haproxy_connect, cf_haproxy_close, Curl_cf_def_get_host, - cf_haproxy_get_select_socks, + cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -214,7 +208,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index be54aec..b4f33c8 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c @@ -188,9 +188,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, #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; @@ -325,42 +322,25 @@ out: return result; } -static int cf_hc_get_select_socks(struct Curl_cfilter *cf, +static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { - 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); - CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc); - if(!brc) - continue; - for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { - 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++; - } + if(!cf->connected) { + struct cf_hc_ctx *ctx = cf->ctx; + struct cf_hc_baller *ballers[2]; + size_t i; + + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + if(!cf_hc_baller_is_active(b)) + continue; + Curl_conn_cf_adjust_pollset(b->cf, data, ps); } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc); - return rc; } static bool cf_hc_data_pending(struct Curl_cfilter *cf, @@ -455,7 +435,7 @@ struct Curl_cftype Curl_cft_http_connect = { cf_hc_connect, cf_hc_close, Curl_cf_def_get_host, - cf_hc_get_select_socks, + cf_hc_adjust_pollset, cf_hc_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -475,7 +455,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index ce3f9e9..e42b4a8 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -81,7 +81,7 @@ #include "memdebug.h" -#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(WIN32) +#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) /* It makes support for IPv4-mapped IPv6 addresses. * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; * Windows Vista and later: default is on; @@ -102,11 +102,7 @@ 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) @@ -127,6 +123,7 @@ static void nosigpipe(struct Curl_easy *data, curl_socket_t sockfd) { int onoff = 1; + (void)data; if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, sizeof(onoff)) < 0) { #if !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -662,7 +659,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) int err = 0; curl_socklen_t errSize = sizeof(err); -#ifdef WIN32 +#ifdef _WIN32 /* * In October 2003 we effectively nullified this function on Windows due to * problems with it using all CPU in multi-threaded cases. @@ -883,34 +880,14 @@ 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]) { - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", active)", ctx->sock); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - else { - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ") no longer at conn->sock[], discarding", ctx->sock); - /* TODO: we do not want this to happen. Need to check which - * code is messing with conn->sock[cf->sockindex] */ - } - 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 */ - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", not active)", ctx->sock); - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - } + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ")", ctx->sock); + if(ctx->sock == cf->conn->sock[cf->sockindex]) + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + if(ctx->active && cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; Curl_bufq_reset(&ctx->recvbuf); ctx->active = FALSE; ctx->buffer_recv = FALSE; @@ -1169,6 +1146,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, *done = FALSE; /* a very negative world view is best */ if(ctx->sock == CURL_SOCKET_BAD) { + int error; result = cf_socket_open(cf, data); if(result) @@ -1181,8 +1159,12 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, /* Connect TCP socket */ rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + error = SOCKERRNO; + set_local_ip(cf, data); + CURL_TRC_CF(data, cf, "local address %s port %d...", + ctx->l_ip, ctx->l_port); if(-1 == rc) { - result = socket_connect_result(data, ctx->r_ip, SOCKERRNO); + result = socket_connect_result(data, ctx->r_ip, error); goto out; } } @@ -1220,13 +1202,14 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, out: if(result) { if(ctx->error) { + set_local_ip(cf, data); data->state.os_errno = ctx->error; SET_SOCKERRNO(ctx->error); #ifndef CURL_DISABLE_VERBOSE_STRINGS { char buffer[STRERROR_LEN]; - infof(data, "connect to %s port %u failed: %s", - ctx->r_ip, ctx->r_port, + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port, Curl_strerror(ctx->error, buffer, sizeof(buffer))); } #endif @@ -1252,20 +1235,19 @@ static void cf_socket_get_host(struct Curl_cfilter *cf, *pport = cf->conn->port; } -static int cf_socket_get_select_socks(struct Curl_cfilter *cf, +static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { 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); + if(ctx->sock != CURL_SOCKET_BAD) { + if(!cf->connected) + Curl_pollset_set_out_only(data, ps, ctx->sock); + else + Curl_pollset_add_in(data, ps, ctx->sock); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - - return rc; } static bool cf_socket_data_pending(struct Curl_cfilter *cf, @@ -1518,6 +1500,9 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_SETUP: Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); break; + case CF_CTRL_FORGET_SOCKET: + ctx->sock = CURL_SOCKET_BAD; + break; } return CURLE_OK; } @@ -1612,7 +1597,7 @@ struct Curl_cftype Curl_cft_tcp = { cf_tcp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1635,7 +1620,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_TCP); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1742,7 +1727,7 @@ struct Curl_cftype Curl_cft_udp = { cf_udp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1765,7 +1750,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1793,7 +1778,7 @@ struct Curl_cftype Curl_cft_unix = { cf_tcp_connect, cf_socket_close, cf_socket_get_host, - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1816,7 +1801,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UNIX); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1857,7 +1842,7 @@ struct Curl_cftype Curl_cft_tcp_accept = { cf_tcp_accept_connect, cf_socket_close, cf_socket_get_host, /* TODO: not accurate */ - cf_socket_get_select_socks, + cf_socket_adjust_pollset, cf_socket_data_pending, cf_socket_send, cf_socket_recv, @@ -1879,7 +1864,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, Curl_conn_cf_discard_all(data, conn, sockindex); DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index f74eb40..e78ecd7 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -33,6 +33,7 @@ #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "multiif.h" #include "progress.h" +#include "select.h" #include "warnless.h" /* The last 3 #include files should be in this order */ @@ -70,12 +71,14 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, } } -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { - return cf->next? - cf->next->cft->get_select_socks(cf->next, data, socks) : 0; + /* NOP */ + (void)cf; + (void)data; + (void)ps; } bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, @@ -212,7 +215,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(cft); - cf = calloc(sizeof(*cf), 1); + cf = calloc(1, sizeof(*cf)); if(!cf) goto out; @@ -303,15 +306,6 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) cf->cft->do_close(cf, data); } -int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) -{ - if(cf) - return cf->cft->get_select_socks(cf, data, socks); - return 0; -} - ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { @@ -433,22 +427,31 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) return FALSE; } -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks) +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + /* Get the lowest not-connected filter, if there are any */ + while(cf && !cf->connected && cf->next && !cf->next->connected) + cf = cf->next; + /* From there on, give all filters a chance to adjust the pollset. + * Lower filters are called later, so they may override */ + while(cf) { + cf->cft->adjust_pollset(cf, data, ps); + cf = cf->next; + } +} + +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { - struct Curl_cfilter *cf; + int i; 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); + for(i = 0; i < 2; ++i) { + Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps); } - return GETSOCK_BLANK; } void Curl_conn_get_host(struct Curl_easy *data, int sockindex, @@ -524,6 +527,18 @@ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD; } +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex) +{ + if(data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + if(cf) + (void)Curl_conn_cf_cntrl(cf, data, TRUE, + CF_CTRL_FORGET_SOCKET, 0, NULL); + fake_sclose(data->conn->sock[sockindex]); + data->conn->sock[sockindex] = CURL_SOCKET_BAD; + } +} + static CURLcode cf_cntrl_all(struct connectdata *conn, struct Curl_easy *data, bool ignore_result, @@ -646,3 +661,142 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, &n, NULL) : CURLE_UNKNOWN_OPTION; return (result || n <= 0)? 1 : (size_t)n; } + + +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + size_t i; + (void)data; + memset(ps, 0, sizeof(*ps)); + for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) + ps->sockets[i] = CURL_SOCKET_BAD; +} + +/** + * + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + if(!VALID_SOCK(sock)) + return; + + DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); + DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + ps->actions[i] &= (unsigned char)(~remove_flags); + ps->actions[i] |= (unsigned char)add_flags; + /* all gone? remove socket */ + if(!ps->actions[i]) { + if((i + 1) < ps->num) { + memmove(&ps->sockets[i], &ps->sockets[i + 1], + (ps->num - (i + 1)) * sizeof(ps->sockets[0])); + memmove(&ps->actions[i], &ps->actions[i + 1], + (ps->num - (i + 1)) * sizeof(ps->actions[0])); + } + --ps->num; + } + return; + } + } + /* not present */ + if(add_flags) { + /* Having more SOCKETS per easy handle than what is defined + * is a programming error. This indicates that we need + * to raise this limit, making easy_pollset larger. + * Since we use this in tight loops, we do not want to make + * the pollset dynamic unnecessarily. + * The current maximum in practise is HTTP/3 eyeballing where + * we have up to 4 sockets involved in connection setup. + */ + DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE); + if(i < MAX_SOCKSPEREASYHANDLE) { + ps->sockets[i] = sock; + ps->actions[i] = (unsigned char)add_flags; + ps->num = i + 1; + } + } +} + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out) +{ + Curl_pollset_change(data, ps, sock, + (do_in?CURL_POLL_IN:0)|(do_out?CURL_POLL_OUT:0), + (!do_in?CURL_POLL_IN:0)|(!do_out?CURL_POLL_OUT:0)); +} + +static void ps_add(struct Curl_easy *data, struct easy_pollset *ps, + int bitmap, curl_socket_t *socks) +{ + if(bitmap) { + int i; + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) { + if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) { + break; + } + if(bitmap & GETSOCK_READSOCK(i)) { + if(bitmap & GETSOCK_WRITESOCK(i)) + Curl_pollset_add_inout(data, ps, socks[i]); + else + /* is READ, since we checked MASK_RW above */ + Curl_pollset_add_in(data, ps, socks[i]); + } + else + Curl_pollset_add_out(data, ps, socks[i]); + } + } +} + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks)) +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + + DEBUGASSERT(data->conn); + bitmap = get_socks_cb(data, data->conn, socks); + ps_add(data, ps, bitmap, socks); +} + +void Curl_pollset_add_socks2(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)) +{ + curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; + int bitmap; + + bitmap = get_socks_cb(data, socks); + ps_add(data, ps, bitmap, socks); +} + +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write) +{ + unsigned int i; + + (void)data; + DEBUGASSERT(VALID_SOCK(sock)); + for(i = 0; i < ps->num; ++i) { + if(ps->sockets[i] == sock) { + *pwant_read = !!(ps->actions[i] & CURL_POLL_IN); + *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT); + return; + } + } + *pwant_read = *pwant_write = FALSE; +} diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index 2c65264..09a3f16 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h @@ -60,14 +60,34 @@ typedef void Curl_cft_get_host(struct Curl_cfilter *cf, const char **pdisplay_host, int *pport); -/* Filters may return sockets and fdset flags they are waiting for. - * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets. - * @return read/write fdset for index in socks - * or GETSOCK_BLANK when nothing to wait on +struct easy_pollset; + +/* Passing in an easy_pollset for monitoring of sockets, let + * filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN). + * This may add a socket or, in case no actions remain, remove + * a socket from the set. + * + * Filter implementations need to call filters "below" *after* they have + * made their adjustments. This allows lower filters to override "upper" + * actions. If a "lower" filter is unable to write, it needs to be able + * to disallow POLL_OUT. + * + * A filter without own restrictions/preferences should not modify + * the pollset. Filters, whose filter "below" is not connected, should + * also do no adjustments. + * + * Examples: a TLS handshake, while ongoing, might remove POLL_IN + * when it needs to write, or vice versa. A HTTP/2 filter might remove + * POLL_OUT when a stream window is exhausted and a WINDOW_UPDATE needs + * to be received first and add instead POLL_IN. + * + * @param cf the filter to ask + * @param data the easy handle the pollset is about + * @param ps the pollset (inout) for the easy handle */ -typedef int Curl_cft_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); +typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); @@ -110,6 +130,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, #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 */ +#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */ /** * Handle event/control for the filter. @@ -171,7 +192,7 @@ struct Curl_cftype { Curl_cft_connect *do_connect; /* establish connection */ Curl_cft_close *do_close; /* close conn */ Curl_cft_get_host *get_host; /* host filter talks to */ - Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */ + Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ 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 */ @@ -200,9 +221,9 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, const char **phost, const char **pdisplay_host, int *pport); -int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); +void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -279,9 +300,6 @@ 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); 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, @@ -364,11 +382,22 @@ bool Curl_conn_data_pending(struct Curl_easy *data, 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. + * Tell filters to forget about the socket at sockindex. */ -int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, - curl_socket_t *socks); +void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); + +/** + * Adjust the pollset for the filter chain startgin at `cf`. + */ +void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps); + +/** + * Adjust pollset from filters installed at transfer's connection. + */ +void Curl_conn_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps); /** * Receive data through the filter chain at `sockindex` for connection @@ -468,6 +497,54 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, int sockindex); +void Curl_pollset_reset(struct Curl_easy *data, + struct easy_pollset *ps); + +/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for + * socket `sock`. If the socket is not already part of the poll set, it + * will be added. + * If the socket is present and all poll flags are cleared, it will be removed. + */ +void Curl_pollset_change(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags); + +void Curl_pollset_set(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool do_in, bool do_out); + +#define Curl_pollset_add_in(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0) +#define Curl_pollset_add_out(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0) +#define Curl_pollset_add_inout(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN|CURL_POLL_OUT, 0) +#define Curl_pollset_set_in_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_IN, CURL_POLL_OUT) +#define Curl_pollset_set_out_only(data, ps, sock) \ + Curl_pollset_change((data), (ps), (sock), \ + CURL_POLL_OUT, CURL_POLL_IN) + +void Curl_pollset_add_socks(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t *socks)); +void Curl_pollset_add_socks2(struct Curl_easy *data, + struct easy_pollset *ps, + int (*get_socks_cb)(struct Curl_easy *data, + curl_socket_t *socks)); + +/** + * Check if the pollset, as is, wants to read and/or write regarding + * the given socket. + */ +void Curl_pollset_check(struct Curl_easy *data, + struct easy_pollset *ps, curl_socket_t sock, + bool *pwant_read, bool *pwant_write); + /** * Types and macros used to keep the current easy handle in filter calls, * allowing for nested invocations. See #10336. diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 93d8768..66f18ec 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -107,7 +107,7 @@ int Curl_conncache_init(struct conncache *connc, int size) connc->closure_handle = curl_easy_init(); if(!connc->closure_handle) return 1; /* bad */ - connc->closure_handle->internal = true; + connc->closure_handle->state.internal = true; Curl_hash_init(&connc->hash, size, Curl_hash_str, Curl_str_key_compare, free_bundle_hash_entry); @@ -243,7 +243,7 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data) conn->connection_id = connc->next_connection_id++; connc->num_conn++; - DEBUGF(infof(data, "Added connection %ld. " + DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". " "The cache now contains %zu members", conn->connection_id, connc->num_conn)); @@ -379,21 +379,26 @@ conncache_find_first_connection(struct conncache *connc) bool Curl_conncache_return_conn(struct Curl_easy *data, struct connectdata *conn) { - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: - data->multi->maxconnects; + unsigned int maxconnects = !data->multi->maxconnects ? + data->multi->num_easy * 4: data->multi->maxconnects; struct connectdata *conn_candidate = NULL; conn->lastused = Curl_now(); /* it was used up until now */ - if(maxconnects > 0 && - Curl_conncache_size(data) > maxconnects) { + if(maxconnects && Curl_conncache_size(data) > maxconnects) { infof(data, "Connection cache is full, closing the oldest one"); conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) { - /* the winner gets the honour of being disconnected */ - Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); + /* Use the closure handle for this disconnect so that anything that + happens during the disconnect is not stored and associated with the + 'data' handle which already just finished a transfer and it is + important that details from this (unrelated) disconnect does not + taint meta-data in the data handle. */ + struct conncache *connc = data->state.conn_cache; + connc->closure_handle->state.buffer = data->state.buffer; + connc->closure_handle->set.buffer_size = data->set.buffer_size; + Curl_disconnect(connc->closure_handle, conn_candidate, + /* dead_connection */ FALSE); } } diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index c7ba3e2..ec5ab71 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -84,6 +84,9 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif /* * Curl_timeleft() returns the amount of milliseconds left allowed for the @@ -348,6 +351,7 @@ void Curl_conncontrol(struct connectdata *conn, */ struct eyeballer { const char *name; + const struct Curl_addrinfo *first; /* complete address list, not owned */ 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 */ @@ -359,9 +363,12 @@ struct eyeballer { expire_id timeout_id; /* ID for Curl_expire() */ CURLcode result; int error; + BIT(rewinded); /* if we rewinded the addr list */ BIT(has_started); /* attempts have started */ BIT(is_done); /* out of addresses/time */ BIT(connected); /* cf has connected */ + BIT(inconclusive); /* connect was not a hard failure, we + * might talk to a restarting server */ }; @@ -408,7 +415,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer, #endif "ip")); baller->cf_create = cf_create; - baller->addr = addr; + baller->first = baller->addr = addr; baller->ai_family = ai_family; baller->primary = primary; baller->delay_ms = delay_ms; @@ -438,6 +445,13 @@ static void baller_free(struct eyeballer *baller, } } +static void baller_rewind(struct eyeballer *baller) +{ + baller->rewinded = TRUE; + baller->addr = baller->first; + baller->inconclusive = FALSE; +} + static void baller_next_addr(struct eyeballer *baller) { baller->addr = addr_next_match(baller->addr, baller->ai_family); @@ -528,6 +542,10 @@ static CURLcode baller_start_next(struct Curl_cfilter *cf, { if(cf->sockindex == FIRSTSOCKET) { baller_next_addr(baller); + /* If we get inconclusive answers from the server(s), we make + * a second iteration over the address list */ + if(!baller->addr && baller->inconclusive && !baller->rewinded) + baller_rewind(baller); baller_start(cf, data, baller, timeoutms); } else { @@ -566,6 +584,8 @@ static CURLcode baller_connect(struct Curl_cfilter *cf, baller->result = CURLE_OPERATION_TIMEDOUT; } } + else if(baller->result == CURLE_WEIRD_SERVER_REPLY) + baller->inconclusive = TRUE; } return baller->result; } @@ -595,7 +615,7 @@ 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++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || baller->is_done) @@ -656,7 +676,7 @@ evaluate: if(not_started > 0) { int added = 0; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || baller->has_started) @@ -691,13 +711,13 @@ evaluate: /* all ballers have failed to connect. */ CURL_TRC_CF(data, cf, "all eyeballers failed"); result = CURLE_COULDNT_CONNECT; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; + if(!baller) + continue; CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", - baller?baller->name:NULL, - baller?baller->has_started:0, - baller?baller->result:0); - if(baller && baller->has_started && baller->result) { + baller->name, baller->has_started, baller->result); + if(baller->has_started && baller->result) { result = baller->result; break; } @@ -838,7 +858,7 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(ctx); DEBUGASSERT(data); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { baller_free(ctx->baller[i], data); ctx->baller[i] = NULL; } @@ -846,35 +866,22 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) ctx->winner = NULL; } -static int cf_he_get_select_socks(struct Curl_cfilter *cf, +static void cf_he_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_he_ctx *ctx = cf->ctx; - size_t i, s; - int wrc, rc = GETSOCK_BLANK; - curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE]; - - if(cf->connected) - return cf->next->cft->get_select_socks(cf->next, data, socks); - - for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { - struct eyeballer *baller = ctx->baller[i]; - if(!baller || !baller->cf) - continue; + size_t i; - 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++; + if(!cf->connected) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; + Curl_conn_cf_adjust_pollset(baller->cf, data, ps); } + CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); } - return rc; } static CURLcode cf_he_connect(struct Curl_cfilter *cf, @@ -956,7 +963,7 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf, if(cf->connected) return cf->next->cft->has_data_pending(cf->next, data); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; if(!baller || !baller->cf) continue; @@ -975,7 +982,7 @@ static struct curltime get_max_baller_time(struct Curl_cfilter *cf, size_t i; memset(&tmax, 0, sizeof(tmax)); - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; memset(&t, 0, sizeof(t)); @@ -1000,7 +1007,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf, int reply_ms = -1; size_t i; - for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { struct eyeballer *baller = ctx->baller[i]; int breply_ms; @@ -1055,7 +1062,7 @@ struct Curl_cftype Curl_cft_happy_eyeballs = { cf_he_connect, cf_he_close, Curl_cf_def_get_host, - cf_he_get_select_socks, + cf_he_adjust_pollset, cf_he_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -1089,7 +1096,7 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf, (void)data; (void)conn; *pcf = NULL; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1122,13 +1129,13 @@ struct transport_provider transport_providers[] = { #ifdef ENABLE_QUIC { TRNSPRT_QUIC, Curl_cf_quic_create }, #endif +#ifndef CURL_DISABLE_TFTP { TRNSPRT_UDP, Curl_cf_udp_create }, +#endif +#ifdef USE_UNIX_SOCKETS { 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) { @@ -1319,7 +1326,7 @@ struct Curl_cftype Curl_cft_setup = { cf_setup_connect, cf_setup_close, Curl_cf_def_get_host, - Curl_cf_def_get_select_socks, + Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, @@ -1340,7 +1347,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index ec4750e..082e0fa 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -63,6 +63,9 @@ #ifndef CURL_DISABLE_HTTP +/* allow no more than 5 "chained" compression steps */ +#define MAX_ENCODE_STACK 5 + #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ @@ -95,7 +98,7 @@ typedef enum { /* Deflate and gzip writer. */ struct zlib_writer { - struct contenc_writer super; + struct Curl_cwriter super; zlibInitState zlib_init; /* zlib init state */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ @@ -171,7 +174,7 @@ static CURLcode process_trailer(struct Curl_easy *data, } static CURLcode inflate_stream(struct Curl_easy *data, - struct contenc_writer *writer, + struct Curl_cwriter *writer, int type, zlibInitState started) { struct zlib_writer *zp = (struct zlib_writer *) writer; @@ -196,7 +199,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); /* because the buffer size is fixed, iteratively decompress and transfer to - the client via downstream_write function. */ + the client via next_write function. */ while(!done) { int status; /* zlib status */ done = TRUE; @@ -217,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, if(z->avail_out != DSIZ) { if(status == Z_OK || status == Z_STREAM_END) { zp->zlib_init = started; /* Data started. */ - result = Curl_unencode_write(data, writer->downstream, decomp, + result = Curl_cwriter_write(data, writer->next, type, decomp, DSIZ - z->avail_out); if(result) { exit_zlib(data, z, &zp->zlib_init, result); @@ -274,8 +277,8 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Deflate handler. */ -static CURLcode deflate_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode deflate_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -290,13 +293,16 @@ static CURLcode deflate_init_writer(struct Curl_easy *data, return CURLE_OK; } -static CURLcode deflate_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode deflate_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + /* Set the compressed input when this function is called */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; @@ -305,11 +311,11 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data, return process_trailer(data, zp); /* Now uncompress the data */ - return inflate_stream(data, writer, ZLIB_INFLATING); + return inflate_stream(data, writer, type, ZLIB_INFLATING); } -static void deflate_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void deflate_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -317,19 +323,19 @@ static void deflate_close_writer(struct Curl_easy *data, exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } -static const struct content_encoding deflate_encoding = { +static const struct Curl_cwtype deflate_encoding = { "deflate", NULL, - deflate_init_writer, - deflate_unencode_write, - deflate_close_writer, + deflate_do_init, + deflate_do_write, + deflate_do_close, sizeof(struct zlib_writer) }; /* Gzip handler. */ -static CURLcode gzip_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode gzip_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -441,19 +447,22 @@ static enum { } #endif -static CURLcode gzip_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode gzip_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(zp->zlib_init == ZLIB_INIT_GZIP) { /* Let zlib handle the gzip decompression entirely */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; /* Now uncompress the data */ - return inflate_stream(data, writer, ZLIB_INIT_GZIP); + return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); } #ifndef OLD_ZLIB_SUPPORT @@ -565,12 +574,12 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data, } /* We've parsed the header, now uncompress the data */ - return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); + return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); #endif } -static void gzip_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void gzip_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -578,12 +587,12 @@ static void gzip_close_writer(struct Curl_easy *data, exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } -static const struct content_encoding gzip_encoding = { +static const struct Curl_cwtype gzip_encoding = { "gzip", "x-gzip", - gzip_init_writer, - gzip_unencode_write, - gzip_close_writer, + gzip_do_init, + gzip_do_write, + gzip_do_close, sizeof(struct zlib_writer) }; @@ -593,7 +602,7 @@ static const struct content_encoding gzip_encoding = { #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { - struct contenc_writer super; + struct Curl_cwriter super; BrotliDecoderState *br; /* State structure for brotli. */ }; @@ -635,8 +644,8 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) return CURLE_WRITE_ERROR; } -static CURLcode brotli_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode brotli_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -645,8 +654,8 @@ static CURLcode brotli_init_writer(struct Curl_easy *data, return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; } -static CURLcode brotli_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode brotli_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; @@ -657,6 +666,9 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(!bp->br) return CURLE_WRITE_ERROR; /* Stream already ended. */ @@ -670,7 +682,7 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, dstleft = DSIZ; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); - result = Curl_unencode_write(data, writer->downstream, + result = Curl_cwriter_write(data, writer->next, type, decomp, DSIZ - dstleft); if(result) break; @@ -693,8 +705,8 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data, return result; } -static void brotli_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void brotli_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; @@ -706,12 +718,12 @@ static void brotli_close_writer(struct Curl_easy *data, } } -static const struct content_encoding brotli_encoding = { +static const struct Curl_cwtype brotli_encoding = { "br", NULL, - brotli_init_writer, - brotli_unencode_write, - brotli_close_writer, + brotli_do_init, + brotli_do_write, + brotli_do_close, sizeof(struct brotli_writer) }; #endif @@ -720,13 +732,13 @@ static const struct content_encoding brotli_encoding = { #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { - struct contenc_writer super; + struct Curl_cwriter super; ZSTD_DStream *zds; /* State structure for zstd. */ void *decomp; }; -static CURLcode zstd_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode zstd_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -737,8 +749,8 @@ static CURLcode zstd_init_writer(struct Curl_easy *data, return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; } -static CURLcode zstd_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode zstd_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; @@ -747,6 +759,9 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, ZSTD_outBuffer out; size_t errorCode; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(!zp->decomp) { zp->decomp = malloc(DSIZ); if(!zp->decomp) @@ -766,7 +781,7 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, return CURLE_BAD_CONTENT_ENCODING; } if(out.pos > 0) { - result = Curl_unencode_write(data, writer->downstream, + result = Curl_cwriter_write(data, writer->next, type, zp->decomp, out.pos); if(result) break; @@ -778,8 +793,8 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data, return result; } -static void zstd_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void zstd_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -795,52 +810,30 @@ static void zstd_close_writer(struct Curl_easy *data, } } -static const struct content_encoding zstd_encoding = { +static const struct Curl_cwtype zstd_encoding = { "zstd", NULL, - zstd_init_writer, - zstd_unencode_write, - zstd_close_writer, + zstd_do_init, + zstd_do_write, + zstd_do_close, sizeof(struct zstd_writer) }; #endif /* Identity handler. */ -static CURLcode identity_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void)data; - (void)writer; - return CURLE_OK; -} - -static CURLcode identity_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - return Curl_unencode_write(data, writer->downstream, buf, nbytes); -} - -static void identity_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding identity_encoding = { +static const struct Curl_cwtype identity_encoding = { "identity", "none", - identity_init_writer, - identity_unencode_write, - identity_close_writer, - sizeof(struct contenc_writer) + Curl_cwriter_def_init, + Curl_cwriter_def_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) }; /* supported content encodings table. */ -static const struct content_encoding * const encodings[] = { +static const struct Curl_cwtype * const encodings[] = { &identity_encoding, #ifdef HAVE_LIBZ &deflate_encoding, @@ -856,13 +849,17 @@ static const struct content_encoding * const encodings[] = { }; -/* Return a list of comma-separated names of supported encodings. */ -char *Curl_all_content_encodings(void) +/* Provide a list of comma-separated names of supported encodings. +*/ +void Curl_all_content_encodings(char *buf, size_t blen) { size_t len = 0; - const struct content_encoding * const *cep; - const struct content_encoding *ce; - char *ace; + const struct Curl_cwtype * const *cep; + const struct Curl_cwtype *ce; + + DEBUGASSERT(buf); + DEBUGASSERT(blen); + buf[0] = 0; for(cep = encodings; *cep; cep++) { ce = *cep; @@ -870,12 +867,12 @@ char *Curl_all_content_encodings(void) len += strlen(ce->name) + 2; } - if(!len) - return strdup(CONTENT_ENCODING_DEFAULT); - - ace = malloc(len); - if(ace) { - char *p = ace; + if(!len) { + if(blen >= sizeof(CONTENT_ENCODING_DEFAULT)) + strcpy(buf, CONTENT_ENCODING_DEFAULT); + } + else if(blen > len) { + char *p = buf; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { @@ -887,75 +884,60 @@ char *Curl_all_content_encodings(void) } p[-2] = '\0'; } - - return ace; } - /* Deferred error dummy writer. */ -static CURLcode error_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static CURLcode error_do_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void)data; (void)writer; return CURLE_OK; } -static CURLcode error_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, +static CURLcode error_do_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - char *all = Curl_all_content_encodings(); + char all[256]; + (void)Curl_all_content_encodings(all, sizeof(all)); (void) writer; (void) buf; (void) nbytes; - if(!all) - return CURLE_OUT_OF_MEMORY; + if(!(type & CLIENTWRITE_BODY)) + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + failf(data, "Unrecognized content encoding type. " "libcurl understands %s content encodings.", all); - free(all); return CURLE_BAD_CONTENT_ENCODING; } -static void error_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) +static void error_do_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void) data; (void) writer; } -static const struct content_encoding error_encoding = { - NULL, +static const struct Curl_cwtype error_writer = { + "ce-error", NULL, - error_init_writer, - error_unencode_write, - error_close_writer, - sizeof(struct contenc_writer) + error_do_init, + error_do_write, + error_do_close, + sizeof(struct Curl_cwriter) }; -/* Write data using an unencoding writer stack. "nbytes" is not - allowed to be 0. */ -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - if(!nbytes) - return CURLE_OK; - if(!writer) - return CURLE_WRITE_ERROR; - return writer->handler->unencode_write(data, writer, buf, nbytes); -} - /* Find the content encoding by name. */ -static const struct content_encoding *find_encoding(const char *name, +static const struct Curl_cwtype *find_encoding(const char *name, size_t len) { - const struct content_encoding * const *cep; + const struct Curl_cwtype * const *cep; for(cep = encodings; *cep; cep++) { - const struct content_encoding *ce = *cep; + const struct Curl_cwtype *ce = *cep; if((strncasecompare(name, ce->name, len) && !ce->name[len]) || (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) return ce; @@ -969,7 +951,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; - unsigned int order = is_transfer? 2: 1; + Curl_cwriter_phase phase = is_transfer? + CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE; CURLcode result; do { @@ -992,23 +975,32 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, Curl_httpchunk_init(data); /* init our chunky engine. */ } else if(namelen) { - const struct content_encoding *encoding; - struct contenc_writer *writer; - if(is_transfer && !data->set.http_transfer_encoding) + const struct Curl_cwtype *cwt; + struct Curl_cwriter *writer; + + if((is_transfer && !data->set.http_transfer_encoding) || + (!is_transfer && data->set.http_ce_skip)) { /* not requested, ignore */ return CURLE_OK; + } - encoding = find_encoding(name, namelen); - if(!encoding) - encoding = &error_encoding; /* Defer error at stack use. */ + if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); + return CURLE_BAD_CONTENT_ENCODING; + } + + cwt = find_encoding(name, namelen); + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ - result = Curl_client_create_writer(&writer, data, encoding, order); + result = Curl_cwriter_create(&writer, data, cwt, phase); if(result) return result; - result = Curl_client_add_writer(data, writer); + result = Curl_cwriter_add(data, writer); if(result) { - Curl_client_free_writer(data, writer); + Curl_cwriter_free(data, writer); return result; } } @@ -1028,20 +1020,15 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; } -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) +void Curl_all_content_encodings(char *buf, size_t blen) { - (void) data; - (void) writer; - (void) buf; - (void) nbytes; - return CURLE_NOT_BUILT_IN; + DEBUGASSERT(buf); + DEBUGASSERT(blen); + if(blen < sizeof(CONTENT_ENCODING_DEFAULT)) + buf[0] = 0; + else + strcpy(buf, CONTENT_ENCODING_DEFAULT); } -char *Curl_all_content_encodings(void) -{ - return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ -} #endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h index ef7930c..1addf23 100644 --- a/Utilities/cmcurl/lib/content_encoding.h +++ b/Utilities/cmcurl/lib/content_encoding.h @@ -25,15 +25,10 @@ ***************************************************************************/ #include "curl_setup.h" -struct contenc_writer; +struct Curl_cwriter; -char *Curl_all_content_encodings(void); +void Curl_all_content_encodings(char *buf, size_t blen); CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer); -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); -void Curl_unencode_cleanup(struct Curl_easy *data); - #endif /* HEADER_CURL_CONTENT_ENCODING_H */ diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index af01203..9095cea 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -330,7 +330,7 @@ static char *sanitize_cookie_path(const char *cookie_path) */ void Curl_cookie_loadfiles(struct Curl_easy *data) { - struct curl_slist *list = data->set.cookielist; + struct curl_slist *list = data->state.cookielist; if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { @@ -365,9 +365,7 @@ static void strstore(char **str, const char *newstr, size_t len) DEBUGASSERT(newstr); DEBUGASSERT(str); free(*str); - *str = Curl_memdup(newstr, len + 1); - if(*str) - (*str)[len] = 0; + *str = Curl_strndup(newstr, len); } /* @@ -1029,15 +1027,23 @@ Curl_cookie_add(struct Curl_easy *data, * dereference it. */ if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { - const psl_ctx_t *psl = Curl_psl_use(data); - int acceptable; - - if(psl) { - acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); - Curl_psl_release(data); + bool acceptable = FALSE; + char lcase[256]; + char lcookie[256]; + size_t dlen = strlen(domain); + size_t clen = strlen(co->domain); + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { + const psl_ctx_t *psl = Curl_psl_use(data); + if(psl) { + /* the PSL check requires lowercase domain name and pattern */ + Curl_strntolower(lcase, domain, dlen + 1); + Curl_strntolower(lcookie, co->domain, clen + 1); + acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); + Curl_psl_release(data); + } + else + acceptable = !bad_domain(domain, strlen(domain)); } - else - acceptable = !bad_domain(domain, strlen(domain)); if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " @@ -1347,7 +1353,7 @@ static int cookie_sort_ct(const void *p1, const void *p2) static struct Cookie *dup_cookie(struct Cookie *src) { - struct Cookie *d = calloc(sizeof(struct Cookie), 1); + struct Cookie *d = calloc(1, sizeof(struct Cookie)); if(d) { CLONE(domain); CLONE(path); diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index d4bb274..a3c5af5 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -67,9 +67,15 @@ /* disables FTP */ #cmakedefine CURL_DISABLE_FTP 1 +/* disables curl_easy_options API for existing options to curl_easy_setopt */ +#cmakedefine CURL_DISABLE_GETOPTIONS 1 + /* disables GOPHER */ #cmakedefine CURL_DISABLE_GOPHER 1 +/* disables headers-api support */ +#cmakedefine CURL_DISABLE_HEADERS_API 1 + /* disables HSTS support */ #cmakedefine CURL_DISABLE_HSTS 1 @@ -91,6 +97,9 @@ /* disables MIME support */ #cmakedefine CURL_DISABLE_MIME 1 +/* disables local binding support */ +#cmakedefine CURL_DISABLE_BINDLOCAL 1 + /* disables MQTT */ #cmakedefine CURL_DISABLE_MQTT 1 @@ -161,9 +170,6 @@ /* Define to 1 if you have _Atomic support. */ #cmakedefine HAVE_ATOMIC 1 -/* Define to 1 if you have the `fchmod' function. */ -#cmakedefine HAVE_FCHMOD 1 - /* Define to 1 if you have the `fnmatch' function. */ #cmakedefine HAVE_FNMATCH 1 @@ -201,6 +207,9 @@ /* Define to 1 if you have the fseeko function. */ #cmakedefine HAVE_FSEEKO 1 +/* Define to 1 if you have the fseeko declaration. */ +#cmakedefine HAVE_DECL_FSEEKO 1 + /* Define to 1 if you have the _fseeki64 function. */ #cmakedefine HAVE__FSEEKI64 1 @@ -306,9 +315,6 @@ /* Define to 1 if symbol `ADDRESS_FAMILY' exists */ #cmakedefine HAVE_ADDRESS_FAMILY 1 -/* Define to 1 if you have the <inttypes.h> header file. */ -#cmakedefine HAVE_INTTYPES_H 1 - /* Define to 1 if you have the ioctlsocket function. */ #cmakedefine HAVE_IOCTLSOCKET 1 @@ -492,9 +498,6 @@ /* Define to 1 if you have the <stdbool.h> header file. */ #cmakedefine HAVE_STDBOOL_H 1 -/* Define to 1 if you have the <stdint.h> header file. */ -#cmakedefine HAVE_STDINT_H 1 - /* Define to 1 if you have the strcasecmp function. */ #cmakedefine HAVE_STRCASECMP 1 @@ -591,12 +594,6 @@ /* Define to 1 if you have the <utime.h> header file. */ #cmakedefine HAVE_UTIME_H 1 -/* Define to 1 if compiler supports C99 variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_C99 1 - -/* Define to 1 if compiler supports old gcc variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_GCC 1 - /* Define to 1 if you have the windows.h header file. */ #cmakedefine HAVE_WINDOWS_H 1 diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 2ea03dd..7a5387a 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -25,7 +25,8 @@ ***************************************************************************/ #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ - || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) + || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) #include <curl/curl.h> diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h index b8c46d7..714ad71 100644 --- a/Utilities/cmcurl/lib/curl_memory.h +++ b/Utilities/cmcurl/lib/curl_memory.h @@ -68,7 +68,7 @@ #undef send #undef recv -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # undef _wcsdup @@ -134,7 +134,7 @@ extern curl_free_callback Curl_cfree; extern curl_realloc_callback Curl_crealloc; extern curl_strdup_callback Curl_cstrdup; extern curl_calloc_callback Curl_ccalloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) extern curl_wcsdup_callback Curl_cwcsdup; #endif @@ -160,7 +160,7 @@ extern curl_wcsdup_callback Curl_cwcsdup; #undef free #define free(ptr) Curl_cfree(ptr) -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # define wcsdup(ptr) Curl_cwcsdup(ptr) diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c index 522ea34..ff21098 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.c +++ b/Utilities/cmcurl/lib/curl_multibyte.c @@ -32,7 +32,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include "curl_multibyte.h" @@ -84,7 +84,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#endif /* WIN32 */ +#endif /* _WIN32 */ #if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h index ddac1f6..8b9ac71 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.h +++ b/Utilities/cmcurl/lib/curl_multibyte.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) /* * MultiByte conversions using Windows kernel32 library. @@ -33,7 +33,7 @@ wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* WIN32 */ +#endif /* _WIN32 */ /* * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() @@ -54,7 +54,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); * ensure that the curl memdebug override macros do not replace them. */ -#if defined(UNICODE) && defined(WIN32) +#if defined(UNICODE) && defined(_WIN32) #define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) #define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) @@ -78,7 +78,7 @@ typedef union { const unsigned char *const_tbyte_ptr; } xcharp_u; -#endif /* UNICODE && WIN32 */ +#endif /* UNICODE && _WIN32 */ #define curlx_unicodefree(ptr) \ do { \ diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index cc0ed91..6f6d75c 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -111,6 +111,7 @@ # include <wincrypt.h> #else # error "Can't compile NTLM support without a crypto library with DES." +# define CURL_NTLM_NOT_SUPPORTED #endif #include "urldata.h" @@ -130,6 +131,7 @@ #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" #define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) +#if !defined(CURL_NTLM_NOT_SUPPORTED) /* * Turns a 56-bit key into being 64-bit wide. */ @@ -144,6 +146,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); } +#endif #if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) /* @@ -337,6 +340,10 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); encrypt_des(plaintext, results + 16, keys + 14); +#else + (void)keys; + (void)plaintext; + (void)results; #endif } @@ -347,9 +354,11 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, unsigned char *lmbuffer /* 21 bytes */) { unsigned char pw[14]; +#if !defined(CURL_NTLM_NOT_SUPPORTED) static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ }; +#endif size_t len = CURLMIN(strlen(password), 14); Curl_strntoupper((char *)pw, password, len); diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c index aa7bea7..b087a37 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c @@ -68,7 +68,9 @@ /* Portable 'sclose_nolog' used only in child process instead of 'sclose' to avoid fooling the socket leak detector */ -#if defined(HAVE_CLOSESOCKET) +#ifdef HAVE_PIPE +# define sclose_nolog(x) close((x)) +#elif defined(HAVE_CLOSESOCKET) # define sclose_nolog(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) # define sclose_nolog(x) CloseSocket((x)) @@ -189,7 +191,7 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, goto done; } - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { + if(wakeup_create(sockfds)) { failf(data, "Could not open socket pair. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; @@ -197,8 +199,8 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, child_pid = fork(); if(child_pid == -1) { - sclose(sockfds[0]); - sclose(sockfds[1]); + wakeup_close(sockfds[0]); + wakeup_close(sockfds[1]); failf(data, "Could not fork. errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); goto done; @@ -268,7 +270,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE); while(len_in > 0) { - ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in); + ssize_t written = wakeup_write(ntlm->ntlm_auth_hlpr_socket, input, len_in); if(written == -1) { /* Interrupted by a signal, retry it */ if(errno == EINTR) @@ -282,7 +284,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, /* Read one line */ while(1) { ssize_t size = - sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); + wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); if(size == -1) { if(errno == EINTR) continue; diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h index 9ed09de..cbe51c2 100644 --- a/Utilities/cmcurl/lib/curl_path.h +++ b/Utilities/cmcurl/lib/curl_path.h @@ -28,7 +28,7 @@ #include <curl/curl.h> #include "urldata.h" -#ifdef WIN32 +#ifdef _WIN32 # undef PATH_MAX # define PATH_MAX MAX_PATH # ifndef R_OK diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 406fb42..f7cf54e 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -39,7 +39,7 @@ /* The last #include file should be: */ #include "memdebug.h" -#if defined(WIN32) && !defined(USE_LWIPSOCK) +#if defined(_WIN32) && !defined(USE_LWIPSOCK) #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) #define SET_RCVTIMEO(tv,s) int tv = s*1000 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 91ddf10..78ad298 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -262,6 +262,8 @@ static void sasl_state(struct SASL *sasl, struct Curl_easy *data, sasl->state = newstate; } +#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) /* Get the SASL server message and convert it to binary. */ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, struct bufref *out) @@ -284,6 +286,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, } return result; } +#endif /* Encode the outgoing SASL message. */ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 8557cf4..7fe6397 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -28,6 +28,11 @@ #define CURL_NO_OLDIES #endif +/* Set default _WIN32_WINNT */ +#ifdef __MINGW32__ +#include <_mingw.h> +#endif + /* * Disable Visual Studio warnings: * 4127 "conditional expression is constant" @@ -36,15 +41,7 @@ #pragma warning(disable:4127) #endif -/* - * Define WIN32 when build target is Win32 API - */ - -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 -#endif - -#ifdef WIN32 +#ifdef _WIN32 /* * Don't include unneeded stuff in Windows headers to avoid compiler * warnings and macro clashes. @@ -82,7 +79,7 @@ #ifdef _WIN32_WCE # include "config-win32ce.h" #else -# ifdef WIN32 +# ifdef _WIN32 # include "config-win32.h" # endif #endif @@ -218,6 +215,23 @@ # define CURL_DISABLE_RTSP #endif +/* + * When HTTP is disabled, disable HTTP-only features + */ + +#if defined(CURL_DISABLE_HTTP) +# define CURL_DISABLE_ALTSVC 1 +# define CURL_DISABLE_COOKIES 1 +# define CURL_DISABLE_BASIC_AUTH 1 +# define CURL_DISABLE_BEARER_AUTH 1 +# define CURL_DISABLE_AWS 1 +# define CURL_DISABLE_DOH 1 +# define CURL_DISABLE_FORM_API 1 +# define CURL_DISABLE_HEADERS_API 1 +# define CURL_DISABLE_HSTS 1 +# define CURL_DISABLE_HTTP_AUTH 1 +#endif + /* ================================================================ */ /* No system header file shall be included in this file before this */ /* point. */ @@ -335,23 +349,6 @@ #include <curl/stdcheaders.h> #endif -#ifdef __POCC__ -# include <sys/types.h> -# include <unistd.h> -# define sys_nerr EILSEQ -#endif - -/* - * Salford-C kludge section (mostly borrowed from wxWidgets). - */ -#ifdef __SALFORDC__ - #pragma suppress 353 /* Possible nested comments */ - #pragma suppress 593 /* Define not used */ - #pragma suppress 61 /* enum has no name */ - #pragma suppress 106 /* unnamed, unused parameter */ - #include <clib.h> -#endif - /* Default Windows file API selection. */ #ifdef _WIN32 # if defined(_MSC_VER) && (_INTEGRAL_MAX_BITS >= 64) @@ -515,11 +512,11 @@ 5. set dir/file naming defines */ -#ifdef WIN32 +#ifdef _WIN32 # define DIR_CHAR "\\" -#else /* WIN32 */ +#else /* _WIN32 */ # ifdef MSDOS /* Watt-32 */ @@ -544,27 +541,7 @@ # define DIR_CHAR "/" -# ifndef fileno /* sunos 4 have this as a macro! */ - int fileno(FILE *stream); -# endif - -#endif /* WIN32 */ - -/* - * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN - * defined in ws2tcpip.h as well as to provide IPv6 support. - * Does not apply if lwIP is used. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK) -# if !defined(HAVE_WS2TCPIP_H) || \ - ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN)) -# undef HAVE_GETADDRINFO_THREADSAFE -# undef HAVE_FREEADDRINFO -# undef HAVE_GETADDRINFO -# undef ENABLE_IPV6 -# endif -#endif +#endif /* _WIN32 */ /* ---------------------------------------------------------------- */ /* resolver specialty compile-time defines */ @@ -572,20 +549,11 @@ /* ---------------------------------------------------------------- */ /* - * lcc-win32 doesn't have _beginthreadex(), lacks threads support. - */ - -#if defined(__LCC__) && defined(WIN32) -# undef USE_THREADS_POSIX -# undef USE_THREADS_WIN32 -#endif - -/* * MSVC threads support requires a multi-threaded runtime library. * _beginthreadex() is not available in single-threaded ones. */ -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT) +#if defined(_MSC_VER) && !defined(_MT) # undef USE_THREADS_POSIX # undef USE_THREADS_WIN32 #endif @@ -596,6 +564,9 @@ #if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO) # define CURLRES_IPV6 +#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) +/* assume on Windows that IPv6 without getaddrinfo is a broken build */ +# error "Unexpected build: IPv6 is enabled but getaddrinfo was not found." #else # define CURLRES_IPV4 #endif @@ -615,35 +586,6 @@ /* ---------------------------------------------------------------- */ -/* - * msvc 6.0 does not have struct sockaddr_storage and - * does not define IPPROTO_ESP in winsock2.h. But both - * are available if PSDK is properly installed. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP)) -# undef HAVE_STRUCT_SOCKADDR_STORAGE -# endif -#endif - -/* - * Intentionally fail to build when using msvc 6.0 without PSDK installed. - * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK - * in lib/config-win32.h although absolutely discouraged and unsupported. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_)) -# if !defined(ALLOW_MSVC6_WITHOUT_PSDK) -# error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \ - "Windows Server 2003 PSDK" -# else -# define CURL_DISABLE_LDAP 1 -# endif -# endif -#endif - #if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) /* The lib and header are present */ #define USE_LIBIDN2 @@ -709,6 +651,18 @@ # define WARN_UNUSED_RESULT #endif +/* noreturn attribute */ + +#if !defined(CURL_NORETURN) +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) +# define CURL_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define CURL_NORETURN __declspec(noreturn) +#else +# define CURL_NORETURN +#endif +#endif + /* * Include macros and defines that should only be processed once. */ @@ -767,7 +721,7 @@ /* In Windows the default file mode is text but an application can override it. Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 */ -#if defined(WIN32) || defined(MSDOS) +#if defined(_WIN32) || defined(MSDOS) #define FOPEN_READTEXT "rt" #define FOPEN_WRITETEXT "wt" #define FOPEN_APPENDTEXT "at" @@ -822,7 +776,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define UNITTEST static #endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) +/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */ +#if defined(USE_NGHTTP2) #define USE_HTTP2 #endif @@ -835,11 +790,11 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, /* 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) +#if defined(HAVE_BASENAME) && defined(_WIN32) #undef HAVE_BASENAME #endif -#if defined(USE_UNIX_SOCKETS) && defined(WIN32) +#if defined(USE_UNIX_SOCKETS) && defined(_WIN32) # if !defined(UNIX_PATH_MAX) /* Replicating logic present in afunix.h (distributed with newer Windows 10 SDK versions only) */ diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index c1ed059..bf0ee66 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -56,7 +56,7 @@ #include <sys/time.h> #endif -#ifdef WIN32 +#ifdef _WIN32 #include <io.h> #include <fcntl.h> #endif @@ -70,11 +70,7 @@ #endif #ifdef USE_WOLFSSL -# if defined(HAVE_STDINT_H) -# include <stdint.h> -# elif defined(HAVE_INTTYPES_H) -# include <inttypes.h> -# endif +#include <stdint.h> #endif #ifdef USE_SCHANNEL diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index 5af7c24..b26c391 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h @@ -88,6 +88,22 @@ extern PSecurityFunctionTable s_pSecFn; # define CRYPT_E_REVOKED ((HRESULT)0x80092010L) #endif +#ifndef CRYPT_E_NO_REVOCATION_DLL +# define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L) +#endif + +#ifndef CRYPT_E_NO_REVOCATION_CHECK +# define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L) +#endif + +#ifndef CRYPT_E_REVOCATION_OFFLINE +# define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L) +#endif + +#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE +# define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) +#endif + #ifdef UNICODE # define SECFLAG_WINNT_AUTH_IDENTITY \ (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index e53b305..0ebe40b 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c @@ -61,10 +61,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; if(data->set.fdebug) { bool inCallback = Curl_is_in_callback(data); - /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to - distinguish their handle from internal handles. */ - if(data->internal) - DEBUGASSERT(!data->set.private_data); Curl_set_in_callback(data, true); (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); Curl_set_in_callback(data, inCallback); @@ -109,6 +105,8 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) } } +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* Curl_infof() is for info message along the way */ #define MAXINFO 2048 @@ -128,13 +126,11 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...) } } -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { DEBUGASSERT(cf); - if(data && Curl_trc_cf_is_verbose(cf, data)) { + if(Curl_trc_cf_is_verbose(cf, data)) { va_list ap; int len; char buffer[MAXINFO + 2]; @@ -232,24 +228,14 @@ CURLcode Curl_trc_init(void) if(config) { return Curl_trc_opt(config); } -#endif +#endif /* DEBUGBUILD */ return CURLE_OK; } -#else /* !CURL_DISABLE_VERBOSE_STRINGS) */ +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ CURLcode Curl_trc_init(void) { return CURLE_OK; } -#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...) -{ - (void)data; - (void)cf; - (void)fmt; -} -#endif - -#endif /* !DEBUGBUILD */ +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index 84b5471..ade9108 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h @@ -55,19 +55,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, char *ptr, size_t size); /** - * Output an informational message when transfer's verbose logging is enabled. - */ -void Curl_infof(struct Curl_easy *data, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else - const char *fmt, ...); -#endif - -/** * Output a failure message on registered callbacks for transfer. */ void Curl_failf(struct Curl_easy *data, @@ -82,39 +69,15 @@ void Curl_failf(struct Curl_easy *data, #define failf Curl_failf -/** - * Output an informational message when both transfer's verbose logging - * and connection filters verbose logging are enabled. - */ -void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 3, 4))); -#else - const char *fmt, ...); -#endif - #define CURL_LOG_LVL_NONE 0 #define CURL_LOG_LVL_INFO 1 -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) -/* informational messages enabled */ - -#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) -#define Curl_trc_cf_is_verbose(cf, data) \ - ((data) && (data)->set.verbose && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) - -/* explainer: we have some mix configuration and werror settings - * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced - * on gnuc and some other compiler. Need to treat carefully. - */ -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define CURL_HAVE_C99 +#endif +#ifdef CURL_HAVE_C99 #define infof(data, ...) \ do { if(Curl_trc_is_verbose(data)) \ Curl_infof(data, __VA_ARGS__); } while(0) @@ -122,29 +85,62 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, do { if(Curl_trc_cf_is_verbose(cf, data)) \ Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) -#else /* no variadic macro args */ +#else #define infof Curl_infof #define CURL_TRC_CF Curl_trc_cf_infof -#endif /* variadic macro args */ +#endif + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ -#else /* !CURL_DISABLE_VERBOSE_STRINGS */ +#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) +#define Curl_trc_cf_is_verbose(cf, data) \ + ((data) && (data)->set.verbose && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) + +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, +#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +#else + const char *fmt, ...); +#endif + +#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ /* All informational messages are not compiled in for size savings */ #define Curl_trc_is_verbose(d) ((void)(d), FALSE) #define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE) -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#define CURL_TRC_CF(...) Curl_nop_stmt -#define Curl_trc_cf_infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#define CURL_TRC_CF(x...) Curl_nop_stmt -#define Curl_trc_cf_infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif +static void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +static void Curl_trc_cf_infof(struct Curl_easy *data, + struct Curl_cfilter *cf, + const char *fmt, ...) +{ + (void)data; (void)cf; (void)fmt; +} -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ #endif /* HEADER_CURL_TRC_H */ diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index bb0c89e..1d928e9 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -242,7 +242,7 @@ static CURLcode dohprobe(struct Curl_easy *data, /* pass in the struct pointer via a local variable to please coverity and the gcc typecheck helpers */ struct dynbuf *resp = &p->serverdoh; - doh->internal = true; + doh->state.internal = true; ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); @@ -252,6 +252,7 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); #ifdef USE_HTTP2 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); #endif #ifndef CURLDEBUG /* enforce HTTPS if not debug */ @@ -339,9 +340,10 @@ static CURLcode dohprobe(struct Curl_easy *data, doh->set.dohfor = data; /* identify for which transfer this is done */ p->easy = doh; - /* DoH private_data must be null because the user must have a way to - distinguish their transfer's handle from DoH handles in user - callbacks (ie SSL CTX callback). */ + /* DoH handles must not inherit private_data. The handles may be passed to + the user via callbacks and the user will be able to identify them as + internal handles because private data is not set. The user can then set + private_data via CURLOPT_PRIVATE if they so choose. */ DEBUGASSERT(!doh->set.private_data); if(curl_multi_add_handle(multi, doh)) @@ -372,7 +374,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, int slot; struct dohdata *dohp; struct connectdata *conn = data->conn; - *waitp = TRUE; /* this never returns synchronously */ + *waitp = FALSE; (void)hostname; (void)port; @@ -380,7 +382,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, DEBUGASSERT(conn); /* start clean, consider allocating this struct on demand */ - dohp = data->req.doh = calloc(sizeof(struct dohdata), 1); + dohp = data->req.doh = calloc(1, sizeof(struct dohdata)); if(!dohp) return NULL; @@ -412,12 +414,14 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, dohp->pending++; } #endif + *waitp = TRUE; /* this never returns synchronously */ return NULL; error: curl_slist_free_all(dohp->headers); data->req.doh->headers = NULL; for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { + (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); Curl_close(&dohp->probe[slot].easy); } Curl_safefree(data->req.doh); @@ -787,8 +791,8 @@ static void showdoh(struct Curl_easy *data, * must be an associated call later to Curl_freeaddrinfo(). */ -static struct Curl_addrinfo * -doh2ai(const struct dohentry *de, const char *hostname, int port) +static CURLcode doh2ai(const struct dohentry *de, const char *hostname, + int port, struct Curl_addrinfo **aip) { struct Curl_addrinfo *ai; struct Curl_addrinfo *prevai = NULL; @@ -801,9 +805,10 @@ doh2ai(const struct dohentry *de, const char *hostname, int port) int i; size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ - if(!de) - /* no input == no output! */ - return NULL; + DEBUGASSERT(de); + + if(!de->numaddr) + return CURLE_COULDNT_RESOLVE_HOST; for(i = 0; i < de->numaddr; i++) { size_t ss_size; @@ -876,8 +881,9 @@ doh2ai(const struct dohentry *de, const char *hostname, int port) Curl_freeaddrinfo(firstai); firstai = NULL; } + *aip = firstai; - return firstai; + return result; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -898,6 +904,7 @@ UNITTEST void de_cleanup(struct dohentry *d) CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dnsp) { + struct connectdata *conn = data->conn; CURLcode result; struct dohdata *dohp = data->req.doh; *dnsp = NULL; /* defaults to no response */ @@ -906,7 +913,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { - failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); + failf(data, "Could not DoH-resolve: %s", conn->resolve_async.hostname); return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } @@ -932,10 +939,12 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, p->dnstype, &de); Curl_dyn_free(&p->serverdoh); +#ifndef CURL_DISABLE_VERBOSE_STRINGS if(rc[slot]) { infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), type2name(p->dnstype), dohp->host); } +#endif } /* next slot */ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ @@ -947,10 +956,10 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, infof(data, "DoH Host name: %s", dohp->host); showdoh(data, &de); - ai = doh2ai(&de, dohp->host, dohp->port); - if(!ai) { + result = doh2ai(&de, dohp->host, dohp->port, &ai); + if(result) { de_cleanup(&de); - return CURLE_OUT_OF_MEMORY; + return result; } if(data->share) @@ -967,7 +976,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, Curl_freeaddrinfo(ai); } else { - data->state.async.dns = dns; + conn->resolve_async.dns = dns; *dnsp = dns; result = CURLE_OK; /* address resolution OK */ } diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index 0c9c491..2973d8d 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -77,6 +77,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, DEBUGASSERT(indx < s->toobig); DEBUGASSERT(!s->leng || s->bufr); DEBUGASSERT(a <= s->toobig); + DEBUGASSERT(!len || mem); if(fit > s->toobig) { Curl_dyn_free(s); @@ -174,10 +175,12 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) */ CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) { - size_t n = strlen(str); + size_t n; + DEBUGASSERT(str); DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); + n = strlen(str); return dyn_nappend(s, (unsigned char *)str, n); } @@ -191,6 +194,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); + DEBUGASSERT(fmt); rc = Curl_dyn_vprintf(s, fmt, ap); if(!rc) diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c index 979b3e8..d754895 100644 --- a/Utilities/cmcurl/lib/dynhds.c +++ b/Utilities/cmcurl/lib/dynhds.c @@ -27,6 +27,10 @@ #include "strcase.h" /* The last 3 #include files should be in this order */ +#ifdef USE_NGHTTP2 +#include <stdint.h> +#include <nghttp2/nghttp2.h> +#endif /* USE_NGHTTP2 */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" @@ -365,3 +369,28 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) return result; } +#ifdef USE_NGHTTP2 + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount) +{ + nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); + size_t i; + + *pcount = 0; + if(!nva) + return NULL; + + for(i = 0; i < dynhds->hds_len; ++i) { + struct dynhds_entry *e = dynhds->hds[i]; + DEBUGASSERT(e); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP2_NV_FLAG_NONE; + } + *pcount = dynhds->hds_len; + return nva; +} + +#endif /* USE_NGHTTP2 */ diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h index 8a05348..3b53600 100644 --- a/Utilities/cmcurl/lib/dynhds.h +++ b/Utilities/cmcurl/lib/dynhds.h @@ -171,4 +171,13 @@ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf); +#ifdef USE_NGHTTP2 + +#include <stdint.h> +#include <nghttp2/nghttp2.h> + +nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount); + +#endif /* USE_NGHTTP2 */ + #endif /* HEADER_CURL_DYNHDS_H */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 6b4fb8e..322d1a4 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -112,7 +112,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #define system_strdup strdup #endif -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +#if defined(_MSC_VER) && defined(_DLL) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ #endif @@ -125,11 +125,11 @@ curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; #endif -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) +#if defined(_MSC_VER) && defined(_DLL) # pragma warning(default:4232) /* MSVC extension, dllimport identity */ #endif @@ -153,7 +153,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; #endif } @@ -188,18 +188,10 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } -#if defined(USE_SSH) if(Curl_ssh_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n")); goto fail; } -#endif - -#ifdef USE_WOLFSSH - if(WS_SUCCESS != wolfSSH_Init()) { - DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); - return CURLE_FAILED_INIT; - } -#endif easy_init_flags = flags; @@ -295,7 +287,7 @@ void curl_global_cleanup(void) Curl_ssl_cleanup(); Curl_resolver_global_cleanup(); -#ifdef WIN32 +#ifdef _WIN32 Curl_win32_cleanup(easy_init_flags); #endif @@ -752,7 +744,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) return CURLE_RECURSIVE_API_CALL; /* Copy the MAXCONNECTS option to the multi handle */ - curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); mcode = curl_multi_add_handle(multi, data); if(mcode) { @@ -845,8 +837,10 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) dst->set = src->set; Curl_mime_initpart(&dst->set.mimepost); - /* clear all string pointers first */ + /* clear all dest string and blob pointers first, in case we error out + mid-function */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); + memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all strings */ for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { @@ -855,8 +849,6 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) return result; } - /* clear all blob pointers first */ - memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all blobs */ for(j = (enum dupblob)0; j < BLOB_LAST; j++) { result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); @@ -866,10 +858,13 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) /* duplicate memory areas pointed to */ i = STRING_COPYPOSTFIELDS; - if(src->set.postfieldsize && src->set.str[i]) { - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); + if(src->set.str[i]) { + if(src->set.postfieldsize == -1) + dst->set.str[i] = strdup(src->set.str[i]); + else + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); if(!dst->set.str[i]) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ @@ -919,18 +914,19 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->progress.callback = data->progress.callback; #ifndef CURL_DISABLE_COOKIES - if(data->cookies) { + outcurl->state.cookielist = NULL; + if(data->cookies && data->state.cookie_engine) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ - outcurl->cookies = Curl_cookie_init(data, NULL, outcurl->cookies, + outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) goto fail; } - if(data->set.cookielist) { - outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist); - if(!outcurl->set.cookielist) + if(data->state.cookielist) { + outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) goto fail; } #endif @@ -976,33 +972,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); } #endif - /* Clone the resolver handle, if present, for the new handle */ - if(Curl_resolver_duphandle(outcurl, - &outcurl->state.async.resolver, - data->state.async.resolver)) - goto fail; - -#ifdef USE_ARES - { - CURLcode rc; - - rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - } -#endif /* USE_ARES */ Curl_initinfo(outcurl); @@ -1016,13 +985,10 @@ fail: if(outcurl) { #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(outcurl->set.cookielist); - outcurl->set.cookielist = NULL; + free(outcurl->cookies); #endif - Curl_safefree(outcurl->state.buffer); + free(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); - Curl_safefree(outcurl->state.url); - Curl_safefree(outcurl->state.referer); Curl_altsvc_cleanup(&outcurl->asi); Curl_hsts_cleanup(&outcurl->hsts); Curl_freeset(outcurl); diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h index d3fffd0..4f6764d 100644 --- a/Utilities/cmcurl/lib/easy_lock.h +++ b/Utilities/cmcurl/lib/easy_lock.h @@ -93,6 +93,15 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock) atomic_store_explicit(lock, false, memory_order_release); } +#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) + +#include <pthread.h> + +#define curl_simple_lock pthread_mutex_t +#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER +#define curl_simple_lock_lock(m) pthread_mutex_lock(m) +#define curl_simple_lock_unlock(m) pthread_mutex_unlock(m) + #else #undef GLOBAL_INIT_IS_THREADSAFE diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index ffa9fb7..c985071 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -69,7 +69,7 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) +#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__) #define DOS_FILESYSTEM 1 #elif defined(__amigaos4__) #define AMIGA_FILESYSTEM 1 @@ -414,7 +414,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) bool size_known; bool fstated = FALSE; char *buf = data->state.buffer; - curl_off_t bytecount = 0; int fd; struct FILEPROTO *file; @@ -563,7 +562,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(nread <= 0 || (size_known && (expected_size == 0))) break; - bytecount += nread; if(size_known) expected_size -= nread; @@ -571,10 +569,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(result) return result; - result = Curl_pgrsSetDownloadCounter(data, bytecount); - if(result) - return result; - if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c index 75b8a7a..851279f 100644 --- a/Utilities/cmcurl/lib/fopen.c +++ b/Utilities/cmcurl/lib/fopen.c @@ -40,6 +40,51 @@ #include "memdebug.h" /* + The dirslash() function breaks a null-terminated pathname string into + directory and filename components then returns the directory component up + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + this instead returns a "" string. + + This function returns a pointer to malloc'ed memory. + + The input path to this function is expected to have a file name part. +*/ + +#ifdef _WIN32 +#define PATHSEP "\\" +#define IS_SEP(x) (((x) == '/') || ((x) == '\\')) +#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) +#define PATHSEP "\\" +#define IS_SEP(x) ((x) == '\\') +#else +#define PATHSEP "/" +#define IS_SEP(x) ((x) == '/') +#endif + +static char *dirslash(const char *path) +{ + size_t n; + struct dynbuf out; + DEBUGASSERT(path); + Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH); + n = strlen(path); + if(n) { + /* find the rightmost path separator, if any */ + while(n && !IS_SEP(path[n-1])) + --n; + /* skip over all the path separators, if any */ + while(n && IS_SEP(path[n-1])) + --n; + } + if(Curl_dyn_addn(&out, path, n)) + return NULL; + /* if there was a directory, append a single trailing slash */ + if(n && Curl_dyn_addn(&out, PATHSEP, 1)) + return NULL; + return Curl_dyn_ptr(&out); +} + +/* * Curl_fopen() opens a file for writing with a temp name, to be renamed * to the final name when completed. If there is an existing file using this * name at the time of the open, this function will clone the mode from that @@ -50,47 +95,44 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, FILE **fh, char **tempname) { CURLcode result = CURLE_WRITE_ERROR; - unsigned char randsuffix[9]; + unsigned char randbuf[41]; char *tempstore = NULL; struct_stat sb; int fd = -1; + char *dir = NULL; *tempname = NULL; *fh = fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { return CURLE_OK; + } fclose(*fh); *fh = NULL; - result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix)); + result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); if(result) goto fail; - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); + dir = dirslash(filename); + if(dir) { + /* The temp file name should not end up too long for the target file + system */ + tempstore = aprintf("%s%s.tmp", dir, randbuf); + free(dir); + } + if(!tempstore) { result = CURLE_OUT_OF_MEMORY; goto fail; } result = CURLE_WRITE_ERROR; - fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600); + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); if(fd == -1) goto fail; -#ifdef HAVE_FCHMOD - { - struct_stat nsb; - if((fstat(fd, &nsb) != -1) && - (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { - /* if the user and group are the same, clone the original mode */ - if(fchmod(fd, (mode_t)sb.st_mode) == -1) - goto fail; - } - } -#endif - *fh = fdopen(fd, FOPEN_WRITETEXT); if(!*fh) goto fail; @@ -105,7 +147,6 @@ fail: } free(tempstore); - return result; } diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index e40c4bc..05dc9b5 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -603,9 +603,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, app passed in a bad combo, so we better check for that first. */ if(form->name) { /* copy name (without strdup; possibly not null-terminated) */ - form->name = Curl_memdup(form->name, form->namelength? - form->namelength: - strlen(form->name) + 1); + form->name = Curl_strndup(form->name, form->namelength? + form->namelength: + strlen(form->name)); } if(!form->name) { return_value = CURL_FORMADD_MEMORY; @@ -792,7 +792,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) /* wrap call to fseeko so it matches the calling convention of callback */ static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) { -#if defined(HAVE_FSEEKO) +#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) return fseeko(stream, (off_t)offset, whence); #elif defined(HAVE__FSEEKI64) return _fseeki64(stream, (__int64)offset, whence); diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 6e7fda0..28e8ca4 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -819,7 +819,7 @@ static int ftp_domore_getsock(struct Curl_easy *data, DEBUGF(infof(data, "ftp_domore_getsock()")); if(conn->cfilter[SECONDARYSOCKET] && !Curl_conn_is_connected(conn, SECONDARYSOCKET)) - return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks); + return 0; if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); @@ -947,7 +947,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, char *port_start = NULL; char *port_sep = NULL; - addr = calloc(addrlen + 1, 1); + addr = calloc(1, addrlen + 1); if(!addr) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -4380,7 +4380,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, CURLcode result = CURLE_OK; struct ftp_conn *ftpc = &conn->proto.ftpc; - ftp = calloc(sizeof(struct FTP), 1); + ftp = calloc(1, sizeof(struct FTP)); if(!ftp) return CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index 2a7ca5b..82f1ea0 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -55,9 +55,6 @@ /* The last #include file should be: */ #include "memdebug.h" -/* allocs buffer which will contain one line of LIST command response */ -#define FTP_BUFFER_ALLOCSIZE 160 - typedef enum { PL_UNIX_TOTALSIZE = 0, PL_UNIX_FILETYPE, diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h index 075c02e..ea66d32 100644 --- a/Utilities/cmcurl/lib/functypes.h +++ b/Utilities/cmcurl/lib/functypes.h @@ -38,7 +38,7 @@ 2. For systems with config-*.h files, define them there. */ -#ifdef WIN32 +#ifdef _WIN32 /* int recv(SOCKET, char *, int, int) */ #define RECV_TYPE_ARG1 SOCKET #define RECV_TYPE_ARG2 char * diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c index 8069784..48ee972 100644 --- a/Utilities/cmcurl/lib/getenv.c +++ b/Utilities/cmcurl/lib/getenv.c @@ -31,10 +31,11 @@ static char *GetEnv(const char *variable) { -#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) || \ + defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */ (void)variable; return NULL; -#elif defined(WIN32) +#elif defined(_WIN32) /* This uses Windows API instead of C runtime getenv() to get the environment variable since some changes aren't always visible to the latter. #4774 */ char *buf = NULL; diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 2f6762c..faf01c5 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -67,10 +67,11 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, int status, struct Curl_addrinfo *ai) { + struct connectdata *conn = data->conn; struct Curl_dns_entry *dns = NULL; CURLcode result = CURLE_OK; - data->state.async.status = status; + conn->resolve_async.status = status; if(CURL_ASYNC_SUCCESS == status) { if(ai) { @@ -78,8 +79,8 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_cache_addr(data, ai, - data->state.async.hostname, 0, - data->state.async.port); + conn->resolve_async.hostname, 0, + conn->resolve_async.port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -94,12 +95,12 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, } } - data->state.async.dns = dns; + conn->resolve_async.dns = dns; /* Set async.done TRUE last in this function since it may be used multi- threaded and once this is TRUE the other thread may read fields from the async struct */ - data->state.async.done = TRUE; + conn->resolve_async.done = TRUE; /* IPv4: The input hostent struct will be freed by ares when we return from this function */ diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 3cd9a65..e7c318a 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -117,6 +117,13 @@ static void freednsentry(void *freethis); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns); +#else +#define show_resolve_info(x,y) Curl_nop_stmt +#endif + /* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is @@ -481,9 +488,11 @@ Curl_cache_addr(struct Curl_easy *data, return NULL; } #endif + if(!hostlen) + hostlen = strlen(hostname); /* Create a new cache entry */ - dns = calloc(1, sizeof(struct Curl_dns_entry)); + dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen); if(!dns) { return NULL; } @@ -497,6 +506,9 @@ Curl_cache_addr(struct Curl_easy *data, time(&dns->timestamp); if(dns->timestamp == 0) dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */ + dns->hostport = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); /* Store the resolved data in our DNS cache. */ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, @@ -521,7 +533,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) struct sockaddr_in6 sa6; unsigned char ipv6[16]; unsigned short port16 = (unsigned short)(port & 0xffff); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; @@ -568,7 +580,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) return NULL; memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); + ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; ca->ai_flags = 0; @@ -729,7 +741,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, Curl_set_in_callback(data, true); st = data->set.resolver_start( #ifdef USE_CURL_ASYNC - data->state.async.resolver, + conn->resolve_async.resolver, #else NULL, #endif @@ -823,8 +835,10 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); - else + else { rc = CURLRESOLV_RESOLVED; + show_resolve_info(data, dns); + } } } @@ -839,7 +853,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, * execution. This effectively causes the remainder of the application to run * within a signal handler which is nonportable and could lead to problems. */ -static +CURL_NORETURN static void alarmfunc(int sig) { (void)sig; @@ -1269,9 +1283,11 @@ err: Curl_freeaddrinfo(head); return CURLE_OUT_OF_MEMORY; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS infof(data, "Added %.*s:%d:%s to DNS cache%s", (int)hlen, host_begin, port, addresses, permanent ? "" : " (non-permanent)"); +#endif /* Wildcard hostname */ if((hlen == 1) && (host_begin[0] == '*')) { @@ -1285,18 +1301,89 @@ err: return CURLE_OK; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static void show_resolve_info(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct Curl_addrinfo *a; + CURLcode result = CURLE_OK; +#ifdef CURLRES_IPV6 + struct dynbuf out[2]; +#else + struct dynbuf out[1]; +#endif + DEBUGASSERT(data); + DEBUGASSERT(dns); + + if(!data->set.verbose || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + a = dns->addr; + + infof(data, "Host %s:%d was resolved.", + (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); + + Curl_dyn_init(&out[0], 1024); +#ifdef CURLRES_IPV6 + Curl_dyn_init(&out[1], 1024); +#endif + + while(a) { + if( +#ifdef CURLRES_IPV6 + a->ai_family == PF_INET6 || +#endif + a->ai_family == PF_INET) { + char buf[MAX_IPADR_LEN]; + struct dynbuf *d = &out[(a->ai_family != PF_INET)]; + Curl_printable_address(a, buf, sizeof(buf)); + if(Curl_dyn_len(d)) + result = Curl_dyn_addn(d, ", ", 2); + if(!result) + result = Curl_dyn_add(d, buf); + if(result) { + infof(data, "too many IP, can't show"); + goto fail; + } + } + a = a->ai_next; + } + +#ifdef CURLRES_IPV6 + infof(data, "IPv6: %s", + (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)")); +#endif + infof(data, "IPv4: %s", + (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)")); + +fail: + Curl_dyn_free(&out[0]); +#ifdef CURLRES_IPV6 + Curl_dyn_free(&out[1]); +#endif +} +#endif + CURLcode Curl_resolv_check(struct Curl_easy *data, struct Curl_dns_entry **dns) { + CURLcode result; #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) (void)data; (void)dns; #endif #ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - return Curl_doh_is_resolved(data, dns); + if(data->conn->bits.doh) { + result = Curl_doh_is_resolved(data, dns); + } + else #endif - return Curl_resolver_is_resolved(data, dns); + result = Curl_resolver_is_resolved(data, dns); + if(*dns) + show_resolve_info(data, *dns); + return result; } int Curl_resolv_getsock(struct Curl_easy *data, @@ -1328,9 +1415,9 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) struct connectdata *conn = data->conn; #ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - conn->dns_entry = data->state.async.dns; - data->state.async.dns = NULL; + if(conn->resolve_async.dns) { + conn->dns_entry = conn->resolve_async.dns; + conn->resolve_async.dns = NULL; } #endif @@ -1352,11 +1439,11 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) #ifdef USE_CURL_ASYNC CURLcode Curl_resolver_error(struct Curl_easy *data) { + struct connectdata *conn = data->conn; const char *host_or_proxy; CURLcode result; #ifndef CURL_DISABLE_PROXY - struct connectdata *conn = data->conn; if(conn->bits.httpproxy) { host_or_proxy = "proxy"; result = CURLE_COULDNT_RESOLVE_PROXY; @@ -1369,7 +1456,7 @@ CURLcode Curl_resolver_error(struct Curl_easy *data) } failf(data, "Could not resolve %s: %s", host_or_proxy, - data->state.async.hostname); + conn->resolve_async.hostname); return result; } diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index b68f539..fb53a57 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -64,6 +64,10 @@ struct Curl_dns_entry { time_t timestamp; /* use-counter, use Curl_resolv_unlock to release reference */ long inuse; + /* hostname port number that resolved to addr. */ + int hostport; + /* hostname that resolved to addr. may be NULL (unix domain sockets). */ + char hostname[1]; }; bool Curl_host_is_ipnum(const char *hostname); diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index 6b0ba55..18969a7 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -71,8 +71,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) #if defined(CURLRES_SYNCH) #ifdef DEBUG_ADDRINFO -static void dump_addrinfo(struct connectdata *conn, - const struct Curl_addrinfo *ai) +static void dump_addrinfo(const struct Curl_addrinfo *ai) { printf("dump_addrinfo:\n"); for(; ai; ai = ai->ai_next) { @@ -84,7 +83,7 @@ static void dump_addrinfo(struct connectdata *conn, } } #else -#define dump_addrinfo(x,y) Curl_nop_stmt +#define dump_addrinfo(x) Curl_nop_stmt #endif /* @@ -149,7 +148,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, Curl_addrinfo_set_port(res, port); } - dump_addrinfo(conn, res); + dump_addrinfo(res); return res; } diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index 7ecf004..9314be2 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c @@ -40,6 +40,7 @@ #include "fopen.h" #include "rename.h" #include "share.h" +#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -76,7 +77,7 @@ static time_t hsts_debugtime(void *unused) struct hsts *Curl_hsts_init(void) { - struct hsts *h = calloc(sizeof(struct hsts), 1); + struct hsts *h = calloc(1, sizeof(struct hsts)); if(h) { Curl_llist_init(&h->list, NULL); } @@ -108,7 +109,7 @@ void Curl_hsts_cleanup(struct hsts **hp) static struct stsentry *hsts_entry(void) { - return calloc(sizeof(struct stsentry), 1); + return calloc(1, sizeof(struct stsentry)); } static CURLcode hsts_create(struct hsts *h, @@ -116,23 +117,30 @@ static CURLcode hsts_create(struct hsts *h, bool subdomains, curl_off_t expires) { - struct stsentry *sts = hsts_entry(); + struct stsentry *sts; char *duphost; size_t hlen; + DEBUGASSERT(h); + DEBUGASSERT(hostname); + + hlen = strlen(hostname); + if(hlen && (hostname[hlen - 1] == '.')) + /* strip off any trailing dot */ + --hlen; + if(!hlen) + /* no host name left */ + return CURLE_BAD_FUNCTION_ARGUMENT; + + sts = hsts_entry(); if(!sts) return CURLE_OUT_OF_MEMORY; - duphost = strdup(hostname); + duphost = Curl_strndup(hostname, hlen); if(!duphost) { free(sts); return CURLE_OUT_OF_MEMORY; } - hlen = strlen(duphost); - if(duphost[hlen - 1] == '.') - /* strip off trailing any dot */ - duphost[--hlen] = 0; - sts->host = duphost; sts->expires = expires; sts->includeSubDomains = subdomains; @@ -564,7 +572,7 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) void Curl_hsts_loadfiles(struct Curl_easy *data) { - struct curl_slist *l = data->set.hstslist; + struct curl_slist *l = data->state.hstslist; if(l) { Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 40ef70d..be6d442 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -836,6 +836,7 @@ output_auth_headers(struct Curl_easy *data, (data->state.aptr.user ? data->state.aptr.user : "")); #else + (void)proxy; infof(data, "Server auth using %s with user '%s'", auth, data->state.aptr.user ? data->state.aptr.user : ""); @@ -845,7 +846,7 @@ output_auth_headers(struct Curl_easy *data, else authstatus->multipass = FALSE; - return CURLE_OK; + return result; } /** @@ -970,17 +971,21 @@ Curl_http_output_auth(struct Curl_easy *data, } #endif -/* - * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: - * headers. They are dealt with both in the transfer.c main loop and in the - * proxy CONNECT loop. - */ - +#if defined(USE_SPNEGO) || defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) static int is_valid_auth_separator(char ch) { return ch == '\0' || ch == ',' || ISSPACE(ch); } +#endif +/* + * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: + * headers. They are dealt with both in the transfer.c main loop and in the + * proxy CONNECT loop. + */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth) /* the first non-space */ { @@ -992,11 +997,15 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : &conn->http_negotiate_state; #endif +#if defined(USE_SPNEGO) || \ + defined(USE_NTLM) || \ + !defined(CURL_DISABLE_DIGEST_AUTH) || \ + !defined(CURL_DISABLE_BASIC_AUTH) || \ + !defined(CURL_DISABLE_BEARER_AUTH) + unsigned long *availp; struct auth *authp; - (void) conn; /* In case conditionals make it unused. */ - if(proxy) { availp = &data->info.proxyauthavail; authp = &data->state.authproxy; @@ -1005,6 +1014,11 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, availp = &data->info.httpauthavail; authp = &data->state.authhost; } +#else + (void) proxy; +#endif + + (void) conn; /* In case conditionals make it unused. */ /* * Here we check if we want the specific single authentication (using ==) and @@ -1140,7 +1154,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } } #else - ; + { + /* + * Empty block to terminate the if-else chain correctly. + * + * A semicolon would yield the same result here, but can cause a + * compiler warning when -Wextra is enabled. + */ + } #endif /* there may be multiple methods on one line, so keep reading */ @@ -1403,7 +1424,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, * and install our own `data->state.fread_func` that * on subsequent calls reads `in` empty. * - when the whisked away `in` is empty, the `fread_func` - * is restored ot its original state. + * is restored to its original state. * The problem is that `fread_func` can only return * `upload_buffer_size` lengths. If the send we do here * is larger and blocks, we do re-sending with smaller @@ -1678,8 +1699,6 @@ static CURLcode expect100(struct Curl_easy *data, struct dynbuf *req) { CURLcode result = CURLE_OK; - data->state.expect100header = FALSE; /* default to false unless it is set - to TRUE below */ if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) && (conn->httpversion < 20)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an @@ -2414,14 +2433,16 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, /* Convert the form structure into a mime structure, then keep the conversion */ if(!data->state.formp) { - data->state.formp = calloc(sizeof(curl_mimepart), 1); + data->state.formp = calloc(1, sizeof(curl_mimepart)); if(!data->state.formp) return CURLE_OUT_OF_MEMORY; Curl_mime_cleanpart(data->state.formp); result = Curl_getformdata(data, data->state.formp, data->set.httppost, data->state.fread_func); - if(result) + if(result) { + Curl_safefree(data->state.formp); return result; + } data->state.mimepost = data->state.formp; } break; @@ -2494,6 +2515,29 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, return result; } +static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn, + struct dynbuf *r) +{ + data->state.expect100header = FALSE; + /* Avoid Expect: 100-continue if Upgrade: is used */ + if(data->req.upgr101 == UPGR101_INIT) { + struct HTTP *http = data->req.p.http; + /* For really small puts we don't use Expect: headers at all, and for + the somewhat bigger ones we allow the app to disable it. Just make + sure that the expect100header is always set to the preferred value + here. */ + char *ptr = Curl_checkheaders(data, STRCONST("Expect")); + if(ptr) { + data->state.expect100header = + Curl_compareheader(ptr, STRCONST("Expect:"), + STRCONST("100-continue")); + } + else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) + return expect100(data, conn, r); + } + return CURLE_OK; +} + CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, struct dynbuf *r, Curl_HttpReq httpreq) { @@ -2506,14 +2550,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #endif CURLcode result = CURLE_OK; struct HTTP *http = data->req.p.http; - const char *ptr; - - /* If 'authdone' is FALSE, we must not set the write socket index to the - Curl_transfer() call below, as we're not ready to actually upload any - data yet. */ switch(httpreq) { - case HTTPREQ_PUT: /* Let's PUT the data to the server! */ if(conn->bits.authneg) @@ -2531,20 +2569,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - /* For really small puts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } + result = addexpect(data, conn, r); + if(result) + return result; /* end of headers */ result = Curl_dyn_addn(r, STRCONST("\r\n")); @@ -2617,22 +2644,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } #endif - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; + result = addexpect(data, conn, r); + if(result) + return result; /* make the request end in a true CRLF */ result = Curl_dyn_addn(r, STRCONST("\r\n")); @@ -2692,22 +2706,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; + result = addexpect(data, conn, r); + if(result) + return result; #ifndef USE_HYPER /* With Hyper the body is always passed on separately */ @@ -3193,7 +3194,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); break; case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ + /* continue with HTTP/1.x when explicitly requested */ break; default: /* Check if user wants to use HTTP/2 with clear TCP */ @@ -3685,7 +3686,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, k->content_range = TRUE; } } - else + else if(k->httpcode < 300) data->state.resume_from = 0; /* get everything */ } #if !defined(CURL_DISABLE_COOKIES) @@ -3996,35 +3997,30 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data, */ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, - bool *stop_reading) + const char *buf, size_t blen, + size_t *pconsumed) { CURLcode result; struct SingleRequest *k = &data->req; - ssize_t onread = *nread; - char *ostr = k->str; char *headp; - char *str_start; char *end_ptr; /* header line within buffer loop */ + *pconsumed = 0; do { - size_t rest_length; - size_t full_length; + size_t line_length; int writetype; - /* str_start is start of line within buf */ - str_start = k->str; - /* data is in network encoding so use 0x0a instead of '\n' */ - end_ptr = memchr(str_start, 0x0a, *nread); + end_ptr = memchr(buf, 0x0a, blen); if(!end_ptr) { /* Not a complete header line within buffer, append the data to the end of the headerbuff. */ - result = Curl_dyn_addn(&data->state.headerb, str_start, *nread); + result = Curl_dyn_addn(&data->state.headerb, buf, blen); if(result) return result; + *pconsumed += blen; if(!k->headerline) { /* check if this looks like a protocol header */ @@ -4036,31 +4032,28 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(st == STATUS_BAD) { /* this is not the beginning of a protocol first header line */ k->header = FALSE; - k->badheader = HEADER_ALLBAD; + k->badheader = TRUE; streamclose(conn, "bad HTTP: No end-of-message indicator"); if(!data->set.http09_allowed) { failf(data, "Received HTTP/0.9 when not allowed"); return CURLE_UNSUPPORTED_PROTOCOL; } - break; + goto out; } } - - break; /* read more and try again */ + goto out; /* read more and try again */ } /* decrease the size of the remaining (supposed) header line */ - rest_length = (end_ptr - k->str) + 1; - *nread -= (ssize_t)rest_length; - - k->str = end_ptr + 1; /* move past new line */ - - full_length = k->str - str_start; - - result = Curl_dyn_addn(&data->state.headerb, str_start, full_length); + line_length = (end_ptr - buf) + 1; + result = Curl_dyn_addn(&data->state.headerb, buf, line_length); if(result) return result; + blen -= line_length; + buf += line_length; + *pconsumed += line_length; + /**** * We now have a FULL header line in 'headerb'. *****/ @@ -4078,14 +4071,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, return CURLE_UNSUPPORTED_PROTOCOL; } k->header = FALSE; - if(*nread) + if(blen) /* since there's more, this is a partial bad header */ - k->badheader = HEADER_PARTHEADER; + k->badheader = TRUE; else { /* this was all we read so it's all a bad header */ - k->badheader = HEADER_ALLBAD; - *nread = onread; - k->str = ostr; + k->badheader = TRUE; return CURLE_OK; } break; @@ -4139,22 +4130,23 @@ 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_upgrade(data, conn, FIRSTSOCKET, - k->str, *nread); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); if(result) return result; - *nread = 0; + *pconsumed += blen; + blen = 0; } #ifdef USE_WEBSOCKETS else if(k->upgr101 == UPGR101_WS) { /* verify the response */ - result = Curl_ws_accept(data, k->str, *nread); + result = Curl_ws_accept(data, buf, blen); if(result) return result; k->header = FALSE; /* no more header to parse! */ if(data->set.connect_only) { k->keepon &= ~KEEP_RECV; /* read no more content */ - *nread = 0; + *pconsumed += blen; + blen = 0; } } #endif @@ -4366,7 +4358,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * out and return home. */ if(data->req.no_body) - *stop_reading = TRUE; + k->download_done = TRUE; #ifndef CURL_DISABLE_RTSP else if((conn->handler->protocol & CURLPROTO_RTSP) && (data->set.rtspreq == RTSPREQ_DESCRIBE) && @@ -4375,7 +4367,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, absent, a length 0 must be assumed. It will prevent libcurl from hanging on DESCRIBE request that got refused for whatever reason */ - *stop_reading = TRUE; + k->download_done = TRUE; #endif /* If max download size is *zero* (nothing) we already have @@ -4386,15 +4378,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(0 == k->maxdownload && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) - *stop_reading = TRUE; + k->download_done = TRUE; - if(*stop_reading) { - /* we make sure that this socket isn't read more now */ - k->keepon &= ~KEEP_RECV; - } - - Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen); - break; /* exit header line loop */ + Curl_debug(data, CURLINFO_HEADER_IN, + Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb)); + goto out; /* exit header line loop */ } /* We continue reading headers, reset the line-based header */ @@ -4583,12 +4572,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, Curl_dyn_reset(&data->state.headerb); } - while(*k->str); /* header line within buffer */ + while(blen); /* We might have reached the end of the header part here, but there might be a non-header part left in the end of the read buffer. */ - +out: return CURLE_OK; } @@ -4618,17 +4607,6 @@ out: return result; } -/* simple implementation of strndup(), which isn't portable */ -static char *my_strndup(const char *ptr, size_t len) -{ - char *copy = malloc(len + 1); - if(!copy) - return NULL; - memcpy(copy, ptr, len); - copy[len] = '\0'; - return copy; -} - CURLcode Curl_http_req_make(struct httpreq **preq, const char *method, size_t m_len, const char *scheme, size_t s_len, @@ -4647,17 +4625,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq, goto out; memcpy(req->method, method, m_len); if(scheme) { - req->scheme = my_strndup(scheme, s_len); + req->scheme = Curl_strndup(scheme, s_len); if(!req->scheme) goto out; } if(authority) { - req->authority = my_strndup(authority, a_len); + req->authority = Curl_strndup(authority, a_len); if(!req->authority) goto out; } if(path) { - req->path = my_strndup(path, p_len); + req->path = Curl_strndup(path, p_len); if(!req->path) goto out; } diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index 9ee3c65..56b0913 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -227,8 +227,8 @@ CURLcode Curl_http_size(struct Curl_easy *data); CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, - bool *stop_reading); + const char *buf, size_t blen, + size_t *pconsumed); /** * Curl_http_output_auth() setups the authentication headers for the @@ -263,7 +263,7 @@ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); * All about a core HTTP request, excluding body and trailers */ struct httpreq { - char method[12]; + char method[24]; char *scheme; char *authority; char *path; diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index c8b0594..9738484 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -107,14 +107,14 @@ static int populate_settings(nghttp2_settings_entry *iv, return 3; } -static size_t populate_binsettings(uint8_t *binsettings, - struct Curl_easy *data) +static ssize_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 */ + /* this returns number of bytes it wrote or a negative number on error. */ return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, iv, ivlen); } @@ -369,12 +369,15 @@ static ssize_t nw_out_writer(void *writer_ctx, { struct Curl_cfilter *cf = writer_ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); - if(nwritten > 0) - CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); - return nwritten; + if(data) { + ssize_t nwritten = Curl_conn_cf_send(cf->next, data, + (const char *)buf, buflen, err); + if(nwritten > 0) + CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); + return nwritten; + } + return 0; } static ssize_t send_callback(nghttp2_session *h2, @@ -452,9 +455,14 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, * 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 */ + ssize_t binlen; /* length of the binsettings data */ binlen = populate_binsettings(binsettings, data); + if(binlen <= 0) { + failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); + result = CURLE_FAILED_INIT; + goto out; + } result = http2_data_setup(cf, data, &stream); if(result) @@ -1076,16 +1084,11 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, stream->reset = TRUE; } stream->send_closed = TRUE; - data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); break; case NGHTTP2_WINDOW_UPDATE: - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; + if(CURL_WANT_SEND(data)) { drain_stream(cf, data, stream); - CURL_TRC_CF(data, cf, "[%d] un-holding after win update", - stream_id); } break; default: @@ -1230,15 +1233,10 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { + if(CURL_WANT_SEND(data)) { struct stream_ctx *stream = H2_STREAM_CTX(data); - data->req.keepon &= ~KEEP_SEND_HOLD; - if(stream) { + if(stream) drain_stream(cf, data, stream); - CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", - stream_id); - } } } break; @@ -1338,7 +1336,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->error = error_code; if(stream->error) stream->reset = TRUE; - data_s->req.keepon &= ~KEEP_SEND_HOLD; if(stream->error) CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", @@ -1602,10 +1599,10 @@ static int error_callback(nghttp2_session *session, size_t len, void *userp) { + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)session; - (void)msg; - (void)len; - (void)userp; + failf(data, "%.*s", (int)len, msg); return 0; } #endif @@ -1621,7 +1618,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, size_t blen; struct SingleRequest *k = &data->req; uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ + ssize_t binlen; /* length of the binsettings data */ binlen = populate_binsettings(binsettings, data); if(binlen <= 0) { @@ -2052,23 +2049,13 @@ static ssize_t h2_submit(struct stream_ctx **pstream, /* no longer needed */ Curl_h1_req_parse_free(&stream->h1); - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp2_nv) * nheader); + nva = Curl_dynhds_to_nva(&h2_headers, &nheader); if(!nva) { *err = CURLE_OUT_OF_MEMORY; nwritten = -1; goto out; } - for(i = 0; i < nheader; ++i) { - struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); - nva[i].name = (unsigned char *)e->name; - nva[i].namelen = e->namelen; - nva[i].value = (unsigned char *)e->value; - nva[i].valuelen = e->valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); @@ -2272,14 +2259,6 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * frame buffer or our network out buffer. */ size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, stream->id); - if(rwin == 0) { - /* H2 flow window exhaustion. We need to HOLD upload until we get - * a WINDOW_UPDATE from the server. */ - data->req.keepon |= KEEP_SEND_HOLD; - CURL_TRC_CF(data, cf, "[%d] holding send as remote flow " - "window is exhausted", stream->id); - } - /* Whatever the cause, we need to return CURL_EAGAIN for this call. * We have unwritten state that needs us being invoked again and EAGAIN * is the only way to ensure that. */ @@ -2331,38 +2310,34 @@ out: return nwritten; } -static int cf_h2_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *sock) +static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_h2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - struct stream_ctx *stream = H2_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; - struct cf_call_data save; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - CF_DATA_SAVE(save, cf, data); - sock[0] = Curl_conn_cf_get_socket(cf, data); - - if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD))) - /* 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); - - /* 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_SENDBITS) == 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->id))) - bitmap |= GETSOCK_WRITESOCK(0); + if(ctx->h2 && (want_recv || want_send)) { + struct stream_ctx *stream = H2_STREAM_CTX(data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; - CF_DATA_RESTORE(cf, save); - return bitmap; + CF_DATA_SAVE(save, cf, data); + c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); + s_exhaust = stream && stream->id >= 0 && + !nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->id); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } } - static CURLcode cf_h2_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) @@ -2511,14 +2486,15 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = http2_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE_SEND: { + case CF_CTRL_DATA_DONE_SEND: result = http2_data_done_send(cf, data); break; - } - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + http2_data_done(cf, data, TRUE); + break; + case CF_CTRL_DATA_DONE: http2_data_done(cf, data, arg1 != 0); break; - } default: break; } @@ -2606,7 +2582,7 @@ struct Curl_cftype Curl_cft_nghttp2 = { cf_h2_connect, cf_h2_close, Curl_cf_def_get_host, - cf_h2_get_select_socks, + cf_h2_adjust_pollset, cf_h2_data_pending, cf_h2_send, cf_h2_recv, @@ -2626,7 +2602,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(data->conn); - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; @@ -2652,7 +2628,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 901c22f..b673055 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -456,6 +456,7 @@ static CURLcode canon_query(struct Curl_easy *data, for(i = 0; !result && (i < entry); i++, ap++) { size_t len; const char *q = ap->p; + bool found_equals = false; if(!ap->len) continue; for(len = ap->len; len && !result; q++, len--) { @@ -467,9 +468,13 @@ static CURLcode canon_query(struct Curl_easy *data, case '.': case '_': case '~': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; case '=': /* allowed as-is */ result = Curl_dyn_addn(dq, q, 1); + found_equals = true; break; case '%': /* uppercase the following if hexadecimal */ @@ -497,7 +502,11 @@ static CURLcode canon_query(struct Curl_easy *data, } } } - if(i < entry - 1) { + if(!result && !found_equals) { + /* queries without value still need an equals */ + result = Curl_dyn_addn(dq, "=", 1); + } + if(!result && i < entry - 1) { /* insert ampersands between query pairs */ result = Curl_dyn_addn(dq, "&", 1); } diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 2a401d1..acdb108 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -75,8 +75,6 @@ */ -#define isxdigit_ascii(x) Curl_isxdigit(x) - void Curl_httpchunk_init(struct Curl_easy *data) { struct connectdata *conn = data->conn; @@ -98,9 +96,9 @@ void Curl_httpchunk_init(struct Curl_easy *data) * For example, 0x0d and 0x0a are used instead of '\r' and '\n'. */ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, - char *datap, - ssize_t datalen, - ssize_t *wrote, + char *buf, + size_t blen, + size_t *pconsumed, CURLcode *extrap) { CURLcode result = CURLE_OK; @@ -108,28 +106,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, struct Curl_chunker *ch = &conn->chunk; struct SingleRequest *k = &data->req; size_t piece; - curl_off_t length = (curl_off_t)datalen; - *wrote = 0; /* nothing's written yet */ + *pconsumed = 0; /* nothing's written yet */ /* the original data is written to the client, but we go on with the chunk read process, to properly calculate the content length */ if(data->set.http_te_skip && !k->ignorebody) { - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen); + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen); if(result) { *extrap = result; return CHUNKE_PASSTHRU_ERROR; } } - while(length) { + while(blen) { switch(ch->state) { case CHUNK_HEX: - if(ISXDIGIT(*datap)) { + if(ISXDIGIT(*buf)) { if(ch->hexindex < CHUNK_MAXNUM_LEN) { - ch->hexbuffer[ch->hexindex] = *datap; - datap++; - length--; + ch->hexbuffer[ch->hexindex] = *buf; + buf++; + blen--; ch->hexindex++; } else { @@ -143,7 +140,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, a hexadecimal digit. */ return CHUNKE_ILLEGAL_HEX; - /* length and datap are unmodified */ + /* blen and buf are unmodified */ ch->hexbuffer[ch->hexindex] = 0; if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) @@ -154,7 +151,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, case CHUNK_LF: /* waiting for the LF after a chunk size */ - if(*datap == 0x0a) { + if(*buf == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { ch->state = CHUNK_TRAILER; /* now check for trailers */ @@ -163,19 +160,21 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, ch->state = CHUNK_DATA; } - datap++; - length--; + buf++; + blen--; break; case CHUNK_DATA: - /* We expect 'datasize' of data. We have 'length' right now, it can be + /* We expect 'datasize' of data. We have 'blen' right now, it can be more or less than 'datasize'. Get the smallest piece. */ - piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize); + piece = blen; + if(ch->datasize < (curl_off_t)blen) + piece = curlx_sotouz(ch->datasize); /* Write the data portion available */ if(!data->set.http_te_skip && !k->ignorebody) { - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece); + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece); if(result) { *extrap = result; @@ -183,10 +182,10 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } } - *wrote += piece; + *pconsumed += piece; ch->datasize -= piece; /* decrease amount left to expect */ - datap += piece; /* move read pointer forward */ - length -= piece; /* decrease space left in this round */ + buf += piece; /* move read pointer forward */ + blen -= piece; /* decrease space left in this round */ if(0 == ch->datasize) /* end of data this round, we now expect a trailing CRLF */ @@ -194,18 +193,18 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, break; case CHUNK_POSTLF: - if(*datap == 0x0a) { + if(*buf == 0x0a) { /* The last one before we go back to hex state and start all over. */ Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */ } - else if(*datap != 0x0d) + else if(*buf != 0x0d) return CHUNKE_BAD_CHUNK; - datap++; - length--; + buf++; + blen--; break; case CHUNK_TRAILER: - if((*datap == 0x0d) || (*datap == 0x0a)) { + if((*buf == 0x0d) || (*buf == 0x0a)) { char *tr = Curl_dyn_ptr(&conn->trailer); /* this is the end of a trailer, but if the trailer was zero bytes there was no trailer and we move on */ @@ -229,7 +228,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } Curl_dyn_reset(&conn->trailer); ch->state = CHUNK_TRAILER_CR; - if(*datap == 0x0a) + if(*buf == 0x0a) /* already on the LF */ break; } @@ -240,19 +239,19 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, } } else { - result = Curl_dyn_addn(&conn->trailer, datap, 1); + result = Curl_dyn_addn(&conn->trailer, buf, 1); if(result) return CHUNKE_OUT_OF_MEMORY; } - datap++; - length--; + buf++; + blen--; break; case CHUNK_TRAILER_CR: - if(*datap == 0x0a) { + if(*buf == 0x0a) { ch->state = CHUNK_TRAILER_POSTCR; - datap++; - length--; + buf++; + blen--; } else return CHUNKE_BAD_CHUNK; @@ -261,27 +260,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, case CHUNK_TRAILER_POSTCR: /* We enter this state when a CR should arrive so we expect to have to first pass a CR before we wait for LF */ - if((*datap != 0x0d) && (*datap != 0x0a)) { + if((*buf != 0x0d) && (*buf != 0x0a)) { /* not a CR then it must be another header in the trailer */ ch->state = CHUNK_TRAILER; break; } - if(*datap == 0x0d) { + if(*buf == 0x0d) { /* skip if CR */ - datap++; - length--; + buf++; + blen--; } /* now wait for the final LF */ ch->state = CHUNK_STOP; break; case CHUNK_STOP: - if(*datap == 0x0a) { - length--; + if(*buf == 0x0a) { + blen--; /* Record the length of any data left in the end of the buffer even if there's no more chunks to read */ - ch->datasize = curlx_sotouz(length); + ch->datasize = blen; return CHUNKE_STOP; /* return stop */ } diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index ed50713..0a36f37 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -93,8 +93,8 @@ struct Curl_chunker { /* The following functions are defined in http_chunks.c */ void Curl_httpchunk_init(struct Curl_easy *data); -CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap, - ssize_t length, ssize_t *wrote, +CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *buf, + size_t blen, size_t *pconsumed, CURLcode *passthru); #endif /* HEADER_CURL_HTTP_CHUNKS_H */ diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index a1d6da9..8e18325 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -299,7 +299,7 @@ struct Curl_cftype Curl_cft_http_proxy = { http_proxy_cf_connect, http_proxy_cf_close, Curl_cf_http_proxy_get_host, - Curl_cf_def_get_select_socks, + Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index a024691..81a177f 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -36,7 +36,7 @@ #ifdef USE_LIBIDN2 #include <idn2.h> -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) #define IDN2_LOOKUP(name, host, flags) \ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) #else diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index de64c2a..47cff48 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -1194,8 +1194,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(result) return result; - data->req.bytecount += chunk; - infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU " bytes are left for transfer", chunk, size - chunk); @@ -1430,7 +1428,7 @@ static CURLcode imap_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct IMAP *imap; - imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1); + imap = data->req.p.imap = calloc(1, sizeof(struct IMAP)); if(!imap) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 239d3fb..eb5fe795 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -313,7 +313,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) int ldap_ssl = 0; char *val_b64 = NULL; size_t val_b64_sz = 0; - curl_off_t dlsize = 0; #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ #endif @@ -327,7 +326,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", - LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); + LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); infof(data, "LDAP local: %s", data->state.url); #ifdef HAVE_LDAP_URL_PARSE @@ -345,7 +344,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(conn->given->flags & PROTOPT_SSL) ldap_ssl = 1; infof(data, "LDAP local: trying to establish %s connection", - ldap_ssl ? "encrypted" : "cleartext"); + ldap_ssl ? "encrypted" : "cleartext"); #if defined(USE_WIN32_LDAP) host = curlx_convert_UTF8_to_tchar(conn->host.name); @@ -535,6 +534,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } + Curl_pgrsSetDownloadCounter(data, 0); rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); @@ -596,8 +596,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - dlsize += name_len + 5; - FREE_ON_WINLDAP(name); ldap_memfree(dn); } @@ -659,8 +657,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - dlsize += attr_len + 3; - if((attr_len > 7) && (strcmp(";binary", attr + (attr_len - 7)) == 0)) { /* Binary attribute, encode to base64. */ @@ -689,8 +685,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize += val_b64_sz; } } else { @@ -705,8 +699,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize += vals[i]->bv_len; } result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); @@ -719,8 +711,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - - dlsize++; } /* Free memory used to store values */ @@ -734,10 +724,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); if(result) goto quit; - dlsize++; - result = Curl_pgrsSetDownloadCounter(data, dlsize); - if(result) - goto quit; } if(ber) diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index 30ab62e..486e5fa 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -32,9 +32,8 @@ #include "warnless.h" #ifdef USE_OPENSSL -#include <openssl/opensslconf.h> -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \ - !defined(USE_AMISSL) +#include <openssl/opensslv.h> +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL) /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 #endif diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index d6952a0..f6ced85 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -208,7 +208,7 @@ ALLOC_FUNC char *curl_dbg_strdup(const char *str, return mem; } -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) { diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index c9eb5dc..78a0125 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -64,7 +64,7 @@ CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr, CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source); CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line, const char *src); -#if defined(WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source); @@ -121,7 +121,7 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); #define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) -#ifdef WIN32 +#ifdef _WIN32 # ifdef UNICODE # undef wcsdup # define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 3b27e59..bb66130 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -48,7 +48,7 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef WIN32 +#ifdef _WIN32 # ifndef R_OK # define R_OK 4 # endif @@ -311,8 +311,7 @@ static char *escape_string(struct Curl_easy *data, table = formtable; /* data can be NULL when this function is called indirectly from curl_formget(). */ - if(strategy == MIMESTRATEGY_MAIL || - (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE))) + if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape))) table = mimetable; Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index af5d753..6b5df5b 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -66,9 +66,7 @@ * Non-ANSI integer extensions */ -#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \ - (defined(__POCC__) && defined(_MSC_VER)) || \ - (defined(_WIN32_WCE)) || \ +#if (defined(_WIN32_WCE)) || \ (defined(__MINGW32__)) || \ (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)) # define MP_HAVE_INT_EXTENSIONS @@ -1071,9 +1069,6 @@ static int alloc_addbyter(int output, FILE *data) return outc; /* fputc() returns like this on success */ } -extern int Curl_dyn_vprintf(struct dynbuf *dyn, - const char *format, va_list ap_save); - /* appends the formatted string, returns 0 on success, 1 on error */ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) { diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 54f8882..366235c 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -616,9 +616,6 @@ static void mqstate(struct Curl_easy *data, } -/* for the publish packet */ -#define MQTT_HEADER_LEN 5 /* max 5 bytes */ - static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -677,7 +674,6 @@ MQTT_SUBACK_COMING: /* FALLTHROUGH */ case MQTT_PUB_REMAIN: { /* read rest of packet, but no more. Cap to buffer size */ - struct SingleRequest *k = &data->req; size_t rest = mq->npacket; if(rest > (size_t)data->set.buffer_size) rest = (size_t)data->set.buffer_size; @@ -693,13 +689,8 @@ MQTT_SUBACK_COMING: result = CURLE_PARTIAL_FILE; goto end; } - Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread); mq->npacket -= nread; - k->bytecount += nread; - result = Curl_pgrsSetDownloadCounter(data, k->bytecount); - if(result) - goto end; /* if QoS is set, message contains packet id */ diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index ff753ac..5456113 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -55,22 +55,6 @@ #include "curl_memory.h" #include "memdebug.h" -#ifdef __APPLE__ - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create pipe - -#else /* __APPLE__ */ - -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose -#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) - -#endif /* __APPLE__ */ - /* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every @@ -231,10 +215,6 @@ struct Curl_sh_entry { unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ }; -/* bits for 'action' having no bits means this socket is not expecting any - action */ -#define SH_READ 1 -#define SH_WRITE 2 /* look up a given socket in the socket hash, skip invalid sockets */ static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh, @@ -416,9 +396,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_llist_init(&multi->msgsent, NULL); multi->multiplexing = TRUE; - - /* -1 means it not set by user, use the default value */ - multi->maxconnects = -1; multi->max_concurrent_streams = 100; #ifdef USE_WINSOCK @@ -1040,49 +1017,61 @@ static int protocol_getsock(struct Curl_easy *data, { if(conn->handler->proto_getsock) return conn->handler->proto_getsock(data, conn, socks); - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + return GETSOCK_BLANK; } -/* returns bitmapped flags for this handle and its sockets. The 'socks[]' - array contains MAX_SOCKSPEREASYHANDLE entries. */ -static int multi_getsock(struct Curl_easy *data, - curl_socket_t *socks) +/* Initializes `poll_set` with the current socket poll actions needed + * for transfer `data`. */ +static void multi_getsock(struct Curl_easy *data, + struct easy_pollset *ps) { - struct connectdata *conn = data->conn; /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ - if(!conn) - return 0; + Curl_pollset_reset(data, ps); + if(!data->conn) + return; switch(data->mstate) { default: - return 0; + break; case MSTATE_RESOLVING: - return Curl_resolv_getsock(data, socks); + Curl_pollset_add_socks2(data, ps, Curl_resolv_getsock); + /* connection filters are not involved in this phase */ + return; case MSTATE_PROTOCONNECTING: case MSTATE_PROTOCONNECT: - return protocol_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, protocol_getsock); + break; case MSTATE_DO: case MSTATE_DOING: - return doing_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, doing_getsock); + break; case MSTATE_TUNNELING: case MSTATE_CONNECTING: - return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); + break; case MSTATE_DOING_MORE: - return domore_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, domore_getsock); + break; case MSTATE_DID: /* since is set after DO is completed, we switch to waiting for the same as the PERFORMING state */ case MSTATE_PERFORMING: - return Curl_single_getsock(data, conn, socks); + Curl_pollset_add_socks(data, ps, Curl_single_getsock); + break; + + case MSTATE_RATELIMITING: + /* nothing to wait for */ + return; } + /* Let connection filters add/remove as needed */ + Curl_conn_adjust_pollset(data, ps); } CURLMcode curl_multi_fdset(struct Curl_multi *multi, @@ -1094,8 +1083,8 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, and then we must make sure that is done. */ struct Curl_easy *data; int this_max_fd = -1; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int i; + struct easy_pollset ps; + unsigned int i; (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) @@ -1104,29 +1093,20 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + memset(&ps, 0, sizeof(ps)); for(data = multi->easyp; data; data = data->next) { - int bitmap; -#ifdef __clang_analyzer_ - /* to prevent "The left operand of '>=' is a garbage value" warnings */ - memset(sockbunch, 0, sizeof(sockbunch)); -#endif - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - if(bitmap & GETSOCK_READSOCK(i)) - FD_SET(sockbunch[i], read_fd_set); - if(bitmap & GETSOCK_WRITESOCK(i)) - FD_SET(sockbunch[i], write_fd_set); - if((int)sockbunch[i] > this_max_fd) - this_max_fd = (int)sockbunch[i]; - } - else { - break; - } + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + if(!FDSET_SOCK(ps.sockets[i])) + /* pretend it doesn't exist */ + continue; + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); + if((int)ps.sockets[i] > this_max_fd) + this_max_fd = (int)ps.sockets[i]; } } @@ -1162,9 +1142,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi, bool use_wakeup) { struct Curl_easy *data; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int bitmap; - unsigned int i; + struct easy_pollset ps; + size_t i; unsigned int nfds = 0; unsigned int curlfds; long timeout_internal; @@ -1190,17 +1169,10 @@ static CURLMcode multi_wait(struct Curl_multi *multi, return CURLM_BAD_FUNCTION_ARGUMENT; /* Count up how many fds we have from the multi handle */ + memset(&ps, 0, sizeof(ps)); for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - } - else { - break; - } - } + multi_getsock(data, &ps); + nfds += ps.num; } /* If the internally desired timeout is actually shorter than requested from @@ -1241,40 +1213,35 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { /* Add the curl handles to our pollfds first */ for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); + multi_getsock(data, &ps); - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { - struct pollfd *ufd = &ufds[nfds++]; -#ifdef USE_WINSOCK - long mask = 0; -#endif - ufd->fd = sockbunch[i]; - ufd->events = 0; - if(bitmap & GETSOCK_READSOCK(i)) { + for(i = 0; i < ps.num; i++) { + struct pollfd *ufd = &ufds[nfds++]; #ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + long mask = 0; #endif - ufd->events |= POLLIN; - } - if(bitmap & GETSOCK_WRITESOCK(i)) { + ufd->fd = ps.sockets[i]; + ufd->events = 0; + if(ps.actions[i] & CURL_POLL_IN) { #ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(sockbunch[i]); + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; #endif - ufd->events |= POLLOUT; - } + ufd->events |= POLLIN; + } + if(ps.actions[i] & CURL_POLL_OUT) { #ifdef USE_WINSOCK - if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(ps.sockets[i]); #endif + ufd->events |= POLLOUT; } - else { - break; +#ifdef USE_WINSOCK + if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) { + if(ufds_malloc) + free(ufds); + return CURLM_INTERNAL_ERROR; } +#endif } } } @@ -1386,21 +1353,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { for(data = multi->easyp; data; data = data->next) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(sockbunch[i], multi->wsa_event, 0); - } - else { - /* break on entry not checked for being readable or writable */ - break; + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(ps.sockets[i], NULL, + &wsa_events) == 0) { + if(ret && !pollrc && wsa_events.lNetworkEvents) + retcode++; } + WSAEventSelect(ps.sockets[i], multi->wsa_event, 0); } } } @@ -2021,8 +1983,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif result = CURLE_OK; infof(data, "Hostname '%s' was found in DNS cache", hostname); @@ -2895,53 +2857,36 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data) { - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - int i; + struct easy_pollset cur_poll; + unsigned int i; struct Curl_sh_entry *entry; curl_socket_t s; - int num; - unsigned int curraction; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; int rc; - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) - socks[i] = CURL_SOCKET_BAD; - /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ - curraction = multi_getsock(data, socks); + multi_getsock(data, &cur_poll); /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no longer supervised ones and add new ones */ /* walk over the sockets we got right now */ - for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & GETSOCK_MASK_RW(i)); - i++) { - unsigned char action = CURL_POLL_NONE; - unsigned char prevaction = 0; + for(i = 0; i < cur_poll.num; i++) { + unsigned char cur_action = cur_poll.actions[i]; + unsigned char last_action = 0; int comboaction; - bool sincebefore = FALSE; - s = socks[i]; + s = cur_poll.sockets[i]; /* get it from the hash */ entry = sh_getentry(&multi->sockhash, s); - - if(curraction & GETSOCK_READSOCK(i)) - action |= CURL_POLL_IN; - if(curraction & GETSOCK_WRITESOCK(i)) - action |= CURL_POLL_OUT; - - actions[i] = action; if(entry) { /* check if new for this transfer */ - int j; - for(j = 0; j< data->numsocks; j++) { - if(s == data->sockets[j]) { - prevaction = data->actions[j]; - sincebefore = TRUE; + unsigned int j; + for(j = 0; j< data->last_poll.num; j++) { + if(s == data->last_poll.sockets[j]) { + last_action = data->last_poll.actions[j]; break; } } @@ -2953,23 +2898,23 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* fatal */ return CURLM_OUT_OF_MEMORY; } - if(sincebefore && (prevaction != action)) { + if(last_action && (last_action != cur_action)) { /* Socket was used already, but different action now */ - if(prevaction & CURL_POLL_IN) + if(last_action & CURL_POLL_IN) entry->readers--; - if(prevaction & CURL_POLL_OUT) + if(last_action & CURL_POLL_OUT) entry->writers--; - if(action & CURL_POLL_IN) + if(cur_action & CURL_POLL_IN) entry->readers++; - if(action & CURL_POLL_OUT) + if(cur_action & CURL_POLL_OUT) entry->writers++; } - else if(!sincebefore) { - /* a new user */ + else if(!last_action) { + /* a new transfer using this socket */ entry->users++; - if(action & CURL_POLL_IN) + if(cur_action & CURL_POLL_IN) entry->readers++; - if(action & CURL_POLL_OUT) + if(cur_action & CURL_POLL_OUT) entry->writers++; /* add 'data' to the transfer hash on this socket! */ @@ -2980,11 +2925,11 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } - comboaction = (entry->writers? CURL_POLL_OUT : 0) | + comboaction = (entry->writers ? CURL_POLL_OUT : 0) | (entry->readers ? CURL_POLL_IN : 0); /* socket existed before and has the same action set as before */ - if(sincebefore && ((int)entry->action == comboaction)) + if(last_action && ((int)entry->action == comboaction)) /* same, continue */ continue; @@ -2992,6 +2937,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; @@ -3002,16 +2948,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi, entry->action = comboaction; /* store the current action state */ } - num = i; /* number of sockets */ - - /* when we've walked over all the sockets we should have right now, we must - make sure to detect sockets that are removed */ - for(i = 0; i< data->numsocks; i++) { - int j; + /* Check for last_poll.sockets that no longer appear in cur_poll.sockets. + * Need to remove the easy handle from the multi->sockhash->transfers and + * remove multi->sockhash entry when this was the last transfer */ + for(i = 0; i< data->last_poll.num; i++) { + unsigned int j; bool stillused = FALSE; - s = data->sockets[i]; - for(j = 0; j < num; j++) { - if(s == socks[j]) { + s = data->last_poll.sockets[i]; + for(j = 0; j < cur_poll.num; j++) { + if(s == cur_poll.sockets[j]) { /* this is still supervised */ stillused = TRUE; break; @@ -3024,7 +2969,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* if this is NULL here, the socket has been closed and notified so already by Curl_multi_closed() */ if(entry) { - unsigned char oldactions = data->actions[i]; + unsigned char oldactions = data->last_poll.actions[i]; /* this socket has been removed. Decrease user count */ entry->users--; if(oldactions & CURL_POLL_OUT) @@ -3052,11 +2997,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } } - } /* for loop over numsocks */ + } /* for loop over num */ - memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); - memcpy(data->actions, actions, num*sizeof(char)); - data->numsocks = num; + /* Remember for next time */ + memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll)); return CURLM_OK; } @@ -3296,6 +3240,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, { CURLMcode res = CURLM_OK; va_list param; + unsigned long uarg; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3328,7 +3273,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->timer_userp = va_arg(param, void *); break; case CURLMOPT_MAXCONNECTS: - multi->maxconnects = va_arg(param, long); + uarg = va_arg(param, unsigned long); + if(uarg <= UINT_MAX) + multi->maxconnects = (unsigned int)uarg; break; case CURLMOPT_MAX_HOST_CONNECTIONS: multi->max_host_connections = va_arg(param, long); @@ -3350,9 +3297,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, case CURLMOPT_MAX_CONCURRENT_STREAMS: { long streams = va_arg(param, long); - if(streams < 1) + if((streams < 1) || (streams > INT_MAX)) streams = 100; - multi->max_concurrent_streams = curlx_sltoui(streams); + multi->max_concurrent_streams = (unsigned int)streams; } break; default: @@ -3782,11 +3729,11 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi) struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) * (multi->num_easy + 1)); if(a) { - int i = 0; + unsigned int i = 0; struct Curl_easy *e = multi->easyp; while(e) { DEBUGASSERT(i < multi->num_easy); - if(!e->internal) + if(!e->state.internal) a[i++] = e; e = e->next; } diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 5b16bb6..e03e382 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -93,9 +93,9 @@ struct Curl_multi { struct Curl_easy *easyp; struct Curl_easy *easylp; /* last node */ - int num_easy; /* amount of entries in the linked list above. */ - int num_alive; /* amount of easy handles that are added but have not yet - reached COMPLETE state */ + unsigned int num_easy; /* amount of entries in the linked list above. */ + unsigned int num_alive; /* amount of easy handles that are added but have + not yet reached COMPLETE state */ struct Curl_llist msglist; /* a list of messages from completed transfers */ @@ -136,9 +136,6 @@ struct Curl_multi { /* Shared connection cache (bundles)*/ struct conncache conn_cache; - long maxconnects; /* if >0, a fixed limit of the maximum number of entries - we're allowed to grow the connection cache to */ - long max_host_connections; /* if >0, a fixed limit of the maximum number of connections per host */ @@ -150,8 +147,6 @@ struct Curl_multi { void *timer_userp; struct curltime timer_lastcall; /* the fixed time for the timeout for the previous callback */ - unsigned int max_concurrent_streams; - #ifdef USE_WINSOCK WSAEVENT wsa_event; /* winsock event used for waits */ #else @@ -160,6 +155,10 @@ struct Curl_multi { 0 is used for read, 1 is used for write */ #endif #endif + unsigned int max_concurrent_streams; + unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of + entries we're allowed to grow the connection + cache to */ #define IPV6_UNKNOWN 0 #define IPV6_DEAD 1 #define IPV6_WORKS 2 diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index e6a09b1..038c6dc 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -327,7 +327,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, } retcode = parsenetrc(host, loginp, passwordp, filealloc); free(filealloc); -#ifdef WIN32 +#ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { /* fallback to the old-style "_netrc" file */ filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index 3aff306..131f474 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -319,31 +319,12 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data, { CURLcode result; LDAPURLDesc *lud; - struct ldapconninfo *li; + (void)conn; /* Early URL syntax check. */ result = oldap_url_parse(data, &lud); ldap_free_urldesc(lud); - if(!result) { - li = calloc(1, sizeof(struct ldapconninfo)); - if(!li) - result = CURLE_OUT_OF_MEMORY; - else { - li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); - conn->proto.ldapc = li; - connkeep(conn, "OpenLDAP default"); - - /* Initialize the SASL storage */ - Curl_sasl_init(&li->sasl, data, &saslldap); - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - result = oldap_parse_login_options(conn); - } - } - return result; } @@ -537,7 +518,7 @@ static CURLcode oldap_perform_starttls(struct Curl_easy *data) static CURLcode oldap_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; + struct ldapconninfo *li; static const int version = LDAP_VERSION3; int rc; char *hosturl; @@ -547,6 +528,26 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) (void)done; + DEBUGASSERT(!conn->proto.ldapc); + li = calloc(1, sizeof(struct ldapconninfo)); + if(!li) + return CURLE_OUT_OF_MEMORY; + else { + CURLcode result; + li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); + conn->proto.ldapc = li; + + /* Initialize the SASL storage */ + Curl_sasl_init(&li->sasl, data, &saslldap); + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + + result = oldap_parse_login_options(conn); + if(result) + return result; + } + hosturl = aprintf("ldap%s://%s:%d", conn->handler->flags & PROTOPT_SSL? "s": "", conn->host.name, conn->remote_port); @@ -886,6 +887,11 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) result = oldap_url_parse(data, &lud); if(!result) { + Sockbuf *sb; + /* re-install the libcurl SSL handlers into the sockbuf. */ + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, lud->lud_filter, lud->lud_attrs, 0, NULL, NULL, NULL, 0, &msgid); @@ -947,18 +953,12 @@ static CURLcode client_write(struct Curl_easy *data, if(!len && plen && prefix[plen - 1] == ' ') plen--; result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); - if(!result) - data->req.bytecount += plen; } if(!result && value) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); - if(!result) - data->req.bytecount += len; } if(!result && suffix) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); - if(!result) - data->req.bytecount += slen; } return result; } diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index a9d5fdd6..3e0f20a 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -1088,7 +1088,7 @@ static CURLcode pop3_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct POP3 *pop3; - pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1); + pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3)); if(!pop3) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index e783a9c..e96cbf7 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -304,7 +304,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, * 'actual' is the time in milliseconds it took to actually download the * last 'size' bytes. */ - actual = Curl_timediff(now, start); + actual = Curl_timediff_ceil(now, start); if(actual < minimum) { /* if it downloaded the data faster than the limit, make it wait the difference */ @@ -319,12 +319,6 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, */ CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) { - if(data->set.max_filesize && (size > data->set.max_filesize)) { - failf(data, "Exceeded the maximum allowed file size " - "(%" CURL_FORMAT_CURL_OFF_T ")", - data->set.max_filesize); - return CURLE_FILESIZE_EXCEEDED; - } data->progress.downloaded = size; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 6bd9613..3383c49 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -32,10 +32,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_ARC4RANDOM -/* Some platforms might have the prototype missing (ubuntu + libressl) */ -uint32_t arc4random(void); -#endif #include <curl/curl.h> #include "urldata.h" @@ -50,7 +46,7 @@ uint32_t arc4random(void); #include "curl_memory.h" #include "memdebug.h" -#ifdef WIN32 +#ifdef _WIN32 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 # define HAVE_WIN_BCRYPTGENRANDOM @@ -105,7 +101,6 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length) static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) { - unsigned int r; CURLcode result = CURLE_OK; static unsigned int randseed; static bool seeded = FALSE; @@ -138,7 +133,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) /* ---- non-cryptographic version following ---- */ -#ifdef WIN32 +#ifdef _WIN32 if(!seeded) { result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd)); if(result != CURLE_NOT_BUILT_IN) @@ -146,12 +141,14 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) } #endif -#ifdef HAVE_ARC4RANDOM - *rnd = (unsigned int)arc4random(); - return CURLE_OK; +#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL) + if(!seeded) { + *rnd = (unsigned int)arc4random(); + return CURLE_OK; + } #endif -#if defined(RANDOM_FILE) && !defined(WIN32) +#if defined(RANDOM_FILE) && !defined(_WIN32) if(!seeded) { /* if there's a random file to read a seed from, use it */ int fd = open(RANDOM_FILE, O_RDONLY); @@ -175,9 +172,12 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) seeded = TRUE; } - /* Return an unsigned 32-bit pseudo-random number. */ - r = randseed = randseed * 1103515245 + 12345; - *rnd = (r << 16) | ((r >> 16) & 0xFFFF); + { + unsigned int r; + /* Return an unsigned 32-bit pseudo-random number. */ + r = randseed = randseed * 1103515245 + 12345; + *rnd = (r << 16) | ((r >> 16) & 0xFFFF); + } return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index 1d009f5..bc05239 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -41,7 +41,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); -#ifdef WIN32 +#ifdef _WIN32 /* Random generator shared between the Schannel vtls and Curl_rand*() functions */ CURLcode Curl_win32_random(unsigned char *entropy, size_t length); diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c index 97a66e9..4c88698 100644 --- a/Utilities/cmcurl/lib/rename.c +++ b/Utilities/cmcurl/lib/rename.c @@ -40,7 +40,7 @@ /* return 0 on success, 1 on error */ int Curl_rename(const char *oldpath, const char *newpath) { -#ifdef WIN32 +#ifdef _WIN32 /* rename() on Windows doesn't overwrite, so we can't use it here. MoveFileEx() will overwrite and is usually atomic, however it fails when there are open handles to the file. */ diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index ccd7264..e673bb8 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -45,8 +45,8 @@ #include "curl_memory.h" #include "memdebug.h" -#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ - ((int)((unsigned char)((p)[3])))) +#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \ + ((unsigned int)((unsigned char)((p)[3])))) /* protocol-specific functions set up to be called by the main engine */ static CURLcode rtsp_do(struct Curl_easy *data, bool *done); @@ -59,14 +59,19 @@ static int rtsp_getsock_do(struct Curl_easy *data, /* * Parse and write out any available RTP data. - * - * nread: amount of data left after k->str. will be modified if RTP - * data is parsed and k->str is moved up - * readmore: whether or not the RTP parser needs more data right away + * @param data the transfer + * @param conn the connection + * @param buf data read from connection + * @param blen amount of data in buf + * @param consumed out, number of blen consumed + * @param readmore out, TRUE iff complete buf was consumed and more data + * is needed */ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, + const char *buf, + size_t blen, + size_t *pconsumed, bool *readmore); static CURLcode rtsp_setup_connection(struct Curl_easy *data, @@ -88,7 +93,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, } static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len); +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len); static CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); @@ -585,153 +590,249 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) return result; } - -static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *readmore) { - struct SingleRequest *k = &data->req; +static CURLcode rtsp_filter_rtp(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + bool in_body, + size_t *pconsumed) +{ struct rtsp_conn *rtspc = &(conn->proto.rtspc); - unsigned char *rtp_channel_mask = data->state.rtp_channel_mask; + CURLcode result = CURLE_OK; - char *rtp; /* moving pointer to rtp data */ - ssize_t rtp_dataleft; /* how much data left to parse in this round */ - CURLcode result; - bool interleaved = false; - size_t skip_size = 0; + *pconsumed = 0; + while(blen) { + switch(rtspc->state) { - if(Curl_dyn_len(&rtspc->buf)) { - /* There was some leftover data the last time. Append new buffers */ - if(Curl_dyn_addn(&rtspc->buf, k->str, *nread)) - return CURLE_OUT_OF_MEMORY; - rtp = Curl_dyn_ptr(&rtspc->buf); - rtp_dataleft = Curl_dyn_len(&rtspc->buf); - } - else { - /* Just parse the request buffer directly */ - rtp = k->str; - rtp_dataleft = *nread; - } - - while(rtp_dataleft > 0) { - if(rtp[0] == '$') { - if(rtp_dataleft > 4) { - unsigned char rtp_channel; - int rtp_length; - int idx; - int off; - - /* Parse the header */ - /* The channel identifier immediately follows and is 1 byte */ - rtp_channel = (unsigned char)rtp[1]; - idx = rtp_channel / 8; - off = rtp_channel % 8; - if(!(rtp_channel_mask[idx] & (1 << off))) { - /* invalid channel number, maybe not an RTP packet */ - rtp++; - rtp_dataleft--; - skip_size++; - continue; + case RTP_PARSE_SKIP: { + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0); + if(in_body && buf[0] != '$') { + /* in BODY and no valid start, do not consume and return */ + goto out; + } + while(blen && buf[0] != '$') { + if(!in_body && buf[0] == 'R' && + data->set.rtspreq != RTSPREQ_RECEIVE) { + if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + /* This could be the next response, no consume and return */ + if(*pconsumed) { + DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " + "skipping %zd bytes of junk", *pconsumed)); + } + rtspc->state = RTP_PARSE_SKIP; + rtspc->in_header = TRUE; + goto out; + } } - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); + /* junk, consume without buffering */ + *pconsumed += 1; + ++buf; + --blen; + } + if(blen && buf[0] == '$') { + /* possible start of an RTP message, buffer */ + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - skip_size = 0; - rtspc->rtp_channel = rtp_channel; - - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_CHANNEL; + } + break; + } - if(rtp_dataleft < rtp_length + 4) { - /* Need more - incomplete payload */ - *readmore = TRUE; - break; + case RTP_PARSE_CHANNEL: { + int idx = ((unsigned char)buf[0]) / 8; + int off = ((unsigned char)buf[0]) % 8; + DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1); + if(!(data->state.rtp_channel_mask[idx] & (1 << off))) { + /* invalid channel number, junk or BODY data */ + rtspc->state = RTP_PARSE_SKIP; + if(in_body) { + /* we do not consume this byte, it is BODY data */ + DEBUGF(infof(data, "RTSP: invalid RTP channel %d in BODY, " + "treating as BODY data", idx)); + if(*pconsumed == 0) { + /* We did not consume the initial '$' in our buffer, but had + * it from an earlier call. We cannot un-consume it and have + * to write it directly as BODY data */ + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&rtspc->buf), 1); + Curl_dyn_free(&rtspc->buf); + if(result) + goto out; + } + else { + /* un-consume the '$' and leave */ + Curl_dyn_free(&rtspc->buf); + *pconsumed -= 1; + --buf; + ++blen; + goto out; + } } - interleaved = true; - /* We have the full RTP interleaved packet - * Write out the header including the leading '$' */ - DEBUGF(infof(data, "RTP write channel %d rtp_length %d", - rtspc->rtp_channel, rtp_length)); - result = rtp_client_write(data, &rtp[0], rtp_length + 4); - if(result) { - *readmore = FALSE; - return result; + else { + /* not BODY, forget the junk '$'. Do not consume this byte, + * it might be a start */ + infof(data, "RTSP: invalid RTP channel %d, skipping", idx); + Curl_dyn_free(&rtspc->buf); } + break; + } + /* a valid channel, so we expect this to be a real RTP message */ + rtspc->rtp_channel = (unsigned char)buf[0]; + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + rtspc->state = RTP_PARSE_LEN; + break; + } - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; + case RTP_PARSE_LEN: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + const char *rtp_buf; + DEBUGASSERT(rtp_len >= 2 && rtp_len < 4); + if(Curl_dyn_addn(&rtspc->buf, buf, 1)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + *pconsumed += 1; + ++buf; + --blen; + if(rtp_len == 2) + break; + rtp_buf = Curl_dyn_ptr(&rtspc->buf); + rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4; + rtspc->state = RTP_PARSE_DATA; + break; + } - if(data->set.rtspreq == RTSPREQ_RECEIVE) { - /* If we are in a passive receive, give control back - * to the app as often as we can. - */ - k->keepon &= ~KEEP_RECV; + case RTP_PARSE_DATA: { + size_t rtp_len = Curl_dyn_len(&rtspc->buf); + size_t needed; + DEBUGASSERT(rtp_len < rtspc->rtp_len); + needed = rtspc->rtp_len - rtp_len; + if(needed <= blen) { + if(Curl_dyn_addn(&rtspc->buf, buf, needed)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } + *pconsumed += needed; + buf += needed; + blen -= needed; + /* complete RTP message in buffer */ + DEBUGF(infof(data, "RTP write channel %d rtp_len %zu", + rtspc->rtp_channel, rtspc->rtp_len)); + result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf), + rtspc->rtp_len); + Curl_dyn_free(&rtspc->buf); + rtspc->state = RTP_PARSE_SKIP; + if(result) + goto out; } else { - /* Need more - incomplete header */ - *readmore = TRUE; - break; - } - } - else { - /* If the following data begins with 'RTSP/', which might be an RTSP - message, we should stop skipping the data. */ - /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the - middle of an RTSP message. It is difficult to determine this, so we - stop skipping. */ - size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5; - if((k->headerline > 0 && !interleaved) || - strncmp(rtp, "RTSP/", prefix_len) == 0) { - if(skip_size > 0) { - DEBUGF(infof(data, "Skip the malformed interleaved data %lu " - "bytes", skip_size)); + if(Curl_dyn_addn(&rtspc->buf, buf, blen)) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - break; /* maybe is an RTSP message */ + *pconsumed += blen; + buf += blen; + blen = 0; } - /* Skip incorrect data util the next RTP packet or RTSP message */ - do { - rtp++; - rtp_dataleft--; - skip_size++; - } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R'); + break; + } + + default: + DEBUGASSERT(0); + return CURLE_RECV_ERROR; } } +out: + return result; +} + +static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, + struct connectdata *conn, + const char *buf, + size_t blen, + size_t *pconsumed, + bool *readmore) +{ + struct rtsp_conn *rtspc = &(conn->proto.rtspc); + CURLcode result = CURLE_OK; + size_t consumed = 0; + bool in_body; - if(rtp_dataleft && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft, - *readmore ? "(READMORE)" : "")); + if(!data->req.header) + rtspc->in_header = FALSE; + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); - /* Store the incomplete RTP packet for a "rewind" */ - if(!Curl_dyn_len(&rtspc->buf)) { - /* nothing was stored, add this data */ - if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft)) - return CURLE_OUT_OF_MEMORY; - } - else { - /* keep the remainder */ - Curl_dyn_tail(&rtspc->buf, rtp_dataleft); - } + *readmore = FALSE; + *pconsumed = 0; + if(!blen) { + goto out; + } - /* As far as the transfer is concerned, this data is consumed */ - *nread = 0; - return CURLE_OK; + /* If header parsing is not onging, extract RTP messages */ + if(!rtspc->in_header) { + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + buf += consumed; + blen -= consumed; } - /* Fix up k->str to point just after the last RTP packet */ - k->str += *nread - rtp_dataleft; - *nread = rtp_dataleft; + /* we want to parse headers, do so */ + if(data->req.header && blen) { + rtspc->in_header = TRUE; + result = Curl_http_readwrite_headers(data, conn, buf, blen, + &consumed); + if(result) + goto out; + + *pconsumed += consumed; + buf += consumed; + blen -= consumed; + + if(!data->req.header) + rtspc->in_header = FALSE; + + if(!rtspc->in_header) { + /* If header parsing is done and data left, extract RTP messages */ + in_body = (data->req.headerline && !rtspc->in_header) && + (data->req.size >= 0) && + (data->req.bytecount < data->req.size); + result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed); + if(result) + goto out; + *pconsumed += consumed; + } + } - /* If we get here, we have finished with the leftover/merge buffer */ - Curl_dyn_free(&rtspc->buf); + if(rtspc->state != RTP_PARSE_SKIP) + *readmore = TRUE; - return CURLE_OK; +out: + if(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) { + /* In special mode RECEIVE, we just process one chunk of network + * data, so we stop the transfer here, if we have no incomplete + * RTP message pending. */ + data->req.keepon &= ~KEEP_RECV; + } + return result; } static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) +CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) { size_t wrote; curl_write_callback writeit; @@ -756,7 +857,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) } Curl_set_in_callback(data, true); - wrote = writeit(ptr, 1, len, user_ptr); + wrote = writeit((char *)ptr, 1, len, user_ptr); Curl_set_in_callback(data, false); if(CURL_WRITEFUNC_PAUSE == wrote) { diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 111bac2..237b80f 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -39,6 +39,12 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); #endif /* CURL_DISABLE_RTSP */ +typedef enum { + RTP_PARSE_SKIP, + RTP_PARSE_CHANNEL, + RTP_PARSE_LEN, + RTP_PARSE_DATA +} rtp_parse_st; /* * RTSP Connection data * @@ -47,6 +53,9 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); struct rtsp_conn { struct dynbuf buf; int rtp_channel; + size_t rtp_len; + rtp_parse_st state; + BIT(in_header); }; /**************************************************************************** diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index cae9beb..d92e745 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -76,7 +76,7 @@ int Curl_wait_ms(timediff_t timeout_ms) } #if defined(MSDOS) delay(timeout_ms); -#elif defined(WIN32) +#elif defined(_WIN32) /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ #if TIMEDIFF_T_MAX >= ULONG_MAX if(timeout_ms >= ULONG_MAX) diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 0482c5d..a2fac0c 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -50,6 +50,7 @@ #include "strdup.h" #include "http2.h" #include "headers.h" +#include "progress.h" #include "ws.h" /* The last 3 #include files should be in this order */ @@ -57,6 +58,9 @@ #include "curl_memory.h" #include "memdebug.h" + +static CURLcode do_init_stack(struct Curl_easy *data); + #if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP) /* * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF @@ -385,17 +389,17 @@ static CURLcode chop_write(struct Curl_easy *data, the future to leave the original data alone. */ CURLcode Curl_client_write(struct Curl_easy *data, - int type, - char *ptr, - size_t len) + int type, char *buf, size_t blen) { + CURLcode result; + #if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV) /* FTP data may need conversion. */ if((type & CLIENTWRITE_BODY) && (data->conn->handler->protocol & PROTO_FAMILY_FTP) && data->conn->proto.ftpc.transfertype == 'A') { /* convert end-of-line markers */ - len = convert_lineends(data, ptr, len); + blen = convert_lineends(data, buf, blen); } #endif /* it is one of those, at least */ @@ -405,14 +409,14 @@ CURLcode Curl_client_write(struct Curl_easy *data, /* INFO is only INFO */ DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO)); - if(type == CLIENTWRITE_BODY) { - if(data->req.ignorebody) - return CURLE_OK; - - if(data->req.writer_stack && !data->set.http_ce_skip) - return Curl_unencode_write(data, data->req.writer_stack, ptr, len); + if(!data->req.writer_stack) { + result = do_init_stack(data); + if(result) + return result; + DEBUGASSERT(data->req.writer_stack); } - return chop_write(data, type, FALSE, ptr, len); + + return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); } CURLcode Curl_client_unpause(struct Curl_easy *data) @@ -449,12 +453,12 @@ CURLcode Curl_client_unpause(struct Curl_easy *data) void Curl_client_cleanup(struct Curl_easy *data) { - struct contenc_writer *writer = data->req.writer_stack; + struct Curl_cwriter *writer = data->req.writer_stack; size_t i; while(writer) { - data->req.writer_stack = writer->downstream; - writer->handler->close_writer(data, writer); + data->req.writer_stack = writer->next; + writer->cwt->do_close(data, writer); free(writer); writer = data->req.writer_stack; } @@ -463,61 +467,222 @@ void Curl_client_cleanup(struct Curl_easy *data) Curl_dyn_free(&data->state.tempwrite[i].b); } data->state.tempcount = 0; + data->req.bytecount = 0; + data->req.headerline = 0; +} +/* Write data using an unencoding writer stack. "nbytes" is not + allowed to be 0. */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(!nbytes) + return CURLE_OK; + if(!writer) + return CURLE_WRITE_ERROR; + return writer->cwt->do_write(data, writer, type, buf, nbytes); } -/* Real client writer: no downstream. */ -static CURLcode client_cew_init(struct Curl_easy *data, - struct contenc_writer *writer) +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer) { - (void) data; + (void)data; (void)writer; return CURLE_OK; } -static CURLcode client_cew_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { - (void)writer; - if(!nbytes || data->req.ignorebody) - return CURLE_OK; - return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes); + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); } -static void client_cew_close(struct Curl_easy *data, - struct contenc_writer *writer) +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer) { (void) data; (void) writer; } -static const struct content_encoding client_cew = { +/* Real client writer to installed callbacks. */ +static CURLcode cw_client_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + (void)writer; + if(!nbytes) + return CURLE_OK; + return chop_write(data, type, FALSE, (char *)buf, nbytes); +} + +static const struct Curl_cwtype cw_client = { + "client", NULL, + Curl_cwriter_def_init, + cw_client_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + +static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) +{ + if(limit != -1) { + /* How much more are we allowed to write? */ + curl_off_t remain_diff; + remain_diff = limit - data->req.bytecount; + if(remain_diff < 0) { + /* already written too much! */ + return 0; + } +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + else if(remain_diff > SSIZE_T_MAX) { + return SIZE_T_MAX; + } +#endif + else { + return (size_t)remain_diff; + } + } + return SIZE_T_MAX; +} + +/* Download client writer in phase CURL_CW_PROTOCOL that + * sees the "real" download body data. */ +static CURLcode cw_download_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + CURLcode result; + size_t nwrite, excess_len = 0; + const char *excess_data = NULL; + + if(!(type & CLIENTWRITE_BODY)) { + if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers) + return CURLE_OK; + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + } + + nwrite = nbytes; + if(-1 != data->req.maxdownload) { + size_t wmax = get_max_body_write_len(data, data->req.maxdownload); + if(nwrite > wmax) { + excess_len = nbytes - wmax; + nwrite = wmax; + excess_data = buf + nwrite; + } + + if(nwrite == wmax) { + data->req.download_done = TRUE; + } + } + + if(data->set.max_filesize) { + size_t wmax = get_max_body_write_len(data, data->set.max_filesize); + if(nwrite > wmax) { + nwrite = wmax; + } + } + + data->req.bytecount += nwrite; + ++data->req.bodywrites; + if(!data->req.ignorebody && nwrite) { + result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); + if(result) + return result; + } + result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); + if(result) + return result; + + if(excess_len) { + if(data->conn->handler->readwrite) { + /* RTSP hack moved from transfer loop to here */ + bool readmore = FALSE; /* indicates data is incomplete, need more */ + size_t consumed = 0; + result = data->conn->handler->readwrite(data, data->conn, + excess_data, excess_len, + &consumed, &readmore); + if(result) + return result; + DEBUGASSERT(consumed <= excess_len); + excess_len -= consumed; + if(readmore) { + data->req.download_done = FALSE; + data->req.keepon |= KEEP_RECV; /* we're not done reading */ + } + } + if(excess_len && !data->req.ignorebody) { + infof(data, + "Excess found writing body:" + " excess = %zu" + ", size = %" CURL_FORMAT_CURL_OFF_T + ", maxdownload = %" CURL_FORMAT_CURL_OFF_T + ", bytecount = %" CURL_FORMAT_CURL_OFF_T, + excess_len, data->req.size, data->req.maxdownload, + data->req.bytecount); + connclose(data->conn, "excess found in a read"); + } + } + else if(nwrite < nbytes) { + failf(data, "Exceeded the maximum allowed file size " + "(%" CURL_FORMAT_CURL_OFF_T ") with %" + CURL_FORMAT_CURL_OFF_T " bytes", + data->set.max_filesize, data->req.bytecount); + return CURLE_FILESIZE_EXCEEDED; + } + + return CURLE_OK; +} + +static const struct Curl_cwtype cw_download = { + "download", NULL, - client_cew_init, - client_cew_write, - client_cew_close, - sizeof(struct contenc_writer) + Curl_cwriter_def_init, + cw_download_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) +}; + +/* RAW client writer in phase CURL_CW_RAW that + * enabled tracing of raw data. */ +static CURLcode cw_raw_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) +{ + if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) { + Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes); + } + return Curl_cwriter_write(data, writer->next, type, buf, nbytes); +} + +static const struct Curl_cwtype cw_raw = { + "raw", + NULL, + Curl_cwriter_def_init, + cw_raw_write, + Curl_cwriter_def_close, + sizeof(struct Curl_cwriter) }; /* Create an unencoding writer stage using the given handler. */ -CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, struct Curl_easy *data, - const struct content_encoding *ce_handler, - int order) + const struct Curl_cwtype *cwt, + Curl_cwriter_phase phase) { - struct contenc_writer *writer; + struct Curl_cwriter *writer; CURLcode result = CURLE_OUT_OF_MEMORY; - DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer)); - writer = (struct contenc_writer *) calloc(1, ce_handler->writersize); + DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); + writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size); if(!writer) goto out; - writer->handler = ce_handler; - writer->order = order; - result = ce_handler->init_writer(data, writer); + writer->cwt = cwt; + writer->phase = phase; + result = cwt->do_init(data, writer); out: *pwriter = result? NULL : writer; @@ -526,55 +691,74 @@ out: return result; } -void Curl_client_free_writer(struct Curl_easy *data, - struct contenc_writer *writer) +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer) { if(writer) { - writer->handler->close_writer(data, writer); + writer->cwt->do_close(data, writer); free(writer); } } -/* allow no more than 5 "chained" compression steps */ -#define MAX_ENCODE_STACK 5 +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) +{ + struct Curl_cwriter *w; + size_t n = 0; + for(w = data->req.writer_stack; w; w = w->next) { + if(w->phase == phase) + ++n; + } + return n; +} -static CURLcode init_writer_stack(struct Curl_easy *data) +static CURLcode do_init_stack(struct Curl_easy *data) { + struct Curl_cwriter *writer; + CURLcode result; + DEBUGASSERT(!data->req.writer_stack); - return Curl_client_create_writer(&data->req.writer_stack, - data, &client_cew, 0); + result = Curl_cwriter_create(&data->req.writer_stack, + data, &cw_client, CURL_CW_CLIENT); + if(result) + return result; + + result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + + result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW); + if(result) + return result; + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + } + return result; } -CURLcode Curl_client_add_writer(struct Curl_easy *data, - struct contenc_writer *writer) +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer) { CURLcode result; + struct Curl_cwriter **anchor = &data->req.writer_stack; - if(!data->req.writer_stack) { - result = init_writer_stack(data); + if(!*anchor) { + result = do_init_stack(data); if(result) return result; } - if(data->req.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. */ - if(writer->order >= data->req.writer_stack->order) { - writer->downstream = data->req.writer_stack; - data->req.writer_stack = writer; - } - else { - struct contenc_writer *w = data->req.writer_stack; - while(w->downstream && writer->order < w->downstream->order) - w = w->downstream; - writer->downstream = w->downstream; - w->downstream = writer; - } + /* Insert the writer as first in its phase. + * Skip existing writers of lower phases. */ + while(*anchor && (*anchor)->phase < writer->phase) + anchor = &((*anchor)->next); + writer->next = *anchor; + *anchor = writer; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 9ee00bb..a70189f 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -50,43 +50,122 @@ #define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */ #define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */ +/** + * Write `len` bytes at `prt` to the client. `type` indicates what + * kind of data is being written. + */ CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; +/** + * For a paused transfer, there might be buffered data held back. + * Attempt to flush this data to the client. This *may* trigger + * another pause of the transfer. + */ CURLcode Curl_client_unpause(struct Curl_easy *data); + +/** + * Free all resources related to client writing. + */ void Curl_client_cleanup(struct Curl_easy *data); -struct contenc_writer { - const struct content_encoding *handler; /* Encoding handler. */ - struct contenc_writer *downstream; /* Downstream writer. */ - unsigned int order; /* Ordering within writer stack. */ +/** + * Client Writers - a chain passing transfer BODY data to the client. + * Main application: HTTP and related protocols + * Other uses: monitoring of download progress + * + * Writers in the chain are order by their `phase`. First come all + * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE, + * followed by any in CURL_CW_PROTOCOL, etc. + * + * When adding a writer, it is inserted as first in its phase. This means + * the order of adding writers of the same phase matters, but writers for + * different phases may be added in any order. + * + * Writers which do modify the BODY data written are expected to be of + * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended + * for monitoring writers. Which do *not* modify the data but gather + * statistics or update progress reporting. + */ + +/* Phase a writer operates at. */ +typedef enum { + CURL_CW_RAW, /* raw data written, before any decoding */ + CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ + CURL_CW_PROTOCOL, /* after transfer, but before content decoding */ + CURL_CW_CONTENT_DECODE, /* remove content-encodings */ + CURL_CW_CLIENT /* data written to client */ +} Curl_cwriter_phase; + +/* Client Writer Type, provides the implementation */ +struct Curl_cwtype { + const char *name; /* writer name. */ + const char *alias; /* writer name alias, maybe NULL. */ + CURLcode (*do_init)(struct Curl_easy *data, + struct Curl_cwriter *writer); + CURLcode (*do_write)(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + void (*do_close)(struct Curl_easy *data, + struct Curl_cwriter *writer); + size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */ }; -/* Content encoding writer. */ -struct content_encoding { - const char *name; /* Encoding name. */ - const char *alias; /* Encoding name alias. */ - CURLcode (*init_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - CURLcode (*unencode_write)(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); - void (*close_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - size_t writersize; +/* Client writer instance */ +struct Curl_cwriter { + const struct Curl_cwtype *cwt; /* type implementation */ + struct Curl_cwriter *next; /* Downstream writer. */ + Curl_cwriter_phase phase; /* phase at which it operates */ }; +/** + * Create a new cwriter instance with given type and phase. Is not + * inserted into the writer chain by this call. + * Invokes `writer->do_init()`. + */ +CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, + struct Curl_easy *data, + const struct Curl_cwtype *ce_handler, + Curl_cwriter_phase phase); -CURLcode Curl_client_create_writer(struct contenc_writer **pwriter, - struct Curl_easy *data, - const struct content_encoding *ce_handler, - int order); +/** + * Free a cwriter instance. + * Invokes `writer->do_close()`. + */ +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer); -void Curl_client_free_writer(struct Curl_easy *data, - struct contenc_writer *writer); +/** + * Count the number of writers installed of the given phase. + */ +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase); -CURLcode Curl_client_add_writer(struct Curl_easy *data, - struct contenc_writer *writer); +/** + * Adds a writer to the transfer's writer chain. + * The writers `phase` determines where in the chain it is inserted. + */ +CURLcode Curl_cwriter_add(struct Curl_easy *data, + struct Curl_cwriter *writer); + +/** + * Convenience method for calling `writer->do_write()` that + * checks for NULL writer. + */ +CURLcode Curl_cwriter_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); + +/** + * Default implementations for do_init, do_write, do_close that + * do nothing and pass the data through. + */ +CURLcode Curl_cwriter_def_init(struct Curl_easy *data, + struct Curl_cwriter *writer); +CURLcode Curl_cwriter_def_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes); +void Curl_cwriter_def_close(struct Curl_easy *data, + struct Curl_cwriter *writer); /* internal read-function, does plain socket, SSL and krb4 */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 0d399ad..a08140c 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -50,6 +50,7 @@ #include "multiif.h" #include "altsvc.h" #include "hsts.h" +#include "tftp.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -171,7 +172,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) str = strchr(str, ','); tlen = str? (size_t) (str - token): strlen(token); if(tlen) { - const struct Curl_handler *h = Curl_builtin_scheme(token, tlen); + const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen); if(!h) return CURLE_UNSUPPORTED_PROTOCOL; @@ -261,43 +262,43 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set the absolute number of maximum simultaneous alive connection that * libcurl is allowed to have. */ - arg = va_arg(param, long); - if(arg < 0) + uarg = va_arg(param, unsigned long); + if(uarg > UINT_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxconnects = arg; + data->set.maxconnects = (unsigned int)uarg; break; case CURLOPT_FORBID_REUSE: /* * When this transfer is done, it must not be left to be reused by a * subsequent transfer but shall be closed immediately. */ - data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.reuse_forbid = (0 != va_arg(param, long)); break; case CURLOPT_FRESH_CONNECT: /* * This transfer shall not use a previously cached connection but * should be made with a fresh new connect! */ - data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.reuse_fresh = (0 != va_arg(param, long)); break; case CURLOPT_VERBOSE: /* * Verbose means infof() calls that give a lot of information about * the connection and transfer procedures as well as internal choices. */ - data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.verbose = (0 != va_arg(param, long)); break; case CURLOPT_HEADER: /* * Set to include the header in the general data output stream. */ - data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.include_header = (0 != va_arg(param, long)); break; case CURLOPT_NOPROGRESS: /* * Shut off the internal supported progress meter */ - data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.hide_progress = (0 != va_arg(param, long)); if(data->set.hide_progress) data->progress.flags |= PGRS_HIDE; else @@ -307,7 +308,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Do not include the body part in the output data stream. */ - data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.opt_no_body = (0 != va_arg(param, long)); #ifndef CURL_DISABLE_HTTP if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ @@ -321,11 +322,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Don't output the >=400 error code HTML-page, but instead only * return error. */ - data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_fail_on_error = (0 != va_arg(param, long)); break; case CURLOPT_KEEP_SENDING_ON_ERROR: - data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.http_keep_sending_on_error = (0 != va_arg(param, long)); break; case CURLOPT_UPLOAD: case CURLOPT_PUT: @@ -353,7 +353,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Try to get the file time of the remote document. The time will * later (possibly) become available using curl_easy_getinfo(). */ - data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.get_filetime = (0 != va_arg(param, long)); break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: /* @@ -379,7 +379,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * TFTP option that specifies the block size to use for data transmission. */ arg = va_arg(param, long); - if(arg < 0) + if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.tftp_blksize = arg; break; @@ -409,7 +409,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * * Transfer using ASCII (instead of BINARY). */ - data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.prefer_ascii = (0 != va_arg(param, long)); break; case CURLOPT_TIMECONDITION: /* @@ -577,7 +577,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Switch on automatic referer that gets set if curl follows locations. */ - data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_auto_referer = (0 != va_arg(param, long)); break; case CURLOPT_ACCEPT_ENCODING: @@ -592,28 +592,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ argptr = va_arg(param, char *); if(argptr && !*argptr) { - argptr = Curl_all_content_encodings(); - if(!argptr) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); - free(argptr); - } + char all[256]; + Curl_all_content_encodings(all, sizeof(all)); + result = Curl_setstropt(&data->set.str[STRING_ENCODING], all); } else result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); break; case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.http_transfer_encoding = (0 != va_arg(param, long)); break; case CURLOPT_FOLLOWLOCATION: /* * Follow Location: header hints on an HTTP-server. */ - data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.http_follow_location = (0 != va_arg(param, long)); break; case CURLOPT_UNRESTRICTED_AUTH: @@ -621,8 +616,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Send authentication (user+password) when following locations, even when * hostname changed. */ - data->set.allow_auth_to_other_hosts = - (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long)); break; case CURLOPT_MAXREDIRS: @@ -736,7 +730,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set header option. */ arg = va_arg(param, long); - data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE); + data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE); break; #if !defined(CURL_DISABLE_COOKIES) @@ -760,18 +754,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->set.cookielist, argptr); + cl = curl_slist_append(data->state.cookielist, argptr); if(!cl) { - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; return CURLE_OUT_OF_MEMORY; } - data->set.cookielist = cl; /* store the list for later use */ + data->state.cookielist = cl; /* store the list for later use */ } else { /* clear the list of cookie files */ - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; if(!data->share || !data->share->cookies) { /* throw away all existing cookies if this isn't a shared cookie @@ -811,17 +805,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * prevent the forthcoming read-cookies-from-file actions to accept * cookies that are marked as being session cookies, as they belong to a * previous session. - * - * In the original Netscape cookie spec, "session cookies" are cookies - * with no expire date set. RFC2109 describes the same action if no - * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds - * a 'Discard' action that can enforce the discard even for cookies that - * have a Max-Age. - * - * We run mostly with the original cookie spec, as hardly anyone implements - * anything else. */ - data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.cookiesession = (0 != va_arg(param, long)); break; case CURLOPT_COOKIELIST: @@ -956,7 +941,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(arg) return CURLE_BAD_FUNCTION_ARGUMENT; #else - data->set.http09_allowed = arg ? TRUE : FALSE; + data->set.http09_allowed = !!arg; #endif break; @@ -997,8 +982,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_MIME_OPTIONS: - data->set.mime_options = (unsigned int)va_arg(param, long); - break; + arg = va_arg(param, long); + data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); + break; # endif #endif @@ -1018,8 +1004,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* the DIGEST_IE bit is only used to set a special marker, for all the rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); + data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE); if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ @@ -1072,8 +1057,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Tunnel operations through the proxy instead of normal proxy use */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)); break; case CURLOPT_PROXYPORT: @@ -1102,8 +1086,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* the DIGEST_IE bit is only used to set a special marker, for all the rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); + data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE); if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ @@ -1203,7 +1186,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Set flag for NEC SOCK5 support */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.socks5_gssapi_nec = (0 != va_arg(param, long)); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1251,7 +1234,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * An option that changes the command to one that asks for a list only, no * file info details. Used for FTP, POP3 and SFTP. */ - data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.list_only = (0 != va_arg(param, long)); break; #endif case CURLOPT_APPEND: @@ -1259,7 +1242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * We want to upload and append to an existing file. Used for FTP and * SFTP. */ - data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.remote_append = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_FTP @@ -1270,7 +1253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg; + data->set.ftp_filemethod = (unsigned char)arg; break; case CURLOPT_FTPPORT: /* @@ -1278,26 +1261,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ result = Curl_setstropt(&data->set.str[STRING_FTPPORT], va_arg(param, char *)); - data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE; + data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]); break; case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_eprt = (0 != va_arg(param, long)); break; case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_epsv = (0 != va_arg(param, long)); break; case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_use_pret = (0 != va_arg(param, long)); break; case CURLOPT_FTP_SSL_CCC: arg = va_arg(param, long); if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg; + data->set.ftp_ccc = (unsigned char)arg; break; case CURLOPT_FTP_SKIP_PASV_IP: @@ -1305,7 +1288,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the * bypass of the IP address in PASV responses. */ - data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ftp_skip_ip = (0 != va_arg(param, long)); break; case CURLOPT_FTP_ACCOUNT: @@ -1333,7 +1316,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], va_arg(param, char *)); - data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; + data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); break; #endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) @@ -1867,14 +1850,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Kludgy option to enable CRLF conversions. Subject for removal. */ - data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.crlf = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_PROXY case CURLOPT_HAPROXYPROTOCOL: /* * Set to send the HAProxy Proxy Protocol header */ - data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.haproxyprotocol = (0 != va_arg(param, long)); break; case CURLOPT_HAPROXY_CLIENT_IP: /* @@ -1926,22 +1909,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Enable peer SSL verifying. */ - data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifypeer = - data->set.ssl.primary.verifypeer; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYPEER: /* * Enable peer SSL verifying for DoH. */ - data->set.doh_verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.doh_verifypeer = (0 != va_arg(param, long)); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1953,10 +1931,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) (0 != va_arg(param, long))?TRUE:FALSE; /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifypeer = - data->set.proxy_ssl.primary.verifypeer; - } + Curl_ssl_conn_config_update(data, TRUE); break; #endif case CURLOPT_SSL_VERIFYHOST: @@ -1968,13 +1943,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Obviously people are not reading documentation and too many thought this argument took a boolean when it wasn't and misused it. Treat 1 and 2 the same */ - data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE); + data->set.ssl.primary.verifyhost = !!(arg & 3); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifyhost = - data->set.ssl.primary.verifyhost; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYHOST: @@ -1984,7 +1956,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); /* Treat both 1 and 2 as TRUE */ - data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE); + data->set.doh_verifyhost = !!(arg & 3); break; #endif #ifndef CURL_DISABLE_PROXY @@ -1996,12 +1968,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Treat both 1 and 2 as TRUE */ data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); - /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifyhost = - data->set.proxy_ssl.primary.verifyhost; - } + Curl_ssl_conn_config_update(data, TRUE); break; #endif case CURLOPT_SSL_VERIFYSTATUS: @@ -2013,14 +1981,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)); /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifystatus = - data->set.ssl.primary.verifystatus; - } + Curl_ssl_conn_config_update(data, FALSE); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYSTATUS: @@ -2032,8 +1996,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.doh_verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.doh_verifystatus = (0 != va_arg(param, long)); break; #endif case CURLOPT_SSL_CTX_FUNCTION: @@ -2067,12 +2030,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; } - data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl.falsestart = (0 != va_arg(param, long)); break; case CURLOPT_CERTINFO: #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) - data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl.certinfo = (0 != va_arg(param, long)); else #endif result = CURLE_NOT_BUILT_IN; @@ -2118,14 +2081,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], va_arg(param, struct curl_blob *)); + break; + } else #endif return CURLE_NOT_BUILT_IN; - - break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_CAINFO: /* @@ -2141,13 +2104,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], va_arg(param, struct curl_blob *)); + break; + } else #endif return CURLE_NOT_BUILT_IN; - break; #endif case CURLOPT_CAPATH: /* @@ -2278,7 +2242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * The application asks not to set any signal() or alarm() handlers, * even when using a timeout. */ - data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.no_signal = (0 != va_arg(param, long)); break; case CURLOPT_SHARE: @@ -2453,11 +2417,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Enable or disable TCP_NODELAY, which will disable/enable the Nagle * algorithm */ - data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.tcp_nodelay = (0 != va_arg(param, long)); break; case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ignorecl = (0 != va_arg(param, long)); break; case CURLOPT_CONNECT_ONLY: @@ -2532,8 +2496,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? - TRUE : FALSE; + data->set.ssl.primary.sessionid = (0 != va_arg(param, long)); #ifndef CURL_DISABLE_PROXY data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; #endif @@ -2622,7 +2585,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * disable libcurl transfer encoding is used */ #ifndef USE_HYPER - data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + data->set.http_te_skip = (0 == va_arg(param, long)); break; #else return CURLE_NOT_BUILT_IN; /* hyper doesn't support */ @@ -2632,7 +2595,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * raw data passed to the application when content encoding is used */ - data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + data->set.http_ce_skip = (0 == va_arg(param, long)); break; #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) @@ -2733,7 +2696,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_MAIL_RCPT_ALLOWFAILS: /* allow RCPT TO command to fail for some recipients */ - data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)); break; #endif @@ -2745,7 +2708,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SASL_IR: /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.sasl_ir = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_REQUEST: @@ -2859,7 +2822,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif #ifndef CURL_DISABLE_FTP case CURLOPT_WILDCARDMATCH: - data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.wildcard_enabled = (0 != va_arg(param, long)); break; case CURLOPT_CHUNK_BGN_FUNCTION: data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); @@ -2942,7 +2905,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #endif case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.tcp_keepalive = (0 != va_arg(param, long)); break; case CURLOPT_TCP_KEEPIDLE: arg = va_arg(param, long); @@ -2971,7 +2934,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SSL_ENABLE_NPN: break; case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.ssl_enable_alpn = (0 != va_arg(param, long)); break; #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: @@ -2987,10 +2950,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_PATH_AS_IS: - data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.path_as_is = (0 != va_arg(param, long)); break; case CURLOPT_PIPEWAIT: - data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.pipewait = (0 != va_arg(param, long)); break; case CURLOPT_STREAM_WEIGHT: #if defined(USE_HTTP2) || defined(USE_HTTP3) @@ -3025,12 +2988,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #ifndef CURL_DISABLE_SHUFFLE_DNS case CURLOPT_DNS_SHUFFLE_ADDRESSES: - data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE; + data->set.dns_shuffle_addresses = (0 != va_arg(param, long)); break; #endif case CURLOPT_DISALLOW_USERNAME_IN_URL: - data->set.disallow_username_in_url = - (0 != va_arg(param, long)) ? TRUE : FALSE; + data->set.disallow_username_in_url = (0 != va_arg(param, long)); break; #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_URL: @@ -3095,18 +3057,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* 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); + h = curl_slist_append(data->state.hstslist, argptr); if(!h) { - curl_slist_free_all(data->set.hstslist); - data->set.hstslist = NULL; + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; return CURLE_OUT_OF_MEMORY; } - data->set.hstslist = h; /* store the list for later use */ + data->state.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; + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; if(!data->share || !data->share->hsts) /* throw away the HSTS cache unless shared */ Curl_hsts_cleanup(&data->hsts); diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index 1394838..4e034d4 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -53,14 +53,14 @@ # ifndef NOGDI # define NOGDI # endif -# include <winerror.h> -# include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # ifdef HAVE_WS2TCPIP_H # include <ws2tcpip.h> # endif # endif +# include <windows.h> +# include <winerror.h> # include <tchar.h> # ifdef UNICODE typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); @@ -96,18 +96,12 @@ #ifndef _WIN32_WINNT_WS03 #define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ #endif -#ifndef _WIN32_WINNT_WIN6 -#define _WIN32_WINNT_WIN6 0x0600 /* Windows Vista */ -#endif #ifndef _WIN32_WINNT_VISTA #define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ #endif #ifndef _WIN32_WINNT_WS08 #define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */ #endif -#ifndef _WIN32_WINNT_LONGHORN -#define _WIN32_WINNT_LONGHORN 0x0600 /* Windows Vista */ -#endif #ifndef _WIN32_WINNT_WIN7 #define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */ #endif @@ -117,9 +111,6 @@ #ifndef _WIN32_WINNT_WINBLUE #define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */ #endif -#ifndef _WIN32_WINNT_WINTHRESHOLD -#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 /* Windows 10 */ -#endif #ifndef _WIN32_WINNT_WIN10 #define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */ #endif diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h index 7f55aac..632d919 100644 --- a/Utilities/cmcurl/lib/share.h +++ b/Utilities/cmcurl/lib/share.h @@ -31,14 +31,6 @@ #include "urldata.h" #include "conncache.h" -/* SalfordC says "A structure member may not be volatile". Hence: - */ -#ifdef __SALFORDC__ -#define CURL_VOLATILE -#else -#define CURL_VOLATILE volatile -#endif - #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) @@ -46,7 +38,7 @@ struct Curl_share { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; - CURL_VOLATILE unsigned int dirty; + volatile unsigned int dirty; curl_lock_function lockfunc; curl_unlock_function unlockfunc; diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 32c5137..6c8a47c 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -27,7 +27,7 @@ #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) -#ifdef WIN32 +#ifdef _WIN32 #define getpid GetCurrentProcessId #endif @@ -1047,14 +1047,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) break; } } - data->req.bytecount += len; data->req.offset += len; - result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - if(result) { - req->result = result; - next_state = SMB_CLOSE; - break; - } next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; break; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 81a17e3..65fbc5b 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -1320,7 +1320,7 @@ static CURLcode smtp_init(struct Curl_easy *data) CURLcode result = CURLE_OK; struct SMTP *smtp; - smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1); + smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP)); if(!smtp) result = CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index 963e140..e3d40ff 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c @@ -28,7 +28,7 @@ #include "rand.h" #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) -#ifdef WIN32 +#ifdef _WIN32 /* * This is a socketpair() implementation for Windows. */ @@ -50,7 +50,7 @@ #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif /* !INADDR_LOOPBACK */ -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #include "nonblock.h" /* for curlx_nonblock */ #include "timeval.h" /* needed before select.h */ @@ -87,7 +87,7 @@ int Curl_socketpair(int domain, int type, int protocol, socks[0] = socks[1] = CURL_SOCKET_BAD; -#if defined(WIN32) || defined(__CYGWIN__) +#if defined(_WIN32) || defined(__CYGWIN__) /* don't set SO_REUSEADDR on Windows */ (void)reuse; #ifdef SO_EXCLUSIVEADDRUSE diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index 306ab5d..bd499ab 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h @@ -25,6 +25,23 @@ ***************************************************************************/ #include "curl_setup.h" + +#ifdef HAVE_PIPE + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close +#define wakeup_create pipe + +#else /* HAVE_PIPE */ + +#define wakeup_write swrite +#define wakeup_read sread +#define wakeup_close sclose +#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) + +#endif /* HAVE_PIPE */ + #ifndef HAVE_SOCKETPAIR #include <curl/curl.h> diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index a7b5ab0..3a396de 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -339,8 +339,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif infof(data, "Hostname '%s' was found", sx->hostname); sxstate(sx, data, CONNECT_RESOLVED); @@ -402,8 +402,11 @@ CONNECT_REQ_INIT: socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ if(sx->proxy_user) { size_t plen = strlen(sx->proxy_user); - if(plen >= (size_t)data->set.buffer_size - 8) { - failf(data, "Too long SOCKS proxy user name, can't use"); + if(plen > 255) { + /* there is no real size limit to this field in the protocol, but + SOCKS5 limits the proxy user field to 255 bytes and it seems likely + that a longer field is either a mistake or malicious input */ + failf(data, "Too long SOCKS proxy user name"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ @@ -426,7 +429,8 @@ CONNECT_REQ_INIT: socksreq[7] = 1; /* append hostname */ hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ - if(hostnamelen <= 255) + if((hostnamelen <= 255) && + (packetsize + hostnamelen < data->set.buffer_size)) strcpy((char *)socksreq + packetsize, sx->hostname); else { failf(data, "SOCKS4: too long host name"); @@ -802,8 +806,8 @@ CONNECT_REQ_INIT: if(dns) { #ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; + conn->resolve_async.dns = dns; + conn->resolve_async.done = TRUE; #endif infof(data, "SOCKS5: hostname '%s' found", sx->hostname); } @@ -819,10 +823,19 @@ CONNECT_REQ_INIT: /* FALLTHROUGH */ CONNECT_RESOLVED: case CONNECT_RESOLVED: { - char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */ + char dest[MAX_IPADR_LEN]; /* printable address */ struct Curl_addrinfo *hp = NULL; if(dns) hp = dns->addr; +#ifdef ENABLE_IPV6 + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { + int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? + AF_INET : AF_INET6; + /* scan for the first proper address */ + while(hp && (hp->ai_family != wanted_family)) + hp = hp->ai_next; + } +#endif if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); @@ -1119,7 +1132,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; if(!sx) { - sx = calloc(sizeof(*sx), 1); + sx = calloc(1, sizeof(*sx)); if(!sx) return CURLE_OUT_OF_MEMORY; cf->ctx = sx; @@ -1157,32 +1170,29 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -static int socks_cf_get_select_socks(struct Curl_cfilter *cf, +static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct socks_state *sx = cf->ctx; - int fds; - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - if(!fds && cf->next->connected && !cf->connected && sx) { + if(!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] = Curl_conn_cf_get_socket(cf, data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); switch(sx->state) { case CONNECT_RESOLVING: case CONNECT_SOCKS_READ: case CONNECT_AUTH_READ: case CONNECT_REQ_READ: case CONNECT_REQ_READ_MORE: - fds = GETSOCK_READSOCK(0); + Curl_pollset_set_in_only(data, ps, sock); break; default: - fds = GETSOCK_WRITESOCK(0); + Curl_pollset_set_out_only(data, ps, sock); break; } } - return fds; } static void socks_proxy_cf_close(struct Curl_cfilter *cf, @@ -1227,7 +1237,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { socks_proxy_cf_connect, socks_proxy_cf_close, socks_cf_get_host, - socks_cf_get_select_socks, + socks_cf_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c index 07a6139..2578441 100644 --- a/Utilities/cmcurl/lib/strdup.c +++ b/Utilities/cmcurl/lib/strdup.c @@ -26,7 +26,7 @@ #include <curl/curl.h> -#ifdef WIN32 +#ifdef _WIN32 #include <wchar.h> #endif @@ -56,7 +56,7 @@ char *Curl_strdup(const char *str) } #endif -#ifdef WIN32 +#ifdef _WIN32 /*************************************************************************** * * Curl_wcsdup(source) @@ -101,6 +101,30 @@ void *Curl_memdup(const void *src, size_t length) /*************************************************************************** * + * Curl_strndup(source, length) + * + * Copies the 'source' string to a newly allocated buffer (that is returned). + * Copies not more than 'length' bytes (up to a null terminator) then adds a + * null terminator. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +void *Curl_strndup(const char *src, size_t length) +{ + char *buf = memchr(src, '\0', length); + if(buf) + length = buf - src; + buf = malloc(length + 1); + if(!buf) + return NULL; + memcpy(buf, src, length); + buf[length] = 0; + return buf; +} + +/*************************************************************************** + * * Curl_saferealloc(ptr, size) * * Does a normal realloc(), but will free the data pointer if the realloc diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h index c3430b5..9f12b25 100644 --- a/Utilities/cmcurl/lib/strdup.h +++ b/Utilities/cmcurl/lib/strdup.h @@ -28,10 +28,11 @@ #ifndef HAVE_STRDUP char *Curl_strdup(const char *str); #endif -#ifdef WIN32 +#ifdef _WIN32 wchar_t* Curl_wcsdup(const wchar_t* src); #endif void *Curl_memdup(const void *src, size_t buffer_length); void *Curl_saferealloc(void *ptr, size_t size); +void *Curl_strndup(const char *src, size_t length); #endif /* HEADER_CURL_STRDUP_H */ diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index be41914..0d5f927 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -48,7 +48,7 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) #define PRESERVE_WINDOWS_ERROR_CODE #endif @@ -762,7 +762,7 @@ get_winsock_error (int err, char *buf, size_t len) } #endif /* USE_WINSOCK */ -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) /* This is a helper function for Curl_strerror that converts Windows API error * codes (GetLastError) to error messages. * Returns NULL if no error message was found for error code. @@ -804,7 +804,7 @@ get_winapi_error(int err, char *buf, size_t buflen) return (*buf ? buf : NULL); } -#endif /* WIN32 || _WIN32_WCE */ +#endif /* _WIN32 || _WIN32_WCE */ /* * Our thread-safe and smart strerror() replacement. @@ -837,15 +837,15 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) if(!buflen) return NULL; -#ifndef WIN32 +#ifndef _WIN32 DEBUGASSERT(err >= 0); #endif max = buflen - 1; *buf = '\0'; -#if defined(WIN32) || defined(_WIN32_WCE) -#if defined(WIN32) +#if defined(_WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) /* 'sys_nerr' is the maximum errno number, it is not widely portable */ if(err >= 0 && err < sys_nerr) strncpy(buf, sys_errlist[err], max); @@ -923,7 +923,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) * Curl_winapi_strerror: * Variant of Curl_strerror if the error code is definitely Windows API. */ -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) { #ifdef PRESERVE_WINDOWS_ERROR_CODE @@ -958,7 +958,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) return buf; } -#endif /* WIN32 || _WIN32_WCE */ +#endif /* _WIN32 || _WIN32_WCE */ #ifdef USE_WINDOWS_SSPI /* @@ -986,6 +986,10 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) break; #define SEC2TXT(sec) case sec: txt = #sec; break SEC2TXT(CRYPT_E_REVOKED); + SEC2TXT(CRYPT_E_NO_REVOCATION_DLL); + SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK); + SEC2TXT(CRYPT_E_REVOCATION_OFFLINE); + SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE); SEC2TXT(SEC_E_ALGORITHM_MISMATCH); SEC2TXT(SEC_E_BAD_BINDINGS); SEC2TXT(SEC_E_BAD_PKGID); diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h index 399712f..6806867 100644 --- a/Utilities/cmcurl/lib/strerror.h +++ b/Utilities/cmcurl/lib/strerror.h @@ -29,7 +29,7 @@ #define STRERROR_LEN 256 /* a suitable length */ const char *Curl_strerror(int err, char *buf, size_t buflen); -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(_WIN32_WCE) const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen); #endif #ifdef USE_WINDOWS_SSPI diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index 0cdaf3b..9408d02 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include <curl/curl.h> #include "system_win32.h" @@ -238,4 +238,4 @@ HMODULE Curl_load_library(LPCTSTR filename) #endif } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index 6482643..2566766 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) extern LARGE_INTEGER Curl_freq; extern bool Curl_isVistaOrGreater; @@ -42,8 +42,8 @@ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); -#else /* WIN32 */ +#else /* _WIN32 */ #define Curl_win32_init(x) CURLE_OK -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index e78140d..6630155 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -70,8 +70,6 @@ /* RFC2348 allows the block size to be negotiated */ #define TFTP_BLKSIZE_DEFAULT 512 -#define TFTP_BLKSIZE_MIN 8 -#define TFTP_BLKSIZE_MAX 65464 #define TFTP_OPTION_BLKSIZE "blksize" /* from RFC2349: */ @@ -978,11 +976,9 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* alloc pkt buffers based on specified blksize */ - if(data->set.tftp_blksize) { + if(data->set.tftp_blksize) + /* range checked when set */ blksize = (int)data->set.tftp_blksize; - if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN) - return CURLE_TFTP_ILLEGAL; - } need_blksize = blksize; /* default size is the fallback when no OACK is received */ @@ -1107,7 +1103,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct tftp_state_data *state = conn->proto.tftpc; - struct SingleRequest *k = &data->req; /* Receive the packet */ fromlen = sizeof(fromaddr); @@ -1141,11 +1136,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)state->rpacket.data + 4, state->rbytes-4); - if(!result) { - k->bytecount += state->rbytes-4; - result = Curl_pgrsSetDownloadCounter(data, - (curl_off_t) k->bytecount); - } if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); return result; diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 5d2d5da..12404bf 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h @@ -25,6 +25,9 @@ ***************************************************************************/ #ifndef CURL_DISABLE_TFTP extern const struct Curl_handler Curl_handler_tftp; + +#define TFTP_BLKSIZE_MIN 8 +#define TFTP_BLKSIZE_MAX 65464 #endif #endif /* HEADER_CURL_TFTP_H */ diff --git a/Utilities/cmcurl/lib/timediff.c b/Utilities/cmcurl/lib/timediff.c index 1b762bb..d0824d1 100644 --- a/Utilities/cmcurl/lib/timediff.c +++ b/Utilities/cmcurl/lib/timediff.c @@ -53,7 +53,7 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) #endif tv->tv_sec = (time_t)tv_sec; tv->tv_usec = (suseconds_t)tv_usec; -#elif defined(WIN32) /* maybe also others in the future */ +#elif defined(_WIN32) /* maybe also others in the future */ #if TIMEDIFF_T_MAX > LONG_MAX /* tv_sec overflow check on Windows there we know it is long */ if(tv_sec > LONG_MAX) diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index 026d9d1..5a6727c 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -24,11 +24,10 @@ #include "timeval.h" -#if defined(WIN32) && !defined(MSDOS) +#if defined(_WIN32) -/* set in win32_init() */ -extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; +#include <curl/curl.h> +#include "system_win32.h" /* In case of bug fix this function has a counterpart in tool_util.c */ struct curltime Curl_now(void) diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 6886764..96f1fde 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -163,9 +163,9 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, { size_t buffersize = bytes; size_t nread; - curl_read_callback readfunc = NULL; void *extra_data = NULL; + int eof_index = 0; #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_INITIALIZED) { @@ -223,6 +223,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, */ readfunc = trailers_read; extra_data = (void *)data; + eof_index = 1; } else #endif @@ -231,10 +232,15 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, extra_data = data->state.in; } - Curl_set_in_callback(data, true); - nread = readfunc(data->req.upload_fromhere, 1, - buffersize, extra_data); - Curl_set_in_callback(data, false); + if(!data->req.fread_eof[eof_index]) { + Curl_set_in_callback(data, true); + nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data); + Curl_set_in_callback(data, false); + /* make sure the callback is not called again after EOF */ + data->req.fread_eof[eof_index] = !nread; + } + else + nread = 0; if(nread == CURL_READFUNC_ABORT) { failf(data, "operation aborted by callback"); @@ -422,16 +428,15 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool *comeback) { CURLcode result = CURLE_OK; - ssize_t nread; /* number of bytes read */ - size_t excess = 0; /* excess bytes read */ - bool readmore = FALSE; /* used by RTP to signal for more data */ + char *buf; + size_t blen; + size_t consumed; int maxloops = 100; curl_off_t max_recv = data->set.max_recv_speed? data->set.max_recv_speed : CURL_OFF_T_MAX; - char *buf = data->state.buffer; bool data_eof_handled = FALSE; - DEBUGASSERT(buf); + DEBUGASSERT(data->state.buffer); *done = FALSE; *comeback = FALSE; @@ -439,8 +444,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, read or we get a CURLE_AGAIN */ do { bool is_empty_data = FALSE; - size_t buffersize = data->set.buffer_size; - size_t bytestoread = buffersize; + size_t bytestoread = data->set.buffer_size; /* 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 @@ -449,31 +453,38 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET); - if(!data_eof_handled && k->size != -1 && !k->header) { - /* make sure we don't read too much */ + /* Each loop iteration starts with a fresh buffer and handles + * all data read into it. */ + buf = data->state.buffer; + blen = 0; + + /* If we are reading BODY data and the connection does NOT handle EOF + * and we know the size of the BODY data, limit the read amount */ + if(!k->header && !data_eof_handled && k->size != -1) { curl_off_t totalleft = k->size - k->bytecount; - if(totalleft < (curl_off_t)bytestoread) + if(totalleft <= 0) + bytestoread = 0; + else if(totalleft < (curl_off_t)bytestoread) bytestoread = (size_t)totalleft; } if(bytestoread) { /* receive data from the network! */ + ssize_t nread; /* number of bytes read */ result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread); - - /* read would've blocked */ if(CURLE_AGAIN == result) { result = CURLE_OK; break; /* get out of loop */ } - - if(result>0) + else if(result) goto out; + DEBUGASSERT(nread >= 0); + blen = (size_t)nread; } 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")); - nread = 0; } if(!k->bytecount) { @@ -485,12 +496,17 @@ static CURLcode readwrite_data(struct Curl_easy *data, *didwhat |= KEEP_RECV; /* indicates data of zero size, i.e. empty file */ - is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; - - if(0 < nread || is_empty_data) { - buf[nread] = 0; + is_empty_data = ((blen == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; + + if(0 < blen || is_empty_data) { + /* data->state.buffer is allocated 1 byte larger than + * data->set.buffer_size admits. *wink* */ + /* TODO: we should really not rely on this being 0-terminated, since + * the actual data read might contain 0s. */ + buf[blen] = 0; } - if(!nread) { + + if(!blen) { /* if we receive 0 or less here, either the data transfer is done or the server closed the connection and we bail out from this! */ if(data_eof_handled) @@ -502,48 +518,70 @@ static CURLcode readwrite_data(struct Curl_easy *data, break; } - /* Default buffer to use when we write the buffer, it may be changed - in the flow below before the actual storing is done. */ - k->str = buf; - if(conn->handler->readwrite) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); + bool readmore = FALSE; /* indicates data is incomplete, need more */ + consumed = 0; + result = conn->handler->readwrite(data, conn, buf, blen, + &consumed, &readmore); if(result) goto out; if(readmore) break; + buf += consumed; + blen -= consumed; + if(k->download_done) { + /* We've stopped dealing with input, get out of the do-while loop */ + if(blen > 0) { + infof(data, + "Excess found:" + " excess = %zu" + " url = %s (zero-length body)", + blen, data->state.up.path); + } + + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_RECV; + break; + } } #ifndef CURL_DISABLE_HTTP /* Since this is a two-state thing, we check if we are parsing headers at the moment or not. */ if(k->header) { - /* we are in parse-the-header-mode */ - bool stop_reading = FALSE; - result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading); + consumed = 0; + result = Curl_http_readwrite_headers(data, conn, buf, blen, &consumed); if(result) goto out; + buf += consumed; + blen -= consumed; if(conn->handler->readwrite && - (k->maxdownload <= 0 && nread > 0)) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); + (k->maxdownload <= 0 && blen > 0)) { + bool readmore = FALSE; /* indicates data is incomplete, need more */ + consumed = 0; + result = conn->handler->readwrite(data, conn, buf, blen, + &consumed, &readmore); if(result) goto out; if(readmore) break; + buf += consumed; + blen -= consumed; } - if(stop_reading) { + if(k->download_done) { /* We've stopped dealing with input, get out of the do-while loop */ - - if(nread > 0) { + if(blen > 0) { infof(data, "Excess found:" - " excess = %zd" + " excess = %zu" " url = %s (zero-length body)", - nread, data->state.up.path); + blen, data->state.up.path); } + /* we make sure that this socket isn't read more now */ + k->keepon &= ~KEEP_RECV; break; } } @@ -553,11 +591,13 @@ static CURLcode readwrite_data(struct Curl_easy *data, /* This is not an 'else if' since it may be a rest from the header parsing, where the beginning of the buffer is headers and the end is non-headers. */ - if(!k->header && (nread > 0 || is_empty_data)) { + if(!k->header && (blen > 0 || is_empty_data)) { - if(data->req.no_body) { + if(data->req.no_body && blen > 0) { /* data arrives although we want none, bail out */ streamclose(conn, "ignoring body"); + DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes", + blen)); *done = TRUE; result = CURLE_WEIRD_SERVER_REPLY; goto out; @@ -576,34 +616,18 @@ static CURLcode readwrite_data(struct Curl_easy *data, } /* this is the first time we write a body part */ #endif /* CURL_DISABLE_HTTP */ - k->bodywrites++; - - /* pass data to the debug function before it gets "dechunked" */ - if(data->set.verbose) { - if(k->badheader) { - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(k->badheader == HEADER_PARTHEADER) - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - else - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - #ifndef CURL_DISABLE_HTTP if(k->chunk) { /* * Here comes a chunked transfer flying and we need to decode this * properly. While the name says read, this function both reads - * and writes away the data. The returned 'nread' holds the number - * of actual data it wrote to the client. + * and writes away the data. */ CURLcode extra; - CHUNKcode res = - Curl_httpchunk_read(data, k->str, nread, &nread, &extra); + CHUNKcode res; + + consumed = 0; + res = Curl_httpchunk_read(data, buf, blen, &consumed, &extra); if(CHUNKE_OK < res) { if(CHUNKE_PASSTHRU_ERROR == res) { @@ -615,9 +639,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, result = CURLE_RECV_ERROR; goto out; } - if(CHUNKE_STOP == res) { + + buf += consumed; + blen -= consumed; + if(CHUNKE_STOP == res) { /* we're done reading chunks! */ k->keepon &= ~KEEP_RECV; /* read no more */ + /* chunks read successfully, download is complete */ + k->download_done = TRUE; /* N number of bytes at the end of the str buffer that weren't written to the client. */ @@ -631,117 +660,57 @@ static CURLcode readwrite_data(struct Curl_easy *data, } #endif /* CURL_DISABLE_HTTP */ - /* Account for body content stored in the header buffer */ - if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) { - size_t headlen = Curl_dyn_len(&data->state.headerb); - DEBUGF(infof(data, "Increasing bytecount by %zu", headlen)); - k->bytecount += headlen; - } - - if((-1 != k->maxdownload) && - (k->bytecount + nread >= k->maxdownload)) { + max_recv -= blen; - excess = (size_t)(k->bytecount + nread - k->maxdownload); - if(excess > 0 && !k->ignorebody) { - infof(data, - "Excess found in a read:" - " excess = %zu" - ", size = %" CURL_FORMAT_CURL_OFF_T - ", maxdownload = %" CURL_FORMAT_CURL_OFF_T - ", bytecount = %" CURL_FORMAT_CURL_OFF_T, - excess, k->size, k->maxdownload, k->bytecount); - connclose(conn, "excess found in a read"); - } - - nread = (ssize_t) (k->maxdownload - k->bytecount); - if(nread < 0) /* this should be unusual */ - nread = 0; - - /* HTTP/3 over QUIC should keep reading until QUIC connection - is closed. In contrast to HTTP/2 which can stop reading - from TCP connection, HTTP/3 over QUIC needs ACK from server - to ensure stream closure. It should keep reading. */ - if(!is_http3) { - k->keepon &= ~KEEP_RECV; /* we're done reading */ - } - } - - k->bytecount += nread; - max_recv -= nread; - - result = Curl_pgrsSetDownloadCounter(data, k->bytecount); - if(result) - goto out; - - if(!k->chunk && (nread || k->badheader || is_empty_data)) { + if(!k->chunk && (blen || k->badheader || is_empty_data)) { /* If this is chunky transfer, it was already written */ - if(k->badheader && !k->ignorebody) { + if(k->badheader) { /* we parsed a piece of data wrongly assuming it was a header and now we output it as body instead */ size_t headlen = Curl_dyn_len(&data->state.headerb); /* Don't let excess data pollute body writes */ - if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload) - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - headlen); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - (size_t)k->maxdownload); + if(k->maxdownload != -1 && (curl_off_t)headlen > k->maxdownload) + headlen = (size_t)k->maxdownload; + result = Curl_client_write(data, CLIENTWRITE_BODY, + Curl_dyn_ptr(&data->state.headerb), + headlen); if(result) goto out; } - if(k->badheader < HEADER_ALLBAD) { - /* This switch handles various content encodings. If there's an - error here, be sure to check over the almost identical code - in http_chunks.c. - Make sure that ALL_CONTENT_ENCODINGS contains all the - encodings handled here. */ - if(!k->ignorebody && nread) { + + if(blen) { #ifndef CURL_DISABLE_POP3 - if(conn->handler->protocol & PROTO_FAMILY_POP3) - result = Curl_pop3_write(data, k->str, nread); - else -#endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, - nread); + if(conn->handler->protocol & PROTO_FAMILY_POP3) { + result = k->ignorebody? CURLE_OK : + Curl_pop3_write(data, buf, blen); } + else +#endif /* CURL_DISABLE_POP3 */ + result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen); } - k->badheader = HEADER_NORMAL; /* taken care of now */ + k->badheader = FALSE; /* taken care of now */ if(result) goto out; } - } /* if(!header and data to read) */ - - if(conn->handler->readwrite && excess) { - /* Parse the excess data */ - k->str += nread; - - if(&k->str[excess] > &buf[data->set.buffer_size]) { - /* the excess amount was too excessive(!), make sure - it doesn't read out of buffer */ - excess = &buf[data->set.buffer_size] - k->str; + if(k->download_done && !is_http3) { + /* HTTP/3 over QUIC should keep reading until QUIC connection + is closed. In contrast to HTTP/2 which can stop reading + from TCP connection, HTTP/3 over QUIC needs ACK from server + to ensure stream closure. It should keep reading. */ + k->keepon &= ~KEEP_RECV; /* we're done reading */ } - nread = (ssize_t)excess; - - result = conn->handler->readwrite(data, conn, &nread, &readmore); - if(result) - goto out; - - if(readmore) - k->keepon |= KEEP_RECV; /* we're not done reading */ - break; - } + } /* if(!header and data to read) */ if(is_empty_data) { /* if we received nothing, the server closed the connection and we are done */ k->keepon &= ~KEEP_RECV; + k->download_done = TRUE; } if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) { @@ -764,6 +733,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, on from our side, we need to stop that immediately. */ infof(data, "we are done reading and this is set to close, stop send"); k->keepon &= ~KEEP_SEND; /* no writing anymore either */ + k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */ } out: @@ -783,7 +753,7 @@ CURLcode Curl_done_sending(struct Curl_easy *data, return CURLE_OK; } -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif @@ -977,7 +947,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, if(result) return result; -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) { struct curltime n = Curl_now(); if(Curl_timediff(n, k->last_sndbuf_update) > 1000) { @@ -1430,8 +1400,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } wc = data->wildcard; - if((wc->state < CURLWC_INIT) || - (wc->state >= CURLWC_CLEAN)) { + if(wc->state < CURLWC_INIT) { if(wc->ftpwc) wc->dtor(wc->ftpwc); Curl_safefree(wc->pattern); @@ -1635,7 +1604,7 @@ CURLcode Curl_follow(struct Curl_easy *data, return Curl_uc_to_curlcode(uc); } - p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); + p = Curl_get_scheme_handler(scheme); if(p && (p->protocol != data->info.conn_protocol)) { infof(data, "Clear auth, redirects scheme from %s to %s", data->info.conn_scheme, scheme); diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 61dad44..b81785f 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -168,130 +168,6 @@ static curl_prot_t get_protocol_family(const struct Curl_handler *h) return h->family; } - -/* - * Protocol table. Schemes (roughly) in 2019 popularity order: - * - * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP, - * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT - */ -static const struct Curl_handler * const protocols[] = { - -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_https, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#endif - -#ifdef USE_WEBSOCKETS -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_wss, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_ws, -#endif -#endif - -#ifndef CURL_DISABLE_FTP - &Curl_handler_ftp, -#endif - -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - &Curl_handler_ftps, -#endif - -#if defined(USE_SSH) - &Curl_handler_sftp, -#endif - -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#endif - -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - &Curl_handler_scp, -#endif - -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#ifdef USE_SSL - &Curl_handler_smtps, -#endif -#endif - -#ifndef CURL_DISABLE_LDAP - &Curl_handler_ldap, -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - &Curl_handler_ldaps, -#endif -#endif - -#ifndef CURL_DISABLE_IMAP - &Curl_handler_imap, -#ifdef USE_SSL - &Curl_handler_imaps, -#endif -#endif - -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, -#endif - -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#endif - -#ifndef CURL_DISABLE_POP3 - &Curl_handler_pop3, -#ifdef USE_SSL - &Curl_handler_pop3s, -#endif -#endif - -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smb, -#ifdef USE_SSL - &Curl_handler_smbs, -#endif -#endif - -#ifndef CURL_DISABLE_RTSP - &Curl_handler_rtsp, -#endif - -#ifndef CURL_DISABLE_MQTT - &Curl_handler_mqtt, -#endif - -#ifndef CURL_DISABLE_GOPHER - &Curl_handler_gopher, -#ifdef USE_SSL - &Curl_handler_gophers, -#endif -#endif - -#ifdef USE_LIBRTMP - &Curl_handler_rtmp, - &Curl_handler_rtmpt, - &Curl_handler_rtmpe, - &Curl_handler_rtmpte, - &Curl_handler_rtmps, - &Curl_handler_rtmpts, -#endif - -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, -#endif - - (struct Curl_handler *) NULL -}; - void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ @@ -320,8 +196,8 @@ void Curl_freeset(struct Curl_easy *data) Curl_mime_cleanpart(&data->set.mimepost); #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(data->set.cookielist); - data->set.cookielist = NULL; + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; #endif } @@ -363,16 +239,18 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Detach connection if any is left. This should not be normal, but can be the case for example with CONNECT_ONLY + recv/send (test 556) */ Curl_detach_connection(data); - if(data->multi) - /* This handle is still part of a multi handle, take care of this first - and detach this handle from there. */ - curl_multi_remove_handle(data->multi, data); + if(!data->state.internal) { + if(data->multi) + /* This handle is still part of a multi handle, take care of this first + and detach this handle from there. */ + curl_multi_remove_handle(data->multi, data); - if(data->multi_easy) { - /* when curl_easy_perform() is used, it creates its own multi handle to - use and this is the one */ - curl_multi_cleanup(data->multi_easy); - data->multi_easy = NULL; + if(data->multi_easy) { + /* when curl_easy_perform() is used, it creates its own multi handle to + use and this is the one */ + curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } } data->magic = 0; /* force a clear AFTER the possibly enforced removal from @@ -412,7 +290,7 @@ CURLcode Curl_close(struct Curl_easy **datap) #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 */ + curl_slist_free_all(data->state.hstslist); /* clean up list */ #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); @@ -420,10 +298,6 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->info.contenttype); Curl_safefree(data->info.wouldredirect); - /* this destroys the channel and we cannot use it anymore after this */ - Curl_resolver_cancel(data); - Curl_resolver_cleanup(data->state.async.resolver); - data_priority_cleanup(data); /* No longer a dirty share, if it exists */ @@ -457,8 +331,8 @@ CURLcode Curl_close(struct Curl_easy **datap) } #endif - Curl_mime_cleanpart(data->state.formp); #ifndef CURL_DISABLE_HTTP + Curl_mime_cleanpart(data->state.formp); Curl_safefree(data->state.formp); #endif @@ -530,26 +404,16 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) Curl_mime_initpart(&set->mimepost); - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ + Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; #endif - set->ssl.primary.verifypeer = TRUE; - set->ssl.primary.verifyhost = TRUE; #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 - set->proxy_ssl = set->ssl; -#endif set->new_file_perms = 0644; /* Default permissions */ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; @@ -650,13 +514,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->magic = CURLEASY_MAGIC_NUMBER; - result = Curl_resolver_init(data, &data->state.async.resolver); - if(result) { - DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); - free(data); - return result; - } - result = Curl_init_userdefined(data); if(!result) { Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); @@ -673,7 +530,6 @@ CURLcode Curl_open(struct Curl_easy **curl) } if(result) { - Curl_resolver_cleanup(data->state.async.resolver); Curl_dyn_free(&data->state.headerb); Curl_freeset(data); free(data); @@ -707,6 +563,7 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_conn_cf_discard_all(data, conn, (int)i); } + Curl_resolver_cleanup(conn->resolve_async.resolver); Curl_free_idnconverted_hostname(&conn->host); Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY @@ -718,7 +575,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->socks_proxy.passwd); Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); #endif Curl_safefree(conn->user); Curl_safefree(conn->passwd); @@ -733,7 +589,7 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); Curl_safefree(conn->localdev); - Curl_free_primary_ssl_config(&conn->ssl_config); + Curl_ssl_conn_config_cleanup(conn); #ifdef USE_UNIX_SOCKETS Curl_safefree(conn->unix_domain_socket); @@ -807,6 +663,7 @@ void Curl_disconnect(struct Curl_easy *data, conn->handler->disconnect(data, conn, dead_connection); conn_shutdown(data); + Curl_resolver_cancel(data); /* detach it again */ Curl_detach_connection(data); @@ -1059,11 +916,11 @@ ConnectionExists(struct Curl_easy *data, bool *force_reuse, bool *waitpipe) { - struct connectdata *check; - struct connectdata *chosen = 0; + struct connectdata *chosen = NULL; bool foundPendingCandidate = FALSE; - bool canmultiplex = IsMultiplexingPossible(data, needle); + bool canmultiplex = FALSE; struct connectbundle *bundle; + struct Curl_llist_element *curr; #ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & @@ -1082,395 +939,368 @@ ConnectionExists(struct Curl_easy *data, bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && (needle->handler->protocol & CURLPROTO_HTTP); + *usethis = NULL; *force_reuse = FALSE; *waitpipe = FALSE; /* Look up the bundle with all the connections to this particular host. Locks the connection cache, beware of early returns! */ bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); - if(bundle) { - /* Max pipe length is zero (unlimited) for multiplexed connections */ - struct Curl_llist_element *curr; - - infof(data, "Found bundle for host: %p [%s]", - (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); - - /* We can't multiplex if we don't know anything about the server */ - if(canmultiplex) { - if(bundle->multiuse == BUNDLE_UNKNOWN) { - if(data->set.pipewait) { - infof(data, "Server doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - - infof(data, "Server doesn't support multiplex (yet)"); - canmultiplex = FALSE; + if(!bundle) { + CONNCACHE_UNLOCK(data); + return FALSE; + } + infof(data, "Found bundle for host: %p [%s]", + (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); + + /* We can only multiplex iff the transfer allows it AND we know + * that the server we want to talk to supports it as well. */ + canmultiplex = FALSE; + if(IsMultiplexingPossible(data, needle)) { + if(bundle->multiuse == BUNDLE_UNKNOWN) { + if(data->set.pipewait) { + infof(data, "Server doesn't support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ } - if((bundle->multiuse == BUNDLE_MULTIPLEX) && - !Curl_multiplex_wanted(data->multi)) { + infof(data, "Server doesn't support multiplex (yet)"); + } + else if(bundle->multiuse == BUNDLE_MULTIPLEX) { + if(Curl_multiplex_wanted(data->multi)) + canmultiplex = TRUE; + else infof(data, "Could multiplex, but not asked to"); - canmultiplex = FALSE; - } - if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { - infof(data, "Can not multiplex, even if we wanted to"); - canmultiplex = FALSE; - } } + else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { + infof(data, "Can not multiplex, even if we wanted to"); + } + } - curr = bundle->conn_list.head; - while(curr) { - bool match = FALSE; - size_t multiplexed = 0; + curr = bundle->conn_list.head; + while(curr) { + struct connectdata *check = curr->ptr; + /* Get next node now. We might remove a dead `check` connection which + * would invalidate `curr` as well. */ + curr = curr->next; - /* - * Note that if we use an HTTP proxy in normal mode (no tunneling), we - * check connections to that proxy and not to the actual remote server. - */ - check = curr->ptr; - curr = curr->next; + /* Note that if we use an HTTP proxy in normal mode (no tunneling), we + * check connections to that proxy and not to the actual remote server. + */ + if(check->connect_only || check->bits.close) + /* connect-only or to-be-closed connections will not be reused */ + continue; - if(check->connect_only || check->bits.close) - /* connect-only or to-be-closed connections will not be reused */ - continue; + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER + && data->set.ipver != check->ip_version) { + /* skip because the connection is not via the requested IP version */ + continue; + } - if(extract_if_dead(check, data)) { - /* disconnect it */ - Curl_disconnect(data, check, TRUE); + if(!canmultiplex) { + if(Curl_resolver_asynch() && + /* primary_ip[0] is NUL only if the resolving of the name hasn't + completed yet and until then we don't reuse this connection */ + !check->primary_ip[0]) continue; - } + } - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { - /* skip because the connection is not via the requested IP version */ + if(CONN_INUSE(check)) { + if(!canmultiplex) { + /* transfer can't be multiplexed and check is in use */ continue; } - - if(bundle->multiuse == BUNDLE_MULTIPLEX) - multiplexed = CONN_INUSE(check); - - if(!canmultiplex) { - if(multiplexed) { - /* can only happen within multi handles, and means that another easy - handle is using this connection */ - continue; - } - - if(Curl_resolver_asynch() && - /* primary_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't reuse this connection */ - !check->primary_ip[0]) + else { + /* Could multiplex, but not when check belongs to another multi */ + struct Curl_llist_element *e = check->easyq.head; + struct Curl_easy *entry = e->ptr; + if(entry->multi != data->multi) continue; } + } - if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T - " isn't open enough, can't reuse", check->connection_id); - continue; - } + if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ + infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T + " isn't open enough, can't reuse", check->connection_id); + continue; + } + + /* `check` is connected. if it is in use and does not support multiplex, + * we cannot use it. */ + if(!check->bits.multiplex && CONN_INUSE(check)) + continue; #ifdef USE_UNIX_SOCKETS - if(needle->unix_domain_socket) { - if(!check->unix_domain_socket) - continue; - if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) - continue; - if(needle->bits.abstract_unix_socket != - check->bits.abstract_unix_socket) - continue; - } - else if(check->unix_domain_socket) + if(needle->unix_domain_socket) { + if(!check->unix_domain_socket) continue; -#endif - - if((needle->handler->flags&PROTOPT_SSL) != - (check->handler->flags&PROTOPT_SSL)) - /* don't do mixed SSL and non-SSL connections */ - if(get_protocol_family(check->handler) != - needle->handler->protocol || !check->bits.tls_upgraded) - /* except protocols that have been upgraded via TLS */ - continue; - -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy != check->bits.httpproxy || - needle->bits.socksproxy != check->bits.socksproxy) + if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) continue; - - if(needle->bits.socksproxy && - !socks_proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) + if(needle->bits.abstract_unix_socket != + check->bits.abstract_unix_socket) continue; + } + else if(check->unix_domain_socket) + continue; #endif - if(needle->bits.conn_to_host != check->bits.conn_to_host) - /* don't mix connections that use the "connect to host" feature and - * connections that don't use this feature */ - continue; - if(needle->bits.conn_to_port != check->bits.conn_to_port) - /* don't mix connections that use the "connect to port" feature and - * connections that don't use this feature */ + if((needle->handler->flags&PROTOPT_SSL) != + (check->handler->flags&PROTOPT_SSL)) + /* don't do mixed SSL and non-SSL connections */ + if(get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded) + /* except protocols that have been upgraded via TLS */ continue; -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) - continue; + if(needle->bits.conn_to_host != check->bits.conn_to_host) + /* don't mix connections that use the "connect to host" feature and + * connections that don't use this feature */ + continue; - if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) - continue; + if(needle->bits.conn_to_port != check->bits.conn_to_port) + /* don't mix connections that use the "connect to port" feature and + * connections that don't use this feature */ + continue; - if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { - /* use https proxy */ - if(needle->http_proxy.proxytype != - check->http_proxy.proxytype) - continue; - else if(needle->handler->flags&PROTOPT_SSL) { - /* use double layer ssl */ - if(!Curl_ssl_config_matches(&needle->proxy_ssl_config, - &check->proxy_ssl_config)) - continue; - } - else if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) - continue; - } - } -#endif +#ifndef CURL_DISABLE_PROXY + if(needle->bits.httpproxy != check->bits.httpproxy || + needle->bits.socksproxy != check->bits.socksproxy) + continue; - if(h2upgrade && !check->httpversion && canmultiplex) { - if(data->set.pipewait) { - infof(data, "Server upgrade doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - infof(data, "Server upgrade cannot be used"); - continue; /* can't be used atm */ - } + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) + continue; - if(!canmultiplex && CONN_INUSE(check)) - /* this request can't be multiplexed but the checked connection is - already in use so we skip it */ + if(needle->bits.httpproxy) { + if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) continue; - if(CONN_INUSE(check)) { - /* Subject for multiplex use if 'checks' belongs to the same multi - handle as 'data' is. */ - struct Curl_llist_element *e = check->easyq.head; - struct Curl_easy *entry = e->ptr; - if(entry->multi != data->multi) - continue; - } + if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) + continue; - if(needle->localdev || needle->localport) { - /* If we are bound to a specific local end (IP+port), we must not - reuse a random other one, although if we didn't ask for a - particular one we can reuse one that was bound. - - This comparison is a bit rough and too strict. Since the input - parameters can be specified in numerous ways and still end up the - same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that reuses of bound connections - will most likely also reuse the exact same binding parameters and - missing out a few edge cases shouldn't hurt anyone very much. - */ - if((check->localport != needle->localport) || - (check->localportrange != needle->localportrange) || - (needle->localdev && - (!check->localdev || strcmp(check->localdev, needle->localdev)))) + if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + if(needle->http_proxy.proxytype != check->http_proxy.proxytype) continue; - } - - if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we're using the same name and password as well */ - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd) || - Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || - Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { - /* one of them was different */ + /* match SSL config to proxy */ + if(!Curl_ssl_conn_config_match(data, check, TRUE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL proxy parameters, can't reuse", + check->connection_id)); continue; } + /* the SSL config to the server, which may apply here is checked + * further below */ } + } +#endif - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ - if(needle->gssapi_delegation != check->gssapi_delegation) + if(h2upgrade && !check->httpversion && canmultiplex) { + if(data->set.pipewait) { + infof(data, "Server upgrade doesn't support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ + } + infof(data, "Server upgrade cannot be used"); + continue; /* can't be used atm */ + } + + if(needle->localdev || needle->localport) { + /* If we are bound to a specific local end (IP+port), we must not + reuse a random other one, although if we didn't ask for a + particular one we can reuse one that was bound. + + This comparison is a bit rough and too strict. Since the input + parameters can be specified in numerous ways and still end up the + same it would take a lot of processing to make it really accurate. + Instead, this matching will assume that reuses of bound connections + will most likely also reuse the exact same binding parameters and + missing out a few edge cases shouldn't hurt anyone very much. + */ + if((check->localport != needle->localport) || + (check->localportrange != needle->localportrange) || + (needle->localdev && + (!check->localdev || strcmp(check->localdev, needle->localdev)))) continue; + } - /* If 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 >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) + if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + /* This protocol requires credentials per connection, + so verify that we're using the same name and password as well */ + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd) || + Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || + Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { + /* one of them was different */ continue; -#ifdef USE_SSH - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { - if(!ssh_config_matches(needle, check)) - continue; } + } + + /* GSS delegation differences do not actually affect every connection + and auth method, but this check takes precaution before efficiency */ + if(needle->gssapi_delegation != check->gssapi_delegation) + continue; + + /* If looking for HTTP and the HTTP version we want is less + * than the HTTP version of the check connection, continue looking */ + if((needle->handler->protocol & PROTO_FAMILY_HTTP) && + (((check->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + || ((check->httpversion >= 30) && + (data->state.httpwant < CURL_HTTP_VERSION_3)))) + continue; +#ifdef USE_SSH + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { + if(!ssh_config_matches(needle, check)) + continue; + } #endif #ifndef CURL_DISABLE_FTP - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ - if(Curl_timestrcmp(needle->proto.ftpc.account, - check->proto.ftpc.account) || - Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, - check->proto.ftpc.alternative_to_user) || - (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || - (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) - continue; - } + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { + /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + if(Curl_timestrcmp(needle->proto.ftpc.account, + check->proto.ftpc.account) || + Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, + check->proto.ftpc.alternative_to_user) || + (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || + (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) + continue; + } #endif - if((needle->handler->flags&PROTOPT_SSL) + /* Additional match requirements if talking TLS OR + * not talking to a HTTP proxy OR using a tunnel through a proxy */ + if((needle->handler->flags&PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY - || !needle->bits.httpproxy || needle->bits.tunnel_proxy -#endif - ) { - /* The requested connection does not use an HTTP proxy or it uses SSL - or it is a non-SSL protocol tunneled or it is a non-SSL protocol - which is allowed to be upgraded via TLS */ - - if((strcasecompare(needle->handler->scheme, check->handler->scheme) || - (get_protocol_family(check->handler) == - needle->handler->protocol && check->bits.tls_upgraded)) && - (!needle->bits.conn_to_host || strcasecompare( - needle->conn_to_host.name, check->conn_to_host.name)) && - (!needle->bits.conn_to_port || - needle->conn_to_port == check->conn_to_port) && - strcasecompare(needle->host.name, check->host.name) && - needle->remote_port == check->remote_port) { - /* The schemes match or the protocol family is the same and the - previous connection was TLS upgraded, and the hostname and host - port match */ - if(needle->handler->flags & PROTOPT_SSL) { - /* This is a SSL connection so verify that we're using the same - SSL options as well */ - if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) { - DEBUGF(infof(data, - "Connection #%" CURL_FORMAT_CURL_OFF_T - " has different SSL parameters, can't reuse", - check->connection_id)); - continue; - } - } - match = TRUE; - } - } - else { - /* The requested connection is using the same HTTP proxy in normal - mode (no tunneling) */ - match = TRUE; + || !needle->bits.httpproxy || needle->bits.tunnel_proxy +#endif + ) { + /* Talking the same protocol scheme or a TLS upgraded protocol in the + * same protocol family? */ + if(!strcasecompare(needle->handler->scheme, check->handler->scheme) && + (get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded)) + continue; + + /* If needle has "conn_to_*" set, check must match this */ + if((needle->bits.conn_to_host && !strcasecompare( + needle->conn_to_host.name, check->conn_to_host.name)) || + (needle->bits.conn_to_port && + needle->conn_to_port != check->conn_to_port)) + continue; + + /* hostname and port must match */ + if(!strcasecompare(needle->host.name, check->host.name) || + needle->remote_port != check->remote_port) + continue; + + /* If talking TLS, check needs to use the same SSL options. */ + if((needle->handler->flags & PROTOPT_SSL) && + !Curl_ssl_conn_config_match(data, check, FALSE)) { + DEBUGF(infof(data, + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL parameters, can't reuse", + check->connection_id)); + continue; } + } - if(match) { #if defined(USE_NTLM) - /* If we are looking for an HTTP+NTLM connection, check if this is - already authenticating with the right credentials. If not, keep - looking so that we can reuse NTLM connections if - possible. (Especially we must not reuse the same connection if - partway through a handshake!) */ - if(wantNTLMhttp) { - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd)) { - - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(check->http_ntlm_state == NTLMSTATE_NONE) - chosen = check; - continue; - } - } - else if(check->http_ntlm_state != NTLMSTATE_NONE) { - /* Connection is using NTLM auth but we don't want NTLM */ - continue; - } - -#ifndef CURL_DISABLE_PROXY - /* Same for Proxy NTLM authentication */ - if(wantProxyNTLMhttp) { - /* Both check->http_proxy.user and check->http_proxy.passwd can be - * NULL */ - if(!check->http_proxy.user || !check->http_proxy.passwd) - continue; - - if(Curl_timestrcmp(needle->http_proxy.user, - check->http_proxy.user) || - Curl_timestrcmp(needle->http_proxy.passwd, - check->http_proxy.passwd)) - continue; - } - else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { - /* Proxy connection is using NTLM auth but we don't want NTLM */ - continue; - } -#endif - if(wantNTLMhttp || wantProxyNTLMhttp) { - /* Credentials are already checked, we can use this connection */ + /* If we are looking for an HTTP+NTLM connection, check if this is + already authenticating with the right credentials. If not, keep + looking so that we can reuse NTLM connections if + possible. (Especially we must not reuse the same connection if + partway through a handshake!) */ + if(wantNTLMhttp) { + if(Curl_timestrcmp(needle->user, check->user) || + Curl_timestrcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(check->http_ntlm_state == NTLMSTATE_NONE) chosen = check; + continue; + } + } + else if(check->http_ntlm_state != NTLMSTATE_NONE) { + /* Connection is using NTLM auth but we don't want NTLM */ + continue; + } - if((wantNTLMhttp && - (check->http_ntlm_state != NTLMSTATE_NONE)) || - (wantProxyNTLMhttp && - (check->proxy_ntlm_state != NTLMSTATE_NONE))) { - /* We must use this connection, no other */ - *force_reuse = TRUE; - break; - } +#ifndef CURL_DISABLE_PROXY + /* Same for Proxy NTLM authentication */ + if(wantProxyNTLMhttp) { + /* Both check->http_proxy.user and check->http_proxy.passwd can be + * NULL */ + if(!check->http_proxy.user || !check->http_proxy.passwd) + continue; - /* Continue look up for a better connection */ - continue; - } + if(Curl_timestrcmp(needle->http_proxy.user, + check->http_proxy.user) || + Curl_timestrcmp(needle->http_proxy.passwd, + check->http_proxy.passwd)) + continue; + } + else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { + /* Proxy connection is using NTLM auth but we don't want NTLM */ + continue; + } +#endif + if(wantNTLMhttp || wantProxyNTLMhttp) { + /* Credentials are already checked, we may use this connection. + * With NTLM being weird as it is, we MUST use a + * connection where it has already been fully negotiated. + * If it has not, we keep on looking for a better one. */ + chosen = check; + + if((wantNTLMhttp && + (check->http_ntlm_state != NTLMSTATE_NONE)) || + (wantProxyNTLMhttp && + (check->proxy_ntlm_state != NTLMSTATE_NONE))) { + /* We must use this connection, no other */ + *force_reuse = TRUE; + break; + } + /* Continue look up for a better connection */ + continue; + } #endif - if(canmultiplex) { - /* We can multiplex if we want to. Let's continue looking for - the optimal connection to use. */ - - if(!multiplexed) { - /* We have the optimal connection. Let's stop looking. */ - chosen = check; - break; - } -#ifdef USE_NGHTTP2 - /* If multiplexed, make sure we don't go over concurrency limit */ - if(check->bits.multiplex) { - if(multiplexed >= Curl_conn_get_max_concurrent(data, check, - FIRSTSOCKET)) { - infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", - multiplexed); - continue; - } - else if(multiplexed >= - Curl_multi_max_concurrent_streams(data->multi)) { - infof(data, "client side MAX_CONCURRENT_STREAMS reached" - ", skip (%zu)", - multiplexed); - continue; - } - } -#endif - /* When not multiplexed, we have a match here! */ - chosen = check; - infof(data, "Multiplexed connection found"); - break; - } - else { - /* We have found a connection. Let's stop searching. */ - chosen = check; - break; - } + if(CONN_INUSE(check)) { + DEBUGASSERT(canmultiplex); + DEBUGASSERT(check->bits.multiplex); + /* If multiplexed, make sure we don't go over concurrency limit */ + if(CONN_INUSE(check) >= + Curl_multi_max_concurrent_streams(data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)", CONN_INUSE(check)); + continue; } + if(CONN_INUSE(check) >= + Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", + CONN_INUSE(check)); + continue; + } + /* When not multiplexed, we have a match here! */ + infof(data, "Multiplexed connection found"); + } + else if(extract_if_dead(check, data)) { + /* disconnect it */ + Curl_disconnect(data, check, TRUE); + continue; } - } + + /* We have found a connection. Let's stop searching. */ + chosen = check; + break; + } /* loop over connection bundle */ if(chosen) { /* mark it as used before releasing the lock */ @@ -1561,17 +1391,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; #endif - conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; - conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; - conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; - conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options; -#ifndef CURL_DISABLE_PROXY - conn->proxy_ssl_config.verifystatus = - data->set.proxy_ssl.primary.verifystatus; - conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; - conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; - conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options; -#endif conn->ip_version = data->set.ipver; conn->connect_only = data->set.connect_only; conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ @@ -1615,30 +1434,231 @@ error: return NULL; } -/* returns the handler if the given scheme is built-in */ -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen) +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) { - const struct Curl_handler * const *pp; - const struct Curl_handler *p; - /* Scan protocol handler table and match against 'scheme'. The handler may - be changed later when the protocol specific setup function is called. */ - if(schemelen == CURL_ZERO_TERMINATED) - schemelen = strlen(scheme); - for(pp = protocols; (p = *pp) != NULL; pp++) - if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen]) - /* Protocol found in table. */ - return p; - return NULL; /* not found */ + return Curl_getn_scheme_handler(scheme, strlen(scheme)); } +/* returns the handler if the given scheme is built-in */ +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len) +{ + /* table generated by schemetable.c: + 1. gcc schemetable.c && ./a.out + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + 7. if needed, adjust the #ifdefs in schemetable.c and rerun + */ + static const struct Curl_handler * const protocols[67] = { +#ifndef CURL_DISABLE_FILE + &Curl_handler_file, +#else + NULL, +#endif + NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) + &Curl_handler_gophers, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpe, +#else + NULL, +#endif +#ifndef CURL_DISABLE_SMTP + &Curl_handler_smtp, +#else + NULL, +#endif +#if defined(USE_SSH) + &Curl_handler_sftp, +#else + NULL, +#endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ + (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smb, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) + &Curl_handler_smtps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TELNET + &Curl_handler_telnet, +#else + NULL, +#endif +#ifndef CURL_DISABLE_GOPHER + &Curl_handler_gopher, +#else + NULL, +#endif +#ifndef CURL_DISABLE_TFTP + &Curl_handler_tftp, +#else + NULL, +#endif + NULL, NULL, NULL, +#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) + &Curl_handler_ftps, +#else + NULL, +#endif +#ifndef CURL_DISABLE_HTTP + &Curl_handler_http, +#else + NULL, +#endif +#ifndef CURL_DISABLE_IMAP + &Curl_handler_imap, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmps, +#else + NULL, +#endif +#ifdef USE_LIBRTMP + &Curl_handler_rtmpt, +#else + NULL, +#endif + NULL, NULL, NULL, +#if !defined(CURL_DISABLE_LDAP) && \ + !defined(CURL_DISABLE_LDAPS) && \ + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) + &Curl_handler_ldaps, +#else + NULL, +#endif +#if defined(USE_WEBSOCKETS) && \ + defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_wss, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_https, +#else + NULL, +#endif + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#ifndef CURL_DISABLE_RTSP + &Curl_handler_rtsp, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \ + defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4) + &Curl_handler_smbs, +#else + NULL, +#endif +#if defined(USE_SSH) && !defined(USE_WOLFSSH) + &Curl_handler_scp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_POP3 + &Curl_handler_pop3, +#else + NULL, +#endif + NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmp, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpte, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_DICT + &Curl_handler_dict, +#else + NULL, +#endif + NULL, NULL, NULL, +#ifndef CURL_DISABLE_MQTT + &Curl_handler_mqtt, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) + &Curl_handler_pop3s, +#else + NULL, +#endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) + &Curl_handler_imaps, +#else + NULL, +#endif + NULL, +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + &Curl_handler_ws, +#else + NULL, +#endif + NULL, +#ifdef USE_LIBRTMP + &Curl_handler_rtmpts, +#else + NULL, +#endif +#ifndef CURL_DISABLE_LDAP + &Curl_handler_ldap, +#else + NULL, +#endif + NULL, NULL, +#ifndef CURL_DISABLE_FTP + &Curl_handler_ftp, +#else + NULL, +#endif + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_handler *h; + unsigned int c = 978; + while(l) { + c <<= 5; + c += Curl_raw_tolower(*s); + s++; + l--; + } + + h = protocols[c % 67]; + if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len]) + return h; + } + return NULL; +} static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) { - const struct Curl_handler *p = Curl_builtin_scheme(protostr, - CURL_ZERO_TERMINATED); + const struct Curl_handler *p = Curl_get_scheme_handler(protostr); if(p && /* Protocol found in table. Check if allowed */ (data->set.allowed_protocols & p->protocol)) { @@ -1652,7 +1672,6 @@ static CURLcode findprotocol(struct Curl_easy *data, else { /* Perform setup complement if some. */ conn->handler = conn->given = p; - /* 'port' and 'remote_port' are set in setup_connection_internals() */ return CURLE_OK; } @@ -1705,14 +1724,14 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, conn->scope_id = (unsigned int)scope; #if defined(HAVE_IF_NAMETOINDEX) else { -#elif defined(WIN32) +#elif defined(_WIN32) else if(Curl_if_nametoindex) { #endif -#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32) +#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) /* Zone identifier is not numeric */ unsigned int scopeidx = 0; -#if defined(WIN32) +#if defined(_WIN32) scopeidx = Curl_if_nametoindex(zoneid); #else scopeidx = if_nametoindex(zoneid); @@ -1727,7 +1746,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, else conn->scope_id = scopeidx; } -#endif /* HAVE_IF_NAMETOINDEX || WIN32 */ +#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ free(zoneid); } @@ -3596,85 +3615,10 @@ static CURLcode create_conn(struct Curl_easy *data, conn->send[SECONDARYSOCKET] = Curl_conn_send; conn->bits.tcp_fastopen = data->set.tcp_fastopen; - /* Get a cloned copy of the SSL config situation stored in the - connection struct. But to get this going nicely, we must first make - sure that the strings in the master copy are pointing to the correct - strings in the session handle strings array! - - Keep in mind that the pointers in the master copy are pointing to strings - that will be freed as part of the Curl_easy struct, but all cloned - copies will be separately allocated. - */ - data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - data->set.ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST]; - data->set.ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST]; - data->set.ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; - data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; - -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - data->set.proxy_ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - data->set.proxy_ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - data->set.proxy_ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - data->set.proxy_ssl.primary.ca_info_blob = - data->set.blobs[BLOB_CAINFO_PROXY]; - data->set.proxy_ssl.primary.issuercert = - data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.issuercert_blob = - data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.CRLfile = - data->set.str[STRING_SSL_CRLFILE_PROXY]; - data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; - data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; - data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#endif - data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; - data->set.ssl.key = data->set.str[STRING_KEY]; - data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; - data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; - data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; -#ifdef USE_TLS_SRP - data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.username = - data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - data->set.proxy_ssl.primary.password = - data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif -#endif - data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; - - if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary, - &conn->ssl_config)) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifndef CURL_DISABLE_PROXY - if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) { - result = CURLE_OUT_OF_MEMORY; + /* Complete the easy's SSL configuration for connection cache matching */ + result = Curl_ssl_easy_config_complete(data); + if(result) goto out; - } -#endif prune_dead_connections(data); @@ -3789,7 +3733,41 @@ static CURLcode create_conn(struct Curl_easy *data, * This is a brand new connection, so let's store it in the connection * cache of ours! */ + result = Curl_ssl_conn_config_init(data, conn); + if(result) { + DEBUGF(fprintf(stderr, "Error: init connection ssl config\n")); + goto out; + } + + result = Curl_resolver_init(data, &conn->resolve_async.resolver); + if(result) { + DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); + goto out; + } + Curl_attach_connection(data, conn); + +#ifdef USE_ARES + result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_interface(data, + data->set.str[STRING_DNS_INTERFACE]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_local_ip4(data, + data->set.str[STRING_DNS_LOCAL_IP4]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; + + result = Curl_set_dns_local_ip6(data, + data->set.str[STRING_DNS_LOCAL_IP6]); + if(result && result != CURLE_NOT_BUILT_IN) + goto out; +#endif /* USE_ARES */ + result = Curl_conncache_add_conn(data); if(result) goto out; @@ -3976,6 +3954,7 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) k->bytecount = 0; k->ignorebody = FALSE; + Curl_client_cleanup(data); Curl_speedinit(data); Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index f6a5b25..7c1a29b 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -46,8 +46,13 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen); +/* Get protocol handler for a URI scheme + * @param scheme URI scheme, case-insensitive + * @return NULL of handler not found + */ +const struct Curl_handler *Curl_get_scheme_handler(const char *scheme); +const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, + size_t len); #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index 4efab61..0d11e48 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -206,7 +206,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, (void)buflen; /* only used in debug-builds */ if(buf) buf[0] = 0; /* always leave a defined value in buf */ -#ifdef WIN32 +#ifdef _WIN32 if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) return 0; #endif @@ -446,7 +446,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, /* if this is a known scheme, get some details */ if(u->scheme) - h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + h = Curl_get_scheme_handler(u->scheme); /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ @@ -1056,7 +1056,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) ptr += 9; /* now points to the slash after the host */ } else { -#if defined(WIN32) +#if defined(_WIN32) size_t len; /* the host name, NetBIOS computer name, can not contain disallowed @@ -1095,7 +1095,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* no host for file: URLs by default */ Curl_dyn_reset(&host); -#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) /* Don't allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || @@ -1129,7 +1129,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } schemep = schemebuf; - if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) && + if(!Curl_get_scheme_handler(schemep) && !(flags & CURLU_NON_SUPPORT_SCHEME)) { result = CURLUE_UNSUPPORTED_SCHEME; goto fail; @@ -1224,14 +1224,14 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if(flags & CURLU_URLENCODE) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, fragment + 1, fraglen, TRUE, FALSE)) { + if(urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE)) { result = CURLUE_OUT_OF_MEMORY; goto fail; } u->fragment = Curl_dyn_ptr(&enc); } else { - u->fragment = Curl_memdup(fragment + 1, fraglen); + u->fragment = Curl_strndup(fragment + 1, fraglen - 1); if(!u->fragment) { result = CURLUE_OUT_OF_MEMORY; goto fail; @@ -1260,12 +1260,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) u->query = Curl_dyn_ptr(&enc); } else { - u->query = Curl_memdup(query + 1, qlen); + u->query = Curl_strndup(query + 1, qlen - 1); if(!u->query) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - u->query[qlen - 1] = 0; } } else { @@ -1295,12 +1294,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } else { if(!u->path) { - u->path = Curl_memdup(path, pathlen + 1); + u->path = Curl_strndup(path, pathlen); if(!u->path) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - u->path[pathlen] = 0; path = u->path; } else if(flags & CURLU_URLENCODE) @@ -1352,7 +1350,7 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u, */ CURLU *curl_url(void) { - return calloc(sizeof(struct Curl_URL), 1); + return calloc(1, sizeof(struct Curl_URL)); } void curl_url_cleanup(CURLU *u) @@ -1374,7 +1372,7 @@ void curl_url_cleanup(CURLU *u) CURLU *curl_url_dup(const CURLU *in) { - struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); + struct Curl_URL *u = calloc(1, sizeof(struct Curl_URL)); if(u) { DUP(u, in, scheme); DUP(u, in, user); @@ -1447,8 +1445,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there's no stored port number, but asked to deliver a default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); if(h) { msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); ptr = portbuf; @@ -1457,8 +1454,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, else if(ptr && u->scheme) { /* there is a stored port number, but ask to inhibit if it matches the default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); + const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); if(h && (h->defport == u->portnum) && (flags & CURLU_NO_DEFAULT_PORT)) ptr = NULL; @@ -1503,7 +1499,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, else return CURLUE_NO_SCHEME; - h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); + h = Curl_get_scheme_handler(scheme); if(!port && (flags & CURLU_DEFAULT_PORT)) { /* there's no stored port number, but asked to deliver a default one for the scheme */ @@ -1596,7 +1592,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, if(ptr) { size_t partlen = strlen(ptr); size_t i = 0; - *part = Curl_memdup(ptr, partlen + 1); + *part = Curl_strndup(ptr, partlen); if(!*part) return CURLUE_OUT_OF_MEMORY; if(plusdecode) { @@ -1743,9 +1739,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if((plen > MAX_SCHEME_LEN) || (plen < 1)) /* too long or too short */ return CURLUE_BAD_SCHEME; - if(!(flags & CURLU_NON_SUPPORT_SCHEME) && - /* verify that it is a fine scheme */ - !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED)) + /* verify that it is a fine scheme */ + if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part)) return CURLUE_UNSUPPORTED_SCHEME; storep = &u->scheme; urlencode = FALSE; /* never */ @@ -1905,7 +1900,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } newp = Curl_dyn_ptr(&enc); - if(appendquery) { + if(appendquery && newp) { /* Append the 'newp' string onto the old query. Add a '&' separator if none is present at the end of the existing query already */ @@ -1934,8 +1929,8 @@ nomem: } } - if(what == CURLUPART_HOST) { - size_t n = strlen(newp); + else if(what == CURLUPART_HOST) { + size_t n = Curl_dyn_len(&enc); if(!n && (flags & CURLU_NO_AUTHORITY)) { /* Skip hostname check, it's allowed to be empty. */ } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index dff26e6..ff66148 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -266,6 +266,13 @@ typedef enum { /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; +struct ssl_peer { + char *hostname; /* hostname for verification */ + char *dispname; /* display version of hostname */ + char *sni; /* SNI version of hostname or NULL if not usable */ + BIT(is_ip_address); /* if hostname is an IPv4|6 address */ +}; + struct ssl_primary_config { char *CApath; /* certificate dir (doesn't work on windows) */ char *CAfile; /* certificate to verify peer against */ @@ -571,6 +578,13 @@ struct hostname { #define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) +/* transfer wants to send is not PAUSE or HOLD */ +#define CURL_WANT_SEND(data) \ + (((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND) +/* transfer receive is not on PAUSE or HOLD */ +#define CURL_WANT_RECV(data) \ + (!((data)->req.keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD))) + #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) #define USE_CURL_ASYNC struct Curl_async { @@ -589,6 +603,15 @@ struct Curl_async { #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 +/* Polling requested by an easy handle. + * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT. + */ +struct easy_pollset { + curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; + unsigned int num; + unsigned char actions[MAX_SOCKSPEREASYHANDLE]; +}; + enum expect100 { EXP100_SEND_DATA, /* enough waiting, just send the body now */ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ @@ -649,16 +672,8 @@ struct SingleRequest { counter to make only a 100 reply (without a following second response code) result in a CURLE_GOT_NOTHING error code */ - enum { - HEADER_NORMAL, /* no bad header at all */ - HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest - is normal data */ - HEADER_ALLBAD /* all was believed to be header */ - } badheader; /* the header was deemed bad and will be - written as body */ int headerline; /* counts header lines to better track the first one */ - char *str; /* within buf */ curl_off_t offset; /* possible resume offset read from the Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or @@ -669,7 +684,7 @@ struct SingleRequest { enum upgrade101 upgr101; /* 101 upgrade state */ /* Content unencoding stack. See sec 3.5, RFC2616. */ - struct contenc_writer *writer_stack; + struct Curl_cwriter *writer_stack; time_t timeofdoc; long bodywrites; char *location; /* This points to an allocated version of the Location: @@ -706,16 +721,20 @@ struct SingleRequest { #ifndef CURL_DISABLE_DOH struct dohdata *doh; /* DoH specific data for this request */ #endif -#if defined(WIN32) && defined(USE_WINSOCK) +#if defined(_WIN32) && defined(USE_WINSOCK) struct curltime last_sndbuf_update; /* last time readwrite_upload called win_update_buffer_size */ #endif + char fread_eof[2]; /* the body read callback (index 0) returned EOF or + the trailer read callback (index 1) returned EOF */ #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; #endif unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ + BIT(badheader); /* header parsing found sth not a header */ BIT(content_range); /* set TRUE if Content-Range: was found */ + BIT(download_done); /* set to TRUE when download is complete */ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding upload and we're uploading the last chunk */ BIT(ignorebody); /* we read a response-body but we ignore it! */ @@ -799,7 +818,8 @@ struct Curl_handler { /* If used, this function gets called from transfer.c:readwrite_data() to allow the protocol to do extra reads/writes */ CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, bool *readmore); + const char *buf, size_t blen, + size_t *pconsumed, bool *readmore); /* This function can perform various checks on the connection. See CONNCHECK_* for more information about the checks that can be performed, @@ -901,6 +921,9 @@ struct connectdata { multi_done(). This entry will be NULL if the connection is reused as then there is no name resolve done. */ struct Curl_dns_entry *dns_entry; +#ifdef USE_CURL_ASYNC + struct Curl_async resolve_async; /* asynchronous name resolver data */ +#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). */ @@ -1325,7 +1348,8 @@ struct UrlState { curl_off_t recent_conn_id; /* The most recent connection used, might no * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ - + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ char *buffer; /* download buffer */ char *ulbuf; /* allocated upload buffer or NULL */ curl_off_t current_speed; /* the ProgressShow() function sets this, @@ -1358,9 +1382,6 @@ struct UrlState { #endif struct auth authhost; /* auth details for host */ struct auth authproxy; /* auth details for proxy */ -#ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ -#endif #if defined(USE_OPENSSL) /* void instead of ENGINE to avoid bleeding OpenSSL into this header */ @@ -1373,7 +1394,7 @@ struct UrlState { /* a place to store the most recently set (S)FTP entrypath */ char *most_recent_ftp_entrypath; -#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) /* do FTP line-end conversions on most platforms */ #define CURL_DO_LINEEND_CONV /* for FTP downloads: track CRLF sequences that span blocks */ @@ -1411,7 +1432,7 @@ struct UrlState { this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP curl_mimepart *mimepost; - curl_mimepart *formp; /* storage for old API form-posting, alloced on + curl_mimepart *formp; /* storage for old API form-posting, allocated on demand */ size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing @@ -1422,6 +1443,10 @@ struct UrlState { trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ #endif +#ifndef CURL_DISABLE_COOKIES + struct curl_slist *cookielist; /* list of cookie files set by + curl_easy_setopt(COOKIEFILE) calls */ +#endif #ifdef USE_HYPER bool hconnect; /* set if a CONNECT request */ CURLcode hresult; /* used to pass return codes back from hyper callbacks */ @@ -1498,6 +1523,9 @@ struct UrlState { though it will be discarded. We must call the data rewind callback before trying to send again. */ BIT(upload); /* upload request */ + BIT(internal); /* internal: true if this easy handle was created for + internal use and the user does not have ownership of the + handle. */ }; /* @@ -1674,13 +1702,7 @@ 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; @@ -1780,9 +1802,6 @@ 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 */ @@ -1805,8 +1824,6 @@ struct UserDefined { int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ - size_t maxconnects; /* Max idle connections in the connection cache */ - long expect_100_timeout; /* in milliseconds */ #if defined(USE_HTTP2) || defined(USE_HTTP3) struct Curl_data_priority priority; @@ -1831,10 +1848,14 @@ struct UserDefined { BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some recipients */ #endif + unsigned int maxconnects; /* Max idle connections in the connection cache */ unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! (type: curl_usessl)*/ unsigned char connect_only; /* make connection/request, then let application use the socket */ +#ifndef CURL_DISABLE_MIME + BIT(mime_formescape); +#endif BIT(is_fread_set); /* has read callback been set to non-NULL? */ #ifndef CURL_DISABLE_TFTP BIT(tftp_no_options); /* do not send TFTP options requests */ @@ -1971,10 +1992,7 @@ struct Curl_easy { particular order. Note that all sockets are added to the sockhash, where the state etc are also kept. This array is mostly used to detect when a socket is to be removed from the hash. See singlesocket(). */ - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in - sockets[] */ - int numsocks; + struct easy_pollset last_poll; struct Names dns; struct Curl_multi *multi; /* if non-NULL, points to the multi handle @@ -2013,10 +2031,6 @@ struct Curl_easy { #ifdef USE_HYPER struct hyptransfer hyp; #endif - - /* internal: true if this easy handle was created for internal use and the - user does not have ownership of the handle. */ - bool internal; }; #define LIBCURL_NAME "libcurl" diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index 12c6f7d..416da0f 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -125,7 +125,6 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, } else return FALSE; - break; } } diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index f99dd38..31bd0a4 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -39,7 +39,7 @@ #ifdef USE_ARES # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) + defined(_WIN32) # define CARES_STATICLIB # endif # include <ares.h> @@ -409,7 +409,8 @@ static int idn_present(curl_version_info_data *info) #define idn_present NULL #endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) static int https_proxy_present(curl_version_info_data *info) { (void) info; @@ -454,13 +455,14 @@ static const struct feat features_table[] = { #ifndef CURL_DISABLE_HSTS FEATURE("HSTS", NULL, CURL_VERSION_HSTS), #endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) +#if defined(USE_NGHTTP2) FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), #endif #if defined(ENABLE_QUIC) FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), #endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) +#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ + !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) @@ -510,7 +512,7 @@ static const struct feat features_table[] = { #ifdef CURLDEBUG FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG), #endif -#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) +#if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE) FEATURE("Unicode", NULL, CURL_VERSION_UNICODE), #endif #ifdef USE_UNIX_SOCKETS diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c index 872d5b4..e0f239e 100644 --- a/Utilities/cmcurl/lib/version_win32.c +++ b/Utilities/cmcurl/lib/version_win32.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) #include <curl/curl.h> #include "version_win32.h" @@ -316,4 +316,4 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, return matched; } -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h index 3899174..95c0661 100644 --- a/Utilities/cmcurl/lib/version_win32.h +++ b/Utilities/cmcurl/lib/version_win32.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if defined(WIN32) +#if defined(_WIN32) /* Version condition */ typedef enum { @@ -51,6 +51,6 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, const PlatformIdentifier platform, const VersionCondition condition); -#endif /* WIN32 */ +#endif /* _WIN32 */ #endif /* HEADER_CURL_VERSION_WIN32_H */ diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index 6bd0d23..8ae3672 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -38,6 +38,7 @@ #include "http1.h" #include "curl_msh3.h" #include "socketpair.h" +#include "vtls/vtls.h" #include "vquic/vquic.h" /* The last 3 #include files should be in this order */ @@ -45,6 +46,10 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef CURL_DISABLE_SOCKETPAIR +#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set" +#endif + #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) #define H3_STREAM_RECV_CHUNKS \ @@ -672,31 +677,25 @@ out: return nwritten; } -static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_msh3_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(data); - int bitmap = GETSOCK_BLANK; struct cf_call_data save; CF_DATA_SAVE(save, cf, data); if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { - socks[0] = ctx->sock[SP_LOCAL]; - if(stream->recv_error) { - bitmap |= GETSOCK_READSOCK(0); + Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]); drain_stream(cf, data); } else if(stream->req) { - bitmap |= GETSOCK_READSOCK(0); + Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]); drain_stream(cf, data); } } - CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap); - CF_DATA_RESTORE(cf, save); - return bitmap; } static bool cf_msh3_data_pending(struct Curl_cfilter *cf, @@ -802,14 +801,20 @@ 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; + struct ssl_primary_config *conn_config; MSH3_ADDR addr = {0}; CURLcode result; + bool verify; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + verify = !!conn_config->verifypeer; memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); - if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) { + if(verify && (conn_config->CAfile || conn_config->CApath)) { /* TODO: need a way to provide trust anchors to MSH3 */ #ifdef DEBUGBUILD /* we need this for our test cases to run */ @@ -1025,7 +1030,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_msh3_connect, cf_msh3_close, Curl_cf_def_get_host, - cf_msh3_get_select_socks, + cf_msh3_adjust_pollset, cf_msh3_data_pending, cf_msh3_send, cf_msh3_recv, @@ -1047,7 +1052,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, (void)data; (void)conn; (void)ai; /* TODO: msh3 resolves itself? */ - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index 7d681e5..f09b10b 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -78,7 +78,6 @@ #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) /* A stream window is the maximum amount we need to buffer for @@ -134,6 +133,7 @@ void Curl_ngtcp2_ver(char *p, size_t len) struct cf_ngtcp2_ctx { struct cf_quic_ctx q; + struct ssl_peer peer; ngtcp2_path connected_path; ngtcp2_conn *qconn; ngtcp2_cid dcid; @@ -161,6 +161,7 @@ struct cf_ngtcp2_ctx { struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ size_t max_stream_window; /* max flow window for one stream */ + uint64_t max_idle_ms; /* max idle time for QUIC connection */ int qlogfd; BIT(got_first_byte); /* if first byte was received */ #ifdef USE_OPENSSL @@ -191,6 +192,7 @@ struct h3_stream_ctx { bool closed; /* TRUE on stream close */ bool reset; /* TRUE on stream reset */ bool send_closed; /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; #define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ @@ -236,11 +238,21 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)cf; if(stream) { CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); + if(ctx->h3conn && !stream->closed) { + nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id); + nghttp3_conn_close_stream(ctx->h3conn, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + } + Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); Curl_h1_req_parse_free(&stream->h1); @@ -249,6 +261,43 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } +static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, + struct Curl_easy *data, + int64_t stream_id) +{ + struct Curl_easy *sdata; + + (void)cf; + if(H3_STREAM_ID(data) == stream_id) { + return data; + } + else { + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { + return sdata; + } + } + } + return NULL; +} + +static void h3_drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(stream && stream->upload_left && !stream->send_closed) + bits |= CURL_CSELECT_OUT; + if(data->state.dselect_bits != bits) { + data->state.dselect_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + /* ngtcp2 default congestion controller does not perform pacing. Limit the maximum packet burst to MAX_PKT_BURST packets. */ #define MAX_PKT_BURST 10 @@ -261,10 +310,14 @@ struct pkt_io_ctx { ngtcp2_path_storage ps; }; -static ngtcp2_tstamp timestamp(void) +static void pktx_update_time(struct pkt_io_ctx *pktx, + struct Curl_cfilter *cf) { - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + vquic_ctx_update_time(&ctx->q); + pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS + + ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; } static void pktx_init(struct pkt_io_ctx *pktx, @@ -273,9 +326,9 @@ static void pktx_init(struct pkt_io_ctx *pktx, { pktx->cf = cf; pktx->data = data; - pktx->ts = timestamp(); pktx->pkt_count = 0; ngtcp2_path_storage_zero(&pktx->ps); + pktx_update_time(pktx, cf); } static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, @@ -354,7 +407,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, t->initial_max_stream_data_uni = ctx->max_stream_window; t->initial_max_streams_bidi = QUIC_MAX_STREAMS; t->initial_max_streams_uni = QUIC_MAX_STREAMS; - t->max_idle_timeout = QUIC_IDLE_TIMEOUT; + t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS); if(ctx->qlogfd != -1) { s->qlog_write = qlog_callback; } @@ -398,14 +451,19 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct connectdata *conn = cf->conn; + struct ssl_primary_config *conn_config; CURLcode result = CURLE_FAILED_INIT; - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); if(!ssl_ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { @@ -422,8 +480,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, SSL_CTX_set_default_verify_paths(ssl_ctx); { - const char *curves = conn->ssl_config.curves ? - conn->ssl_config.curves : QUIC_GROUPS; + const char *curves = conn_config->curves ? + conn_config->curves : QUIC_GROUPS; if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) { failf(data, "failed setting curves list for QUIC: '%s'", curves); return CURLE_SSL_CIPHER; @@ -432,8 +490,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, #ifndef OPENSSL_IS_BORINGSSL { - const char *ciphers13 = conn->ssl_config.cipher_list13 ? - conn->ssl_config.cipher_list13 : QUIC_CIPHERS; + const char *ciphers13 = conn_config->cipher_list13 ? + conn_config->cipher_list13 : QUIC_CIPHERS; if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) { failf(data, "failed setting QUIC cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; @@ -452,7 +510,7 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, * 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_CTX_set_verify(ssl_ctx, conn_config->verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* give application a chance to interfere with SSL set up. */ @@ -491,7 +549,7 @@ static CURLcode quic_set_client_cert(struct Curl_cfilter *cf, SSL_CTX *ssl_ctx = ctx->sslctx; const struct ssl_config_data *ssl_config; - ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); + ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_config); if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob @@ -514,7 +572,6 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; const uint8_t *alpn = NULL; size_t alpnlen = 0; - unsigned char checkip[16]; DEBUGASSERT(!ctx->ssl); ctx->ssl = SSL_new(ctx->sslctx); @@ -529,13 +586,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); /* set SNI */ - if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) -#ifdef ENABLE_IPV6 - && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) -#endif - ) { - char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); - if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + if(ctx->peer.sni) { + if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) { failf(data, "Failed set SNI"); SSL_free(ctx->ssl); ctx->ssl = NULL; @@ -549,20 +601,24 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; 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; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + DEBUGASSERT(ctx->gtls == NULL); ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); if(!ctx->gtls) return CURLE_OUT_OF_MEMORY; - result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl, - hostname, ctx->gtls, pverifyresult); + result = gtls_client_init(data, conn_config, &data->set.ssl, + &ctx->peer, ctx->gtls, pverifyresult); if(result) return result; @@ -602,10 +658,17 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, 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()); + struct ssl_primary_config *conn_config; + WOLFSSL_CTX *ssl_ctx = NULL; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } + + ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); if(!ssl_ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -613,13 +676,14 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + result = CURLE_FAILED_INIT; goto out; } wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn->ssl_config.cipher_list13 ? - conn->ssl_config.cipher_list13 : + if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn_config->cipher_list13 ? + conn_config->cipher_list13 : QUIC_CIPHERS) != 1) { char error_buffer[256]; ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); @@ -627,8 +691,8 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, goto out; } - if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn->ssl_config.curves ? - conn->ssl_config.curves : + if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn_config->curves ? + conn_config->curves : (char *)QUIC_GROUPS) != 1) { failf(data, "wolfSSL failed to set curves"); goto out; @@ -645,9 +709,9 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, #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(conn_config->verifypeer) { + const char * const ssl_cafile = conn_config->CAfile; + const char * const ssl_capath = conn_config->CApath; wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); if(ssl_cafile || ssl_capath) { @@ -786,6 +850,12 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", stream_id, buflen, nconsumed); if(nconsumed < 0) { + if(!data) { + struct Curl_easy *cdata = CF_DATA_CURRENT(cf); + CURL_TRC_CF(cdata, cf, "[%" PRId64 "] nghttp3 error on stream not " + "used by us, ignored", stream_id); + return 0; + } ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); @@ -816,7 +886,7 @@ cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, (void)stream_user_data; rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -844,7 +914,7 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, app_error_code); CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" PRIu64 ") -> %d", stream3_id, app_error_code, rv); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); return NGTCP2_ERR_CALLBACK_FAILURE; @@ -868,7 +938,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -887,7 +957,7 @@ static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, (void)stream_user_data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -911,16 +981,25 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct Curl_easy *s_data; + struct h3_stream_ctx *stream; int rv; (void)tconn; (void)max_data; (void)stream_user_data; rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } - + s_data = get_stream_easy(cf, data, stream_id); + stream = H3_STREAM_CTX(s_data); + if(stream && stream->quic_flow_blocked) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] unblock quic flow", stream_id); + stream->quic_flow_blocked = FALSE; + h3_drain_stream(cf, data); + } return 0; } @@ -1038,7 +1117,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); } expiry = ngtcp2_conn_get_expiry(ctx->qconn); @@ -1073,46 +1152,29 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, return CURLE_OK; } -static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, +static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - socks[0] = ctx->q.sockfd; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - /* in HTTP/3 we can always get a frame, so check read */ - rv |= GETSOCK_READSOCK(0); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND && - ngtcp2_conn_get_cwnd_left(ctx->qconn) && - ngtcp2_conn_get_max_data_left(ctx->qconn) && - stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) - rv |= GETSOCK_WRITESOCK(0); - - CF_DATA_RESTORE(cf, save); - return rv; -} - -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - unsigned char bits; + if(ctx->qconn && (want_recv || want_send)) { + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && stream->upload_left && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - data->state.dselect_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); + CF_DATA_SAVE(save, cf, data); + c_exhaust = !ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn); + s_exhaust = stream && stream->id >= 0 && stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + CF_DATA_RESTORE(cf, save); } } @@ -1141,7 +1203,6 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, else { CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); } - data->req.keepon &= ~KEEP_SEND_HOLD; h3_drain_stream(cf, data); return 0; } @@ -1570,15 +1631,9 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, /* Everything ACKed, we resume upload processing */ if(!stream->sendbuf_len_in_flight) { int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv) { + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - h3_drain_stream(cf, data); - CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id); - } } return 0; } @@ -1676,6 +1731,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, goto out; stream = H3_STREAM_CTX(data); DEBUGASSERT(stream); + if(!stream) { + *err = CURLE_FAILED_INIT; + goto out; + } nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) @@ -1711,7 +1770,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nva[i].flags = NGHTTP3_NV_FLAG_NONE; } - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, data); if(rc) { failf(data, "can get bidi streams"); *err = CURLE_SEND_ERROR; @@ -1860,15 +1919,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream && sent > 0 && stream->sendbuf_len_in_flight) { /* We have unacknowledged DATA and cannot report success to our * caller. Instead we EAGAIN and remember how much we have already - * "written" into our various internal connection buffers. - * We put the stream upload on HOLD, until this gets ACKed. */ + * "written" into our various internal connection buffers. */ stream->upload_blocked_len = sent; CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " "%zu bytes in flight -> EGAIN", stream->id, len, stream->sendbuf_len_in_flight); *err = CURLE_AGAIN; sent = -1; - data->req.keepon |= KEEP_SEND_HOLD; } out: @@ -1887,40 +1944,37 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; 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; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; 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) { + if(conn_config->verifyhost) { #ifdef USE_OPENSSL X509 *server_cert; - server_cert = SSL_get_peer_certificate(ctx->ssl); + server_cert = SSL_get1_peer_certificate(ctx->ssl); if(!server_cert) { return CURLE_PEER_FAILED_VERIFICATION; } - result = Curl_ossl_verifyhost(data, cf->conn, server_cert); + result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, 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, + conn_config, &data->set.ssl, &ctx->peer, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); if(result) return result; #elif defined(USE_WOLFSSL) - if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE) + if(!ctx->peer.sni || + wolfSSL_check_domain_name(ctx->ssl, ctx->peer.sni) == SSL_FAILURE) return CURLE_PEER_FAILED_VERIFICATION; #endif infof(data, "Verified certificate just fine"); @@ -1955,8 +2009,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", - ngtcp2_strerror(rv)); + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); if(!ctx->last_error.error_code) { if(rv == NGTCP2_ERR_CRYPTO) { ngtcp2_ccerr_set_tls_alert(&ctx->last_error, @@ -1993,7 +2047,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); } #ifdef USE_OPENSSL @@ -2081,11 +2135,18 @@ static ssize_t read_pkt_to_send(void *userp, } else if(n < 0) { switch(n) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: + case NGTCP2_ERR_STREAM_DATA_BLOCKED: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(x->data); DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", + stream_id); + DEBUGASSERT(stream); + if(stream) + stream->quic_flow_blocked = TRUE; n = 0; break; + } case NGTCP2_ERR_STREAM_SHUT_WR: DEBUGASSERT(ndatalen == -1); nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); @@ -2145,7 +2206,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx->ts = timestamp(); + pktx_update_time(pktx, cf); ngtcp2_path_storage_zero(&pktx->ps); } @@ -2282,10 +2343,12 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; - } case CF_CTRL_DATA_DONE_SEND: { struct h3_stream_ctx *stream = H3_STREAM_CTX(data); if(stream && !stream->send_closed) { @@ -2344,6 +2407,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) if(ctx->qconn) ngtcp2_conn_del(ctx->qconn); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_ssl_peer_cleanup(&ctx->peer); memset(ctx, 0, sizeof(*ctx)); ctx->qlogfd = -1; @@ -2358,15 +2422,15 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx && ctx->qconn) { char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_tstamp ts; + struct pkt_io_ctx pktx; ngtcp2_ssize rc; CURL_TRC_CF(data, cf, "close"); - ts = timestamp(); + pktx_init(&pktx, cf, data); rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ NULL, /* pkt_info */ (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, ts); + &ctx->last_error, pktx.ts); if(rc > 0) { while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && SOCKERRNO == EINTR); @@ -2411,9 +2475,14 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->version = NGTCP2_PROTO_VER_MAX; ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); + result = Curl_ssl_peer_init(&ctx->peer, cf); + if(result) + return result; + #ifdef USE_OPENSSL result = quic_ssl_ctx(&ctx->sslctx, cf, data); if(result) @@ -2559,27 +2628,9 @@ out: ngtcp2_conn_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; - - CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms); - Curl_conn_cf_close(cf->next, data); - cf_ngtcp2_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - 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; - } + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -2657,24 +2708,51 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) { - bool alive = TRUE; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool alive = FALSE; + const ngtcp2_transport_params *rp; + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; + if(!ctx->qconn) + goto out; + + /* Both sides of the QUIC connection announce they max idle times in + * the transport parameters. Look at the minimum of both and if + * we exceed this, regard the connection as dead. The other side + * may have completely purged it and will no longer respond + * to any packets from us. */ + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(rp) { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + + if(rp->max_idle_timeout && + (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms) + idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + goto out; + } + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) - return FALSE; + goto out; + alive = TRUE; if(*input_pending) { + CURLcode result; /* This happens before we've sent off a request and the connection is not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ *input_pending = FALSE; - if(cf_progress_ingress(cf, data, NULL)) - alive = FALSE; - else { - alive = TRUE; - } + result = cf_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + alive = result? FALSE : TRUE; } +out: + CF_DATA_RESTORE(cf, save); return alive; } @@ -2686,7 +2764,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_connect, cf_ngtcp2_close, Curl_cf_def_get_host, - cf_ngtcp2_get_select_socks, + cf_ngtcp2_adjust_pollset, cf_ngtcp2_data_pending, cf_ngtcp2_send, cf_ngtcp2_recv, @@ -2706,7 +2784,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index 3f5d327..7123d63 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -55,10 +55,10 @@ #include "curl_memory.h" #include "memdebug.h" -/* #define DEBUG_QUICHE */ +/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ +#define CURL_H3_NO_ERROR (0x0100) #define QUIC_MAX_STREAMS (100) -#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) @@ -92,6 +92,7 @@ static void keylog_callback(const SSL *ssl, const char *line) struct cf_quiche_ctx { struct cf_quic_ctx q; + struct ssl_peer peer; quiche_conn *qconn; quiche_config *cfg; quiche_h3_conn *h3c; @@ -105,7 +106,7 @@ struct cf_quiche_ctx { struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ curl_off_t data_recvd; - size_t sends_on_hold; /* # of streams with SEND_HOLD set */ + uint64_t max_idle_ms; /* max idle time for QUIC conn */ BIT(goaway); /* got GOAWAY from server */ BIT(got_first_byte); /* if first byte was received */ BIT(x509_store_setup); /* if x509 store has been set up */ @@ -132,6 +133,8 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) if(ctx->cfg) quiche_config_free(ctx->cfg); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_ssl_peer_cleanup(&ctx->peer); + memset(ctx, 0, sizeof(*ctx)); } } @@ -140,11 +143,16 @@ static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; if(!ctx->x509_store_setup) { - if(cf->conn->ssl_config.verifypeer) { - const char * const ssl_cafile = cf->conn->ssl_config.CAfile; - const char * const ssl_capath = cf->conn->ssl_config.CApath; + if(conn_config->verifypeer) { + const char * const ssl_cafile = conn_config->CAfile; + const char * const ssl_capath = conn_config->CApath; if(ssl_cafile || ssl_capath) { SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL); /* tell OpenSSL where to find CA certificates that are used to verify @@ -177,9 +185,16 @@ static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - unsigned char checkip[16]; - struct connectdata *conn = data->conn; - const char *curves = conn->ssl_config.curves; + struct ssl_primary_config *conn_config; + CURLcode result; + + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + + result = Curl_ssl_peer_init(&ctx->peer, cf); + if(result) + return result; DEBUGASSERT(!ctx->sslctx); ctx->sslctx = SSL_CTX_new(TLS_method()); @@ -198,8 +213,10 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback); } - if(curves && !SSL_CTX_set1_curves_list(ctx->sslctx, curves)) { - failf(data, "failed setting curves list for QUIC: '%s'", curves); + if(conn_config->curves && + !SSL_CTX_set1_curves_list(ctx->sslctx, conn_config->curves)) { + failf(data, "failed setting curves list for QUIC: '%s'", + conn_config->curves); return CURLE_SSL_CIPHER; } @@ -209,13 +226,8 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_set_app_data(ctx->ssl, cf); - if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) -#ifdef ENABLE_IPV6 - && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) -#endif - ) { - char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); - if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + if(ctx->peer.sni) { + if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) { failf(data, "Failed set SNI"); SSL_free(ctx->ssl); ctx->ssl = NULL; @@ -240,6 +252,7 @@ struct stream_ctx { bool send_closed; /* stream is locally closed */ bool resp_hds_complete; /* complete, final response has been received */ bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; #define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ @@ -249,56 +262,20 @@ struct stream_ctx { #define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ H3_STREAM_CTX(d)->id : -2) -static bool stream_send_is_suspended(struct Curl_easy *data) -{ - return (data->req.keepon & KEEP_SEND_HOLD); -} - -static void stream_send_suspend(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { - data->req.keepon |= KEEP_SEND_HOLD; - ++ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending", - H3_STREAM_ID(data)); - else - CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url); - } -} - -static void stream_send_resume(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; - if(H3_STREAM_ID(data) >= 0) - CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending", - H3_STREAM_ID(data)); - else - CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - static void check_resumes(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_quiche_ctx *ctx = cf->ctx; struct Curl_easy *sdata; - - if(ctx->sends_on_hold) { - DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; - sdata && ctx->sends_on_hold; sdata = sdata->next) { - if(stream_send_is_suspended(sdata)) { - stream_send_resume(cf, sdata); + struct stream_ctx *stream; + + DEBUGASSERT(data->multi); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata->conn == data->conn) { + stream = H3_STREAM_CTX(sdata); + if(stream && stream->quic_flow_blocked) { + stream->quic_flow_blocked = FALSE; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id); } } } @@ -333,9 +310,15 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); - if(stream_send_is_suspended(data)) { - data->req.keepon &= ~KEEP_SEND_HOLD; - --ctx->sends_on_hold; + if(ctx->qconn && !stream->closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); + if(!stream->send_closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); + stream->send_closed = TRUE; + } + stream->closed = TRUE; } Curl_bufq_free(&stream->recvbuf); Curl_h1_req_parse_free(&stream->h1); @@ -590,7 +573,6 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, } stream->closed = TRUE; streamclose(cf->conn, "End of stream"); - data->req.keepon &= ~KEEP_SEND_HOLD; break; case QUICHE_H3_EVENT_GOAWAY: @@ -883,6 +865,8 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t nread = -1; CURLcode result; + vquic_ctx_update_time(&ctx->q); + if(!stream) { *err = CURLE_RECV_ERROR; return -1; @@ -1035,9 +1019,8 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED", - data->state.url); - stream_send_suspend(cf, data); + CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id); + stream->quic_flow_blocked = TRUE; *err = CURLE_AGAIN; nwritten = -1; goto out; @@ -1081,6 +1064,8 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; ssize_t nwritten; + vquic_ctx_update_time(&ctx->q); + *err = cf_process_ingress(cf, data); if(*err) { nwritten = -1; @@ -1104,7 +1089,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); - stream_send_suspend(cf, data); + stream->quic_flow_blocked = TRUE; } *err = CURLE_AGAIN; nwritten = -1; @@ -1173,30 +1158,32 @@ static bool stream_is_writeable(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(data); - return stream && - quiche_conn_stream_writable(ctx->qconn, (uint64_t)stream->id, 1); + return stream && (quiche_conn_stream_writable(ctx->qconn, + (uint64_t)stream->id, 1) > 0); } -static int cf_quiche_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_quiche_ctx *ctx = cf->ctx; - struct SingleRequest *k = &data->req; - int rv = GETSOCK_BLANK; - - socks[0] = ctx->q.sockfd; + bool want_recv = CURL_WANT_RECV(data); + bool want_send = CURL_WANT_SEND(data); - /* in an HTTP/3 connection we can basically always get a frame so we should - always be ready for one */ - rv |= GETSOCK_READSOCK(0); + if(ctx->qconn && (want_recv || want_send)) { + struct stream_ctx *stream = H3_STREAM_CTX(data); + bool c_exhaust, s_exhaust; - /* we're still uploading or the HTTP/3 layer wants to send data */ - if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - && stream_is_writeable(cf, data)) - rv |= GETSOCK_WRITESOCK(0); + c_exhaust = FALSE; /* Have not found any call in quiche that tells + us if the connection itself is blocked */ + s_exhaust = stream && stream->id >= 0 && + (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); - return rv; + Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); + } } /* @@ -1238,10 +1225,12 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE: { + case CF_CTRL_DATA_DETACH: + h3_data_done(cf, data); + break; + case CF_CTRL_DATA_DONE: h3_data_done(cf, data); break; - } case CF_CTRL_DATA_DONE_SEND: { struct stream_ctx *stream = H3_STREAM_CTX(data); if(stream && !stream->send_closed) { @@ -1276,20 +1265,25 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; + struct ssl_primary_config *conn_config; CURLcode result = CURLE_OK; + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) + return CURLE_FAILED_INIT; + 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) { + if(conn_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); + result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert); X509_free(server_cert); if(result) goto out; @@ -1345,6 +1339,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, debug_log_init = 1; } #endif + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); ctx->data_recvd = 0; @@ -1359,7 +1354,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } quiche_config_enable_pacing(ctx->cfg, false); - quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT); + quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000); quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); @@ -1411,7 +1406,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, } /* Known to not work on Windows */ -#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) +#if !defined(_WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) { int qfd; (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); @@ -1449,7 +1444,6 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, { struct cf_quiche_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct curltime now; if(cf->connected) { *done = TRUE; @@ -1464,9 +1458,10 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } *done = FALSE; - now = Curl_now(); + vquic_ctx_update_time(&ctx->q); - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + if(ctx->reconnect_at.tv_sec && + Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; @@ -1476,7 +1471,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, result = cf_connect_start(cf, data); if(result) goto out; - ctx->started_at = now; + ctx->started_at = ctx->q.last_op; result = cf_flush_egress(cf, data); /* we do not expect to be able to recv anything yet */ goto out; @@ -1491,9 +1486,9 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, goto out; if(quiche_conn_is_established(ctx->qconn)) { + ctx->handshake_at = ctx->q.last_op; CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at)); - ctx->handshake_at = now; + (int)Curl_timediff(ctx->handshake_at, ctx->started_at)); result = cf_verify_peer(cf, data); if(!result) { CURL_TRC_CF(data, cf, "peer verified"); @@ -1506,27 +1501,9 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, 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; - - CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms); - Curl_conn_cf_close(cf->next, data); - cf_quiche_ctx_clear(ctx); - result = Curl_conn_cf_connect(cf->next, data, FALSE, done); - 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; - } + * state. The CONNECT may work in the near future again. Indicate + * that as a "weird" reply. */ + result = CURLE_WEIRD_SERVER_REPLY; } out: @@ -1550,6 +1527,7 @@ static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(ctx) { if(ctx->qconn) { + vquic_ctx_update_time(&ctx->q); (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); /* flushing the egress is not a failsafe way to deliver all the outstanding packets, but we also don't want to get stuck here... */ @@ -1617,9 +1595,32 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) { + struct cf_quiche_ctx *ctx = cf->ctx; bool alive = TRUE; *input_pending = FALSE; + if(!ctx->qconn) + return FALSE; + + /* Both sides of the QUIC connection announce they max idle times in + * the transport parameters. Look at the minimum of both and if + * we exceed this, regard the connection as dead. The other side + * may have completely purged it and will no longer respond + * to any packets from us. */ + { + quiche_transport_params qpeerparams; + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + + if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) && + qpeerparams.peer_max_idle_timeout && + qpeerparams.peer_max_idle_timeout < idle_ms) + idle_ms = qpeerparams.peer_max_idle_timeout; + idletime = Curl_timediff(Curl_now(), cf->conn->lastused); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + return FALSE; + } + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) return FALSE; @@ -1646,7 +1647,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_connect, cf_quiche_close, Curl_cf_def_get_host, - cf_quiche_get_select_socks, + cf_quiche_adjust_pollset, cf_quiche_data_pending, cf_quiche_send, cf_quiche_recv, @@ -1667,7 +1668,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, (void)data; (void)conn; - ctx = calloc(sizeof(*ctx), 1); + ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 9a1a1bb..523b807 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -100,6 +100,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) } } #endif + vquic_ctx_update_time(qctx); return CURLE_OK; } @@ -109,6 +110,11 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx) Curl_bufq_free(&qctx->sendbuf); } +void vquic_ctx_update_time(struct cf_quic_ctx *qctx) +{ + qctx->last_op = Curl_now(); +} + static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -242,6 +248,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { + CURLcode result; #ifdef DEBUGBUILD /* simulate network blocking/partial writes */ if(qctx->wblock_percent > 0) { @@ -254,10 +261,14 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } #endif if(qctx->no_gso && pktlen > gsolen) { - return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } - - return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + else { + result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + if(!result) + qctx->last_io = qctx->last_op; + return result; } CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -524,13 +535,17 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, size_t max_pkts, vquic_recv_pkt_cb *recv_cb, void *userp) { + CURLcode result; #if defined(HAVE_SENDMMSG) - return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #elif defined(HAVE_SENDMSG) - return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #else - return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); + result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); #endif + if(!result) + qctx->last_io = qctx->last_op; + return result; } /* diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index dbcd009..a820f39 100644 --- a/Utilities/cmcurl/lib/vquic/vquic_int.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h @@ -31,6 +31,8 @@ #define MAX_PKT_BURST 10 #define MAX_UDP_PAYLOAD_SIZE 1452 +/* Default QUIC connection timeout we announce from our side */ +#define CURL_QUIC_MAX_IDLE_MS (120 * 1000) struct cf_quic_ctx { curl_socket_t sockfd; /* connected UDP socket */ @@ -38,6 +40,8 @@ struct cf_quic_ctx { socklen_t local_addrlen; /* length of local address */ struct bufq sendbuf; /* buffer for sending one or more packets */ + struct curltime last_op; /* last (attempted) send/recv operation */ + struct curltime last_io; /* last successful socket IO */ size_t gsolen; /* length of individual packets in send buf */ size_t split_len; /* if != 0, buffer length after which GSO differs */ size_t split_gsolen; /* length of individual packets after split_len */ @@ -50,6 +54,8 @@ struct cf_quic_ctx { CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx); void vquic_ctx_free(struct cf_quic_ctx *qctx); +void vquic_ctx_update_time(struct cf_quic_ctx *qctx); + void vquic_push_blocked_pkt(struct Curl_cfilter *cf, struct cf_quic_ctx *qctx, const uint8_t *pkt, size_t pktlen, size_t gsolen); diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index b0f49d6..97143c4 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -93,6 +93,7 @@ #if defined(__GNUC__) && \ (LIBSSH_VERSION_MINOR >= 10) || \ (LIBSSH_VERSION_MAJOR > 0) +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -1159,13 +1160,23 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } else if(statvfs) { + #ifdef _MSC_VER + #define LIBSSH_VFS_SIZE_MASK "I64u" + #else + #define LIBSSH_VFS_SIZE_MASK PRIu64 + #endif char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", + "f_bsize: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_frsize: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_blocks: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_bfree: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_bavail: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_files: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_ffree: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_favail: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_fsid: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_flag: %" LIBSSH_VFS_SIZE_MASK "\n" + "f_namemax: %" LIBSSH_VFS_SIZE_MASK "\n", statvfs->f_bsize, statvfs->f_frsize, statvfs->f_blocks, statvfs->f_bfree, statvfs->f_bavail, statvfs->f_files, @@ -1466,13 +1477,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); break; } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += sshc->readdir_len + 1; - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename, - sshc->readdir_len); } else { if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { @@ -1564,12 +1569,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_dyn_ptr(&sshc->readdir_buf), Curl_dyn_len(&sshc->readdir_buf)); - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf), - Curl_dyn_len(&sshc->readdir_buf)); - data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf); - } ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; @@ -1963,10 +1962,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) ssh_disconnect(sshc->ssh_session); if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, - explicitly mark it as closed with the memdebug macro. This libssh + tell the connection to forget about it. This libssh bug is fixed in 0.10.0. */ - fake_sclose(conn->sock[FIRSTSOCKET]); - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; + Curl_conn_forget_socket(data, FIRSTSOCKET); } SSH_STRING_FREE_CHAR(sshc->homedir); @@ -2959,4 +2957,10 @@ void Curl_ssh_version(char *buffer, size_t buflen) (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); } +#if defined(__GNUC__) && \ + (LIBSSH_VERSION_MINOR >= 10) || \ + (LIBSSH_VERSION_MAJOR > 0) +#pragma GCC diagnostic pop +#endif + #endif /* USE_LIBSSH */ diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index f539b39..11f5f4f 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -1537,139 +1537,137 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_SFTP_NEXT_QUOTE); break; } - { - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error command '%s', missing parameter", - cmd); + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error command '%s', missing parameter", + cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + break; + } + + /* + * SFTP is a binary protocol, so we don't send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh2 + * functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6) || + strncasecompare(cmd, "atime ", 6) || + strncasecompare(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in %s: Bad second parameter", cmd); + Curl_safefree(sshc->quote_path1); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + sshc->actualcode = result; break; } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + state(data, SSH_SFTP_QUOTE_STAT); + break; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); else - failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + failf(data, + "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } - - /* - * SFTP is a binary protocol, so we don't send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh2 - * functions. - */ - if(strncasecompare(cmd, "chgrp ", 6) || - strncasecompare(cmd, "chmod ", 6) || - strncasecompare(cmd, "chown ", 6) || - strncasecompare(cmd, "atime ", 6) || - strncasecompare(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - state(data, SSH_SFTP_QUOTE_STAT); - break; - } - if(strncasecompare(cmd, "ln ", 3) || - strncasecompare(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, - "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_SYMLINK); - break; - } - else if(strncasecompare(cmd, "mkdir ", 6)) { - /* create dir */ - state(data, SSH_SFTP_QUOTE_MKDIR); - break; - } - else if(strncasecompare(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_RENAME); - break; - } - else if(strncasecompare(cmd, "rmdir ", 6)) { - /* delete dir */ - state(data, SSH_SFTP_QUOTE_RMDIR); - break; - } - else if(strncasecompare(cmd, "rm ", 3)) { - state(data, SSH_SFTP_QUOTE_UNLINK); + state(data, SSH_SFTP_QUOTE_SYMLINK); + break; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(data, SSH_SFTP_QUOTE_MKDIR); + break; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; break; } + state(data, SSH_SFTP_QUOTE_RENAME); + break; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(data, SSH_SFTP_QUOTE_RMDIR); + break; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(data, SSH_SFTP_QUOTE_UNLINK); + break; + } #ifdef HAS_STATVFS_SUPPORT - else if(strncasecompare(cmd, "statvfs ", 8)) { - state(data, SSH_SFTP_QUOTE_STATVFS); - break; - } -#endif - - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(data, SSH_SFTP_QUOTE_STATVFS); break; } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; } - break; case SSH_SFTP_NEXT_QUOTE: Curl_safefree(sshc->quote_path1); @@ -1962,13 +1960,23 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } else if(rc == 0) { + #ifdef _MSC_VER + #define LIBSSH2_VFS_SIZE_MASK "I64u" + #else + #define LIBSSH2_VFS_SIZE_MASK "llu" + #endif char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", + "f_bsize: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_frsize: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_blocks: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_bfree: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_bavail: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_files: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_ffree: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_favail: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_fsid: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_flag: %" LIBSSH2_VFS_SIZE_MASK "\n" + "f_namemax: %" LIBSSH2_VFS_SIZE_MASK "\n", statvfs.f_bsize, statvfs.f_frsize, statvfs.f_blocks, statvfs.f_bfree, statvfs.f_bavail, statvfs.f_files, @@ -2341,14 +2349,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); break; } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += readdir_len + 1; - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename, - readdir_len); - Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1); } else { result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); @@ -2427,13 +2428,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_dyn_ptr(&sshp->readdir), Curl_dyn_len(&sshp->readdir)); - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&sshp->readdir), - Curl_dyn_len(&sshp->readdir)); - data->req.bytecount += Curl_dyn_len(&sshp->readdir); - } if(result) { Curl_dyn_free(&sshp->readdir); state(data, SSH_STOP); diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 1e1b137..ca0533a 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -267,6 +267,7 @@ void Curl_ssh_attach(struct Curl_easy *data, /* for non-SSH builds */ #define Curl_ssh_cleanup() #define Curl_ssh_attach(x,y) +#define Curl_ssh_init() 0 #endif #endif /* HEADER_CURL_SSH_H */ diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 39cee50..4da7e9d 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -343,9 +343,6 @@ static CURLcode wssh_setup_connection(struct Curl_easy *data, return CURLE_OK; } -static Curl_recv wscp_recv, wsftp_recv; -static Curl_send wscp_send, wsftp_send; - static int userauth(byte authtype, WS_UserAuthData* authdata, void *ctx) diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index 934149c..a6566f4 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -582,17 +582,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; const bool verifypeer = conn_config->verifypeer; const bool verifyhost = conn_config->verifyhost; CURLcode ret; unsigned version_min, version_max; int session_set = 0; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "connect_step1"); @@ -706,11 +701,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } - if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) -#ifdef ENABLE_IPV6 - || (1 == Curl_inet_pton(AF_INET6, hostname, &addr)) -#endif - ) { + if(connssl->peer.is_ip_address) { if(verifyhost) { failf(data, "BearSSL: " "host verification of IP address is not supported"); @@ -719,12 +710,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, hostname = NULL; } else { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { + if(!connssl->peer.sni) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } - hostname = snihost; + hostname = connssl->peer.sni; CURL_TRC_CF(data, cf, "connect_step1, SNI set"); } @@ -749,26 +739,26 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } -static int bearssl_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +static void bearssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - - if(sock == CURL_SOCKET_BAD) - return GETSOCK_BLANK; - else { - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); - if(state & BR_SSL_SENDREC) { - socks[0] = sock; - return GETSOCK_WRITESOCK(0); + if(!cf->connected) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); + + if(state & BR_SSL_SENDREC) { + Curl_pollset_set_out_only(data, ps, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + } } } - socks[0] = sock; - return GETSOCK_READSOCK(0); } static CURLcode bearssl_run_until(struct Curl_cfilter *cf, @@ -1210,7 +1200,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - bearssl_get_select_socks, /* getsock */ + bearssl_adjust_pollset, /* adjust_pollset */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index c538a96..4e337f5 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -402,18 +402,13 @@ set_ssl_version_min_max(struct Curl_easy *data, CURLcode gtls_client_init(struct Curl_easy *data, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, + struct ssl_peer *peer, struct gtls_instance *gtls, long *pverifyresult) { unsigned int init_flags; int rc; bool sni = TRUE; /* default is SNI enabled */ -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif const char *prioritylist; const char *err = NULL; const char *tls13support; @@ -460,50 +455,60 @@ CURLcode gtls_client_init(struct Curl_easy *data, } #endif - if(config->CAfile) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(gtls->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + if(config->verifypeer) { + bool imported_native_ca = false; - rc = gnutls_certificate_set_x509_trust_file(gtls->cred, - config->CAfile, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - config->CAfile, gnutls_strerror(rc)); - if(config->verifypeer) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; + if(ssl_config->native_ca_store) { + rc = gnutls_certificate_set_x509_system_trust(gtls->cred); + if(rc < 0) + infof(data, "error reading native ca store (%s), continuing anyway", + gnutls_strerror(rc)); + else { + infof(data, "found %d certificates in native ca store", rc); + if(rc > 0) + imported_native_ca = true; } } - else - infof(data, "found %d certificates in %s", rc, config->CAfile); - } - if(config->CApath) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, - config->CApath, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - config->CApath, gnutls_strerror(rc)); - if(config->verifypeer) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; + if(config->CAfile) { + /* set the trusted CA cert bundle file */ + gnutls_certificate_set_verify_flags(gtls->cred, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + + rc = gnutls_certificate_set_x509_trust_file(gtls->cred, + config->CAfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CAfile, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + *pverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } } + else + infof(data, "found %d certificates in %s", rc, config->CAfile); } - else - infof(data, "found %d certificates in %s", rc, config->CApath); - } -#ifdef CURL_CA_FALLBACK - /* use system ca certificate store as fallback */ - if(config->verifypeer && !(config->CAfile || config->CApath)) { - /* this ignores errors on purpose */ - gnutls_certificate_set_x509_system_trust(gtls->cred); + if(config->CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, + config->CApath, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CApath, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + *pverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CApath); + } } -#endif if(config->CRLfile) { /* set the CRL list file */ @@ -537,15 +542,9 @@ CURLcode gtls_client_init(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, - snihost, snilen) < 0) { + if(sni && peer->sni) { + if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, + peer->sni, strlen(peer->sni)) < 0) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -699,7 +698,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; result = gtls_client_init(data, conn_config, ssl_config, - connssl->hostname, + &connssl->peer, &backend->gtls, pverifyresult); if(result) return result; @@ -811,8 +810,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_session_t session, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, - const char *dispname, + struct ssl_peer *peer, const char *pinned_key) { unsigned int cert_list_size; @@ -1068,7 +1066,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ - rc = gnutls_x509_crt_check_hostname(x509_cert, hostname); + rc = gnutls_x509_crt_check_hostname(x509_cert, peer->hostname); #if GNUTLS_VERSION_NUMBER < 0x030306 /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP addresses. */ @@ -1081,10 +1079,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data, unsigned char addrbuf[sizeof(struct use_addr)]; size_t addrlen = 0; - if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0) + if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0) addrlen = 4; #ifdef ENABLE_IPV6 - else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0) + else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0) addrlen = 16; #endif @@ -1114,13 +1112,13 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(!rc) { if(config->verifyhost) { failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certname, dispname); + "target host name '%s'", certname, peer->dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, " common name: %s (does not match '%s')", - certname, dispname); + certname, peer->dispname); } else infof(data, " common name: %s (matched)", certname); @@ -1253,8 +1251,7 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, CURLcode result; result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config, - connssl->hostname, connssl->dispname, - pinned_key); + &connssl->peer, pinned_key); if(result) goto out; @@ -1662,7 +1659,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { gtls_cert_status_request, /* cert_status_request */ gtls_connect, /* connect */ gtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ gtls_get_internals, /* get_internals */ gtls_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index ac141e1..1a81c01 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h @@ -43,6 +43,7 @@ struct Curl_easy; struct Curl_cfilter; struct ssl_primary_config; struct ssl_config_data; +struct ssl_peer; struct gtls_instance { gnutls_session_t session; @@ -56,7 +57,7 @@ CURLcode gtls_client_init(struct Curl_easy *data, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, + struct ssl_peer *peer, struct gtls_instance *gtls, long *pverifyresult); @@ -65,8 +66,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_session_t session, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, - const char *hostname, - const char *dispname, + struct ssl_peer *peer, const char *pinned_key); extern const struct Curl_ssl Curl_ssl_gnutls; diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index d37bb18..fbcb25c 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c @@ -23,6 +23,11 @@ ***************************************************************************/ #include "curl_setup.h" +#if defined(USE_OPENSSL) || \ + defined(USE_WOLFSSL) || \ + (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ + defined(USE_QUICHE) + #include "keylog.h" #include <curl/curl.h> @@ -55,7 +60,7 @@ Curl_tls_keylog_open(void) if(keylog_file_name) { keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); if(keylog_file_fp) { -#ifdef WIN32 +#ifdef _WIN32 if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) #else if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) @@ -157,3 +162,5 @@ Curl_tls_keylog_write(const char *label, fputs(line, keylog_file_fp); return true; } + +#endif /* TLS or QUIC backend */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 2f994d7..38f7de7 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -322,7 +322,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; int ret = -1; char errorbuf[128]; @@ -639,9 +639,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } - { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { + + if(connssl->peer.sni) { + if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ @@ -1274,7 +1274,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { Curl_none_cert_status_request, /* cert_status_request */ mbedtls_connect, /* connect */ mbedtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ mbedtls_get_internals, /* get_internals */ mbedtls_close, /* close_one */ mbedtls_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c index bcb7106..22b1b22 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c @@ -51,7 +51,7 @@ int Curl_mbedtlsthreadlock_thread_setup(void) { int i; - mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1); + mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T)); if(!mutex_buf) return 0; /* error, no number of threads defined */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 15d84ed..ca6d931 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -79,6 +79,8 @@ #include <openssl/bio.h> #include <openssl/buffer.h> #include <openssl/pkcs12.h> +#include <openssl/tls1.h> +#include <openssl/evp.h> #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include <openssl/ocsp.h> @@ -96,6 +98,9 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS renegotiations when built with BoringSSL. Renegotiating is non-compliant @@ -173,8 +178,6 @@ #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) #define HAVE_EVP_PKEY_GET_PARAMS 1 -#else -#define SSL_get1_peer_certificate SSL_get_peer_certificate #endif #ifdef HAVE_EVP_PKEY_GET_PARAMS @@ -237,7 +240,11 @@ #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" #else -#define OSSL_PACKAGE "OpenSSL" +# if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +# define OSSL_PACKAGE "quictls" +# else +# define OSSL_PACKAGE "OpenSSL" +#endif #endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) @@ -546,9 +553,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #else RSA_get0_key(rsa, &n, &e, NULL); #endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", BN_num_bits(n)); + BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); #else - BIO_printf(mem, "%d", BN_num_bits(rsa->n)); + BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); #endif /* HAVE_OPAQUE_RSA_DSA_DH */ push_certinfo("RSA Public Key", i); print_pubkey_BN(rsa, n, i); @@ -2106,22 +2113,6 @@ 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 @@ -2144,10 +2135,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, This function is now used from ngtcp2 (QUIC) as well. */ -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, + struct ssl_peer *peer, X509 *server_cert) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2164,25 +2153,21 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, size_t hostlen; (void)conn; - hostlen = strlen(hostname); - -#ifndef ENABLE_IPV6 - /* Silence compiler warnings for unused params */ - (void) conn; -#endif - + hostlen = strlen(peer->hostname); + if(peer->is_ip_address) { #ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in6_addr); - } - else -#endif - if(Curl_inet_pton(AF_INET, hostname, &addr)) { + if(conn->bits.ipv6_ip && + Curl_inet_pton(AF_INET6, peer->hostname, &addr)) { target = GEN_IPADD; - addrlen = sizeof(struct in_addr); + addrlen = sizeof(struct in6_addr); } + else +#endif + if(Curl_inet_pton(AF_INET, peer->hostname, &addr)) { + target = GEN_IPADD; + addrlen = sizeof(struct in_addr); + } + } /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); @@ -2232,9 +2217,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, if((altlen == strlen(altptr)) && /* if this isn't true, there was an embedded zero in the name string and we cannot match it. */ - subj_alt_hostcheck(data, - altptr, - altlen, hostname, hostlen, dispname)) { + subj_alt_hostcheck(data, altptr, altlen, + peer->hostname, hostlen, + peer->dispname)) { dnsmatched = TRUE; } break; @@ -2246,7 +2231,7 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, ipmatched = TRUE; infof(data, " subjectAltName: host \"%s\" matched cert's IP address!", - dispname); + peer->dispname); } break; } @@ -2262,9 +2247,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, /* an alternative name matched */ ; else if(dNSName || iPAddress) { - infof(data, " subjectAltName does not match %s", dispname); + infof(data, " subjectAltName does not match %s", peer->dispname); failf(data, "SSL: no alternative certificate subject name matches " - "target host name '%s'", dispname); + "target host name '%s'", peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2328,9 +2313,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)peer_CN, - peerlen, hostname, hostlen)) { + peerlen, peer->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", peer_CN, dispname); + "target host name '%s'", peer_CN, peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2739,12 +2724,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, #ifdef USE_OPENSSL /* ====================================================== */ -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME -# define use_sni(x) sni = (x) -#else -# define use_sni(x) Curl_nop_stmt -#endif - /* Check for OpenSSL 1.0.2 which has ALPN support. */ #undef HAS_ALPN #if OPENSSL_VERSION_NUMBER >= 0x10002000L \ @@ -3048,6 +3027,151 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } +#if defined(USE_WIN32_CRYPTO) +static CURLcode import_windows_cert_store(struct Curl_easy *data, + const char *name, + X509_STORE *store, + bool *imported) +{ + CURLcode result = CURLE_OK; + HCERTSTORE hStore; + + *imported = false; + + hStore = CertOpenSystemStoreA(0, name); + 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]; +#endif + + 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); +#endif + 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)) + 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(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; + } + } + else + continue; + } + else + 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) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"", cert_name); +#endif + *imported = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + if(result) + return result; + } + + return result; +} +#endif + static CURLcode populate_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, X509_STORE *store) @@ -3077,140 +3201,25 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, 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]; -#endif - - 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); -#endif - 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)) - 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(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; - } - } - else - continue; - } - else - 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) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); -#endif - imported_native_ca = true; - } - X509_free(x509); - } - - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - + const char *storeNames[] = { + "ROOT", /* Trusted Root Certification Authorities */ + "CA" /* Intermediate Certification Authorities */ + }; + size_t i; + for(i = 0; i < ARRAYSIZE(storeNames); ++i) { + bool imported = false; + result = import_windows_cert_store(data, storeNames[i], store, + &imported); if(result) return result; + if(imported) { + infof(data, "successfully imported Windows %s store", storeNames[i]); + imported_native_ca = true; + } + else + infof(data, "error importing Windows %s store, continuing anyway", + storeNames[i]); } - 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) { @@ -3226,7 +3235,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, } if(ssl_cafile || ssl_capath) { -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) /* 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) { @@ -3355,6 +3364,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; X509_STORE *store = NULL; + DEBUGASSERT(multi); if(multi && multi->ssl_backend_data && multi->ssl_backend_data->store && @@ -3374,6 +3384,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; struct multi_ssl_backend_data *mbackend; + DEBUGASSERT(multi); if(!multi) return; @@ -3465,17 +3476,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); BIO *bio; - -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - bool sni; - const char *hostname = connssl->hostname; - -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif -#endif const long int ssl_version = conn_config->version; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; @@ -3510,7 +3510,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #else req_method = SSLv23_client_method(); #endif - use_sni(TRUE); break; case CURL_SSLVERSION_SSLv2: failf(data, "No SSLv2 support"); @@ -3803,13 +3802,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, backend->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) { + if(connssl->peer.sni) { + if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) { failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -3818,6 +3812,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_app_data(backend->handle, cf); + connssl->reused_session = FALSE; if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { @@ -3831,6 +3826,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } /* Informational message */ infof(data, "SSL reusing session ID"); + connssl->reused_session = TRUE; } Curl_ssl_sessionid_unlock(data); } @@ -3991,7 +3987,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->hostname, connssl->port); + connssl->peer.hostname, connssl->port); return result; } @@ -4002,13 +3998,28 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } } else { + int psigtype_nid = NID_undef; + const char *negotiated_group_name = NULL; + /* we connected fine, we're not waiting for anything else. */ connssl->connecting_state = ssl_connect_3; +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid); +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) + negotiated_group_name = SSL_get0_group_name(backend->handle); +#else + negotiated_group_name = + OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF); +#endif +#endif + /* Informational message */ - infof(data, "SSL connection using %s / %s", + infof(data, "SSL connection using %s / %s / %s / %s", SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle)); + SSL_get_cipher(backend->handle), + negotiated_group_name? negotiated_group_name : "[blank]", + OBJ_nid2sn(psigtype_nid)); #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -4085,6 +4096,75 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, return result; } +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_IS_AWSLC) && \ + !defined(CURL_DISABLE_VERBOSE_STRINGS) +static void infof_certstack(struct Curl_easy *data, const SSL *ssl) +{ + STACK_OF(X509) *certstack; + long verify_result; + int num_cert_levels; + int cert_level; + + verify_result = SSL_get_verify_result(ssl); + if(verify_result != X509_V_OK) + certstack = SSL_get_peer_cert_chain(ssl); + else + certstack = SSL_get0_verified_chain(ssl); + num_cert_levels = sk_X509_num(certstack); + + for(cert_level = 0; cert_level < num_cert_levels; cert_level++) { + char cert_algorithm[80] = ""; + char group_name_final[80] = ""; + const X509_ALGOR *palg_cert = NULL; + const ASN1_OBJECT *paobj_cert = NULL; + X509 *current_cert; + EVP_PKEY *current_pkey; + int key_bits; + int key_sec_bits; + int get_group_name; + const char *type_name; + + current_cert = sk_X509_value(certstack, cert_level); + + X509_get0_signature(NULL, &palg_cert, current_cert); + X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert); + OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0); + + current_pkey = X509_get0_pubkey(current_cert); + key_bits = EVP_PKEY_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define EVP_PKEY_get_security_bits EVP_PKEY_security_bits +#endif + key_sec_bits = EVP_PKEY_get_security_bits(current_pkey); +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + { + char group_name[80] = ""; + get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name, + sizeof(group_name), NULL); + msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name); + } + type_name = EVP_PKEY_get0_type_name(current_pkey); +#else + get_group_name = 0; + type_name = NULL; +#endif + + infof(data, + " Certificate level %d: " + "Public key type %s%s (%d/%d Bits/secBits), signed using %s", + cert_level, type_name ? type_name : "?", + get_group_name == 0 ? "" : group_name_final, + key_bits, key_sec_bits, cert_algorithm); + } +} +#else +#define infof_certstack(data, ssl) +#endif + /* * Get the server cert, verify it and show it, etc., only call failf() if the * 'strict' argument is TRUE as otherwise all this is for informational @@ -4163,8 +4243,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = ossl_verifyhost(data, conn, backend->server_cert, - connssl->hostname, connssl->dispname); + result = Curl_ossl_verifyhost(data, conn, &connssl->peer, + backend->server_cert); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4274,9 +4354,12 @@ static CURLcode servercert(struct Curl_cfilter *cf, infof(data, " SSL certificate verify ok."); } + infof_certstack(data, backend->handle); + #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus) { + if(conn_config->verifystatus && !connssl->reused_session) { + /* don't do this after Session ID reuse */ result = verifystatus(cf, data); if(result) { X509_free(backend->server_cert); @@ -4538,22 +4621,9 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, case SSL_ERROR_SSL: { /* A failure in the SSL library occurred, usually a protocol error. The OpenSSL error queue contains more information on the error. */ - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; sslerror = ERR_get_error(); - if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && - ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - connssl->state == ssl_connection_complete && - (connssl_next && connssl_next->state == ssl_connection_complete) - ) { - char ver[120]; - (void)ossl_version(ver, sizeof(ver)); - failf(data, "Error: %s does not support double SSL tunneling.", ver); - } - else - failf(data, "SSL_write() error: %s", - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); + failf(data, "SSL_write() error: %s", + ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); *curlcode = CURLE_SEND_ERROR; rc = -1; goto out; @@ -4858,7 +4928,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_cert_status_request, /* cert_status_request */ ossl_connect, /* connect */ ossl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks,/* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ ossl_get_internals, /* get_internals */ ossl_close, /* close_one */ ossl_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index 950faab..e802363 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h @@ -31,24 +31,21 @@ * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c */ +#include <openssl/ossl_typ.h> #include <openssl/ssl.h> #include "urldata.h" -/* - * In an effort to avoid using 'X509 *' here, we instead use the struct - * x509_st version of the type so that we can forward-declare it here without - * having to include <openssl/x509v3.h>. Including that header causes name - * conflicts when libcurl is built with both Schannel and OpenSSL support. - */ -struct x509_st; +#if (OPENSSL_VERSION_NUMBER < 0x30000000L) +#define SSL_get1_peer_certificate SSL_get_peer_certificate +#endif + CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - struct x509_st *server_cert); + struct ssl_peer *peer, X509 *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; -struct ssl_ctx_st; CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, - struct ssl_ctx_st *ctx, char *cert_file, + SSL_CTX *ctx, char *cert_file, const struct curl_blob *cert_blob, const char *cert_type, char *key_file, const struct curl_blob *key_blob, @@ -65,5 +62,9 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, SSL_CTX *ssl_ctx); +CURLcode Curl_ossl_ctx_configure(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 a3e9d96..8751fd9 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -39,6 +39,7 @@ #include "select.h" #include "strerror.h" #include "multiif.h" +#include "connect.h" /* for the connect timeout */ struct rustls_ssl_backend_data { @@ -75,14 +76,6 @@ cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) return backend->data_pending; } -static CURLcode -cr_connect(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) -{ - infof(data, "rustls_connect: unimplemented"); - return CURLE_SSL_CONNECT_ERROR; -} - struct io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; @@ -386,7 +379,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->hostname; + const char *hostname = connssl->peer.hostname; char errorbuf[256]; size_t errorlen; int result; @@ -458,12 +451,11 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "rustls: failed to get SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = rustls_client_connection_new(backend->config, snihost, &rconn); + /* rustls claims to manage ip address hostnames as well here. So, + * if we have an SNI, we use it, otherwise we pass the hostname */ + char *server = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + result = rustls_client_connection_new(backend->config, server, &rconn); } if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); @@ -486,9 +478,20 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, Curl_alpn_set_negotiated(cf, data, protocol, len); } +/* Given an established network connection, do a TLS handshake. + * + * If `blocking` is true, this function will block until the handshake is + * complete. Otherwise it will return as soon as I/O would block. + * + * For the non-blocking I/O case, this function will set `*done` to true + * once the handshake is complete. This function never reads the value of + * `*done*`. + */ static CURLcode -cr_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +cr_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, + bool *done) { struct ssl_connect_data *const connssl = cf->ctx; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); @@ -502,6 +505,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, bool wants_write; curl_socket_t writefd; curl_socket_t readfd; + timediff_t timeout_ms; + timediff_t socket_check_timeout; DEBUGASSERT(backend); @@ -539,12 +544,29 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, writefd = wants_write?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD; - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0); + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "rustls: operation timed out before socket check"); + return CURLE_OPERATION_TIMEDOUT; + } + + socket_check_timeout = blocking?timeout_ms:0; + + what = Curl_socket_check( + readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } + if(blocking && 0 == what) { + failf(data, "rustls connection timeout after %d ms", + socket_check_timeout); + return CURLE_OPERATION_TIMEDOUT; + } if(0 == what) { infof(data, "Curl_socket_check: %s would block", wants_read&&wants_write ? "writing and reading" : @@ -589,32 +611,43 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, DEBUGASSERT(false); } -/* returns a bitmap of flags for this connection's first socket indicating - whether we want to read or write */ -static int -cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +static CURLcode +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 = Curl_conn_cf_get_socket(cf, data); - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; - struct rustls_connection *rconn = NULL; + return cr_connect_common(cf, data, false, done); +} - (void)data; - DEBUGASSERT(backend); - rconn = backend->conn; +static CURLcode +cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM, + struct Curl_easy *data UNUSED_PARAM) +{ + bool done; /* unused */ + return cr_connect_common(cf, data, true, &done); +} - if(rustls_connection_wants_write(rconn)) { - socks[0] = sockfd; - return GETSOCK_WRITESOCK(0); - } - if(rustls_connection_wants_read(rconn)) { - socks[0] = sockfd; - return GETSOCK_READSOCK(0); +static void cr_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + if(!cf->connected) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + struct ssl_connect_data *const connssl = cf->ctx; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; + struct rustls_connection *rconn = NULL; + + (void)data; + DEBUGASSERT(backend); + rconn = backend->conn; + + if(rustls_connection_wants_write(rconn)) { + Curl_pollset_add_out(data, ps, sock); + } + if(rustls_connection_wants_read(rconn)) { + Curl_pollset_add_in(data, ps, sock); + } } - - return GETSOCK_BLANK; } static void * @@ -675,9 +708,9 @@ const struct Curl_ssl Curl_ssl_rustls = { cr_data_pending, /* data_pending */ Curl_none_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ - cr_connect, /* connect */ + cr_connect_blocking, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ - cr_get_select_socks, /* get_select_socks */ + cr_adjust_pollset, /* adjust_pollset */ cr_get_internals, /* get_internals */ cr_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 410a5c4..ae7f295 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -1063,17 +1063,12 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif SECURITY_STATUS sspi_status = SEC_E_OK; struct Curl_schannel_cred *old_cred = NULL; - struct in_addr addr; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif CURLcode result; - const char *hostname = connssl->hostname; DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { @@ -1154,22 +1149,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } + snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; } /* Warn if SNI is disabled due to use of an IP address */ - if(Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { + if(connssl->peer.is_ip_address) { infof(data, "schannel: using IP address, SNI is not supported by OS."); } @@ -1346,7 +1333,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1700,7 +1687,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->hostname, connssl->port)); + connssl->peer.hostname, connssl->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -2498,7 +2485,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf, if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->hostname, connssl->port); + connssl->peer.hostname, connssl->port); } if(backend->cred && backend->ctxt) { @@ -2754,6 +2741,151 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, return &backend->ctxt->ctxt_handle; } +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + const struct ssl_general_config *cfg = &data->set.general_ssl; + timediff_t timeout_ms; + timediff_t elapsed_ms; + struct curltime now; + unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; + + DEBUGASSERT(multi); + + if(!multi || !multi->ssl_backend_data) { + return NULL; + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + if(!mbackend->cert_store) { + return NULL; + } + + /* zero ca_cache_timeout completely disables caching */ + if(!cfg->ca_cache_timeout) { + return NULL; + } + + /* check for cache timeout by using the cached_x509_store_expired timediff + calculation pattern from openssl.c. + negative timeout means retain forever. */ + timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + if(timeout_ms >= 0) { + now = Curl_now(); + elapsed_ms = Curl_timediff(now, mbackend->time); + if(elapsed_ms >= timeout_ms) { + return NULL; + } + } + + if(ca_info_blob) { + if(!mbackend->CAinfo_blob_digest) { + return NULL; + } + if(mbackend->CAinfo_blob_size != ca_info_blob->len) { + return NULL; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + if(memcmp(mbackend->CAinfo_blob_digest, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) { + return NULL; + } + } + else { + if(!conn_config->CAfile || !mbackend->CAfile || + strcmp(mbackend->CAfile, conn_config->CAfile)) { + return NULL; + } + } + + return mbackend->cert_store; +} + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + struct schannel_multi_ssl_backend_data *mbackend; + unsigned char *CAinfo_blob_digest = NULL; + size_t CAinfo_blob_size = 0; + char *CAfile = NULL; + + DEBUGASSERT(multi); + + if(!multi) { + return false; + } + + if(!multi->ssl_backend_data) { + multi->ssl_backend_data = + calloc(1, sizeof(struct schannel_multi_ssl_backend_data)); + if(!multi->ssl_backend_data) { + return false; + } + } + + mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; + + + if(ca_info_blob) { + CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH); + if(!CAinfo_blob_digest) { + return false; + } + schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + CAinfo_blob_digest, + CURL_SHA256_DIGEST_LENGTH); + CAinfo_blob_size = ca_info_blob->len; + } + else { + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + return false; + } + } + } + + /* free old cache data */ + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + + mbackend->time = Curl_now(); + mbackend->cert_store = cert_store; + mbackend->CAinfo_blob_digest = CAinfo_blob_digest; + mbackend->CAinfo_blob_size = CAinfo_blob_size; + mbackend->CAfile = CAfile; + return true; +} + +static void schannel_free_multi_ssl_backend_data( + struct multi_ssl_backend_data *msbd) +{ + struct schannel_multi_ssl_backend_data *mbackend = + (struct schannel_multi_ssl_backend_data*)msbd; + if(mbackend->cert_store) { + CertCloseStore(mbackend->cert_store, 0); + } + free(mbackend->CAinfo_blob_digest); + free(mbackend->CAfile); + free(mbackend); +} + const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ @@ -2777,7 +2909,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_cert_status_request, /* cert_status_request */ schannel_connect, /* connect */ schannel_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ schannel_get_internals, /* get_internals */ schannel_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -2789,7 +2921,7 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ + schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ }; diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index a128e04..fe7450d 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h @@ -149,5 +149,22 @@ struct schannel_ssl_backend_data { #endif }; +struct schannel_multi_ssl_backend_data { + unsigned char *CAinfo_blob_digest; /* CA info blob digest */ + size_t CAinfo_blob_size; /* CA info blob size */ + char *CAfile; /* CAfile path used to generate + certificate store */ + HCERTSTORE cert_store; /* cached certificate store or + NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data); + +bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + HCERTSTORE cert_store); + #endif /* USE_SCHANNEL */ #endif /* HEADER_CURL_SCHANNEL_INT_H */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index a5d5c98..e7c8bc6 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -470,7 +470,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, CERT_CONTEXT *pCertContextServer = NULL; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->hostname; + const char *conn_hostname = connssl->peer.hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -600,6 +600,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, const CERT_CHAIN_CONTEXT *pChainContext = NULL; HCERTCHAINENGINE cert_chain_engine = NULL; HCERTSTORE trust_store = NULL; + HCERTSTORE own_trust_store = NULL; DEBUGASSERT(BACKEND); @@ -630,31 +631,46 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, result = CURLE_SSL_CACERT_BADFILE; } else { - /* Open the certificate store */ - trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, - 0, - (HCRYPTPROV)NULL, - CERT_STORE_CREATE_NEW_FLAG, - NULL); - if(!trust_store) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: failed to create certificate store: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; + /* try cache */ + trust_store = Curl_schannel_get_cached_cert_store(cf, data); + + if(trust_store) { + infof(data, "schannel: reusing certificate store from cache"); } else { - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - if(ca_info_blob) { - result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); + /* Open the certificate store */ + trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, + 0, + (HCRYPTPROV)NULL, + CERT_STORE_CREATE_NEW_FLAG, + NULL); + if(!trust_store) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: failed to create certificate store: %s", + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; } else { - result = add_certs_file_to_store(trust_store, - conn_config->CAfile, - data); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + own_trust_store = trust_store; + + if(ca_info_blob) { + result = add_certs_data_to_store(trust_store, + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); + } + else { + result = add_certs_file_to_store(trust_store, + conn_config->CAfile, + data); + } + if(result == CURLE_OK) { + if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { + own_trust_store = NULL; + } + } } } } @@ -754,8 +770,8 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, CertFreeCertificateChainEngine(cert_chain_engine); } - if(trust_store) { - CertCloseStore(trust_store, 0); + if(own_trust_store) { + CertCloseStore(own_trust_store, 0); } if(pChainContext) diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 3378f76..0a22ff6 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -46,8 +46,10 @@ #endif /* __clang__ */ #ifdef __GNUC__ +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Wundef" +#pragma GCC diagnostic ignored "-Wunreachable-code" #endif #include <limits.h> @@ -1013,7 +1015,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, } else { size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; - cbuf = calloc(cbuf_size, 1); + cbuf = calloc(1, cbuf_size); if(cbuf) { if(!CFStringGetCString(c, cbuf, cbuf_size, kCFStringEncodingUTF8)) { @@ -1651,11 +1653,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; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif /* ENABLE_IPV6 */ char *ciphers; OSStatus err = noErr; #if CURL_BUILD_MAC @@ -2003,13 +2000,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ if(conn_config->verifyhost) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen); + char *server = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server)); if(err != noErr) { failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d", @@ -2017,11 +2010,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if((Curl_inet_pton(AF_INET, connssl->hostname, &addr)) - #ifdef ENABLE_IPV6 - || (Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) - #endif - ) { + if(connssl->peer.is_ip_address) { infof(data, "WARNING: using IP address, SNI is being disabled by " "the OS."); } @@ -2079,7 +2068,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, ssl_sessionid = aprintf("%s:%d:%d:%s:%d", ssl_cafile ? ssl_cafile : "(blob memory)", - verifypeer, conn_config->verifyhost, connssl->hostname, + verifypeer, conn_config->verifyhost, connssl->peer.hostname, connssl->port); ssl_sessionid_len = strlen(ssl_sessionid); @@ -2665,7 +2654,7 @@ check_handshake: host name: */ case errSSLHostNameMismatch: failf(data, "SSL certificate peer verification failed, the " - "certificate did not match \"%s\"\n", connssl->dispname); + "certificate did not match \"%s\"\n", connssl->peer.dispname); return CURLE_PEER_FAILED_VERIFICATION; /* Problem with SSL / TLS negotiation */ @@ -2757,7 +2746,7 @@ check_handshake: default: /* May also return codes listed in Security Framework Result Codes */ failf(data, "Unknown SSL protocol error in connection to %s:%d", - connssl->hostname, err); + connssl->peer.hostname, err); break; } return CURLE_SSL_CONNECT_ERROR; @@ -3415,7 +3404,6 @@ again: } *curlcode = CURLE_AGAIN; return -1L; - break; /* errSSLClosedGraceful - server gracefully shut down the SSL session errSSLClosedNoNotify - server hung up on us instead of sending a @@ -3425,7 +3413,6 @@ again: case errSSLClosedNoNotify: *curlcode = CURLE_OK; return 0; - break; /* The below is errSSLPeerAuthCompleted; it's not defined in Leopard's headers */ @@ -3445,7 +3432,6 @@ again: failf(data, "SSLRead() return error %d", err); *curlcode = CURLE_RECV_ERROR; return -1L; - break; } } return (ssize_t)processed; @@ -3483,7 +3469,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { Curl_none_cert_status_request, /* cert_status_request */ sectransp_connect, /* connect */ sectransp_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -3500,6 +3486,10 @@ const struct Curl_ssl Curl_ssl_sectransp = { sectransp_send, /* send data to encrypt */ }; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 494b660..34eda3e 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -67,6 +67,7 @@ #include "warnless.h" #include "curl_base64.h" #include "curl_printf.h" +#include "inet_pton.h" #include "strdup.h" /* The last #include files should be: */ @@ -131,9 +132,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second) } #ifdef USE_SSL -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 }; @@ -147,51 +145,83 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) { if(!use_alpn) return NULL; - if(httpwant == CURL_HTTP_VERSION_1_0) - return &ALPN_SPEC_H10; #ifdef USE_HTTP2 if(httpwant >= CURL_HTTP_VERSION_2) return &ALPN_SPEC_H2_H11; +#else + (void)httpwant; #endif + /* Use the ALPN protocol "http/1.1" for HTTP/1.x. + Avoid "http/1.0" because some servers don't support it. */ return &ALPN_SPEC_H11; } #endif /* USE_SSL */ -bool -Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle) -{ - if((data->version == needle->version) && - (data->version_max == needle->version_max) && - (data->ssl_options == needle->ssl_options) && - (data->verifypeer == needle->verifypeer) && - (data->verifyhost == needle->verifyhost) && - (data->verifystatus == needle->verifystatus) && - blobcmp(data->cert_blob, needle->cert_blob) && - blobcmp(data->ca_info_blob, needle->ca_info_blob) && - blobcmp(data->issuercert_blob, needle->issuercert_blob) && - Curl_safecmp(data->CApath, needle->CApath) && - Curl_safecmp(data->CAfile, needle->CAfile) && - Curl_safecmp(data->issuercert, needle->issuercert) && - Curl_safecmp(data->clientcert, needle->clientcert) && +void Curl_ssl_easy_config_init(struct Curl_easy *data) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + data->set.ssl.primary.verifypeer = TRUE; + data->set.ssl.primary.verifyhost = TRUE; + data->set.ssl.primary.sessionid = TRUE; /* session ID caching by default */ +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl = data->set.ssl; +#endif +} + +static bool +match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) +{ + (void)data; + if((c1->version == c2->version) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && #ifdef USE_TLS_SRP - !Curl_timestrcmp(data->username, needle->username) && - !Curl_timestrcmp(data->password, needle->password) && + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && #endif - strcasecompare(data->cipher_list, needle->cipher_list) && - strcasecompare(data->cipher_list13, needle->cipher_list13) && - strcasecompare(data->curves, needle->curves) && - strcasecompare(data->CRLfile, needle->CRLfile) && - strcasecompare(data->pinned_key, needle->pinned_key)) + strcasecompare(c1->cipher_list, c2->cipher_list) && + strcasecompare(c1->cipher_list13, c2->cipher_list13) && + strcasecompare(c1->curves, c2->curves) && + strcasecompare(c1->CRLfile, c2->CRLfile) && + strcasecompare(c1->pinned_key, c2->pinned_key)) return TRUE; return FALSE; } -bool -Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) { dest->version = source->version; dest->version_max = source->version_max; @@ -221,7 +251,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, return TRUE; } -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) { Curl_safefree(sslc->CApath); Curl_safefree(sslc->CAfile); @@ -241,6 +271,111 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) #endif } +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) +{ + data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; + data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + data->set.ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST]; + data->set.ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST]; + data->set.ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; + data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; +#ifdef USE_TLS_SRP + data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; + data->set.ssl.key = data->set.str[STRING_KEY]; + data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; + data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; + data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; + data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; + +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + data->set.proxy_ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + data->set.proxy_ssl.primary.cipher_list13 = + data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + data->set.proxy_ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + data->set.proxy_ssl.primary.ca_info_blob = + data->set.blobs[BLOB_CAINFO_PROXY]; + data->set.proxy_ssl.primary.issuercert = + data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.issuercert_blob = + data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + data->set.proxy_ssl.primary.CRLfile = + data->set.str[STRING_SSL_CRLFILE_PROXY]; + data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; + data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; + data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + data->set.proxy_ssl.primary.username = + data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + data->set.proxy_ssl.primary.password = + data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; +#endif +#endif /* CURL_DISABLE_PROXY */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the esay handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_free_primary_ssl_config(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_free_primary_ssl_config(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} + #ifdef USE_SSL static int multissl_setup(const struct Curl_ssl *backend); #endif @@ -432,7 +567,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, if(!check->sessionid) /* not session ID means blank entry */ continue; - if(strcasecompare(connssl->hostname, check->name) && + if(strcasecompare(connssl->peer.hostname, check->name) && ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || (cf->conn->bits.conn_to_host && check->conn_to_host && strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && @@ -441,7 +576,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, cf->conn->conn_to_port == check->conn_to_port)) && (connssl->port == check->remote_port) && strcasecompare(cf->conn->handler->scheme, check->scheme) && - Curl_ssl_config_matches(conn_config, &check->ssl_config)) { + match_ssl_primary_config(data, conn_config, &check->ssl_config)) { /* yes, we have a session ID! */ (*general_age)++; /* increase general age */ check->age = *general_age; /* set this as used in this age */ @@ -456,7 +591,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, connssl->hostname, connssl->port)); + cf->conn->handler->scheme, connssl->peer.hostname, + connssl->port)); return no_match; } @@ -532,7 +668,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, (void)ssl_config; DEBUGASSERT(ssl_config->primary.sessionid); - clone_host = strdup(connssl->hostname); + clone_host = strdup(connssl->peer.hostname); if(!clone_host) return CURLE_OUT_OF_MEMORY; /* bail out */ @@ -590,7 +726,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store->remote_port = connssl->port; store->scheme = cf->conn->handler->scheme; - if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) { + if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { Curl_free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ free(clone_host); @@ -629,22 +765,21 @@ void Curl_ssl_close_all(struct Curl_easy *data) Curl_ssl->close_all(data); } -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps) { - struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - - if(sock == CURL_SOCKET_BAD) - return GETSOCK_BLANK; - - if(connssl->connecting_state == ssl_connect_2_writing) { - /* we are only interested in writing */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); + if(!cf->connected) { + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + if(sock != CURL_SOCKET_BAD) { + if(connssl->connecting_state == ssl_connect_2_writing) { + Curl_pollset_set_out_only(data, ps, sock); + } + else { + Curl_pollset_set_in_only(data, ps, sock); + } + } } - socks[0] = sock; - return GETSOCK_READSOCK(0); } /* Selects an SSL crypto engine @@ -786,32 +921,6 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, } /* - * Curl_ssl_snihost() converts the input host name to a suitable SNI name put - * in data->state.buffer. Returns a pointer to the name (or NULL if a problem) - * and stores the new length in 'olen'. - * - * SNI fields must not have any trailing dot and while RFC 6066 section 3 says - * the SNI field is case insensitive, browsers always send the data lowercase - * and subsequently there are numerous servers out there that don't work - * unless the name is lowercased. - */ - -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen) -{ - size_t len = strlen(host); - if(len && (host[len-1] == '.')) - len--; - if(len >= data->set.buffer_size) - return NULL; - - Curl_strntolower(data->state.buffer, host, len); - data->state.buffer[len] = 0; - if(olen) - *olen = len; - return data->state.buffer; -} - -/* * Public key pem to der conversion */ @@ -1156,13 +1265,13 @@ static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf, return Curl_ssl->connect_nonblocking(cf, data, done); } -static int multissl_get_select_socks(struct Curl_cfilter *cf, +static void multissl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { if(multissl_setup(NULL)) - return 0; - return Curl_ssl->get_select_socks(cf, data, socks); + return; + Curl_ssl->adjust_pollset(cf, data, ps); } static void *multissl_get_internals(struct ssl_connect_data *connssl, @@ -1214,7 +1323,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_none_cert_status_request, /* cert_status_request */ multissl_connect, /* connect */ multissl_connect_nonblocking, /* connect_nonblocking */ - multissl_get_select_socks, /* getsock */ + multissl_adjust_pollset, /* adjust_pollset */ multissl_get_internals, /* get_internals */ multissl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1409,12 +1518,14 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #ifdef USE_SSL -static void free_hostname(struct ssl_connect_data *connssl) +void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - if(connssl->dispname != connssl->hostname) - free(connssl->dispname); - free(connssl->hostname); - connssl->hostname = connssl->dispname = NULL; + if(peer->dispname != peer->hostname) + free(peer->dispname); + free(peer->sni); + free(peer->hostname); + peer->hostname = peer->sni = peer->dispname = NULL; + peer->is_ip_address = FALSE; } static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1423,12 +1534,26 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl) { Curl_ssl->close(cf, data); connssl->state = ssl_connection_none; - free_hostname(connssl); + Curl_ssl_peer_cleanup(&connssl->peer); } cf->connected = FALSE; } -static CURLcode reinit_hostname(struct Curl_cfilter *cf) +static int is_ip_address(const char *hostname) +{ +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + return (hostname && hostname[0] && (Curl_inet_pton(AF_INET, hostname, &addr) +#ifdef ENABLE_IPV6 + || Curl_inet_pton(AF_INET6, hostname, &addr) +#endif + )); +} + +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf) { struct ssl_connect_data *connssl = cf->ctx; const char *ehostname, *edispname; @@ -1454,23 +1579,43 @@ static CURLcode reinit_hostname(struct Curl_cfilter *cf) } /* 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); + if(ehostname && (!peer->hostname + || strcmp(ehostname, peer->hostname))) { + Curl_ssl_peer_cleanup(peer); + peer->hostname = strdup(ehostname); + if(!peer->hostname) { + Curl_ssl_peer_cleanup(peer); return CURLE_OUT_OF_MEMORY; } if(!edispname || !strcmp(ehostname, edispname)) - connssl->dispname = connssl->hostname; + peer->dispname = peer->hostname; else { - connssl->dispname = strdup(edispname); - if(!connssl->dispname) { - free_hostname(connssl); + peer->dispname = strdup(edispname); + if(!peer->dispname) { + Curl_ssl_peer_cleanup(peer); return CURLE_OUT_OF_MEMORY; } } + + peer->sni = NULL; + peer->is_ip_address = is_ip_address(peer->hostname)? TRUE : FALSE; + if(peer->hostname[0] && !peer->is_ip_address) { + /* not an IP address, normalize according to RCC 6066 ch. 3, + * max len of SNI is 2^16-1, no trailing dot */ + size_t len = strlen(peer->hostname); + if(len && (peer->hostname[len-1] == '.')) + len--; + if(len < USHRT_MAX) { + peer->sni = calloc(1, len + 1); + if(!peer->sni) { + Curl_ssl_peer_cleanup(peer); + return CURLE_OUT_OF_MEMORY; + } + Curl_strntolower(peer->sni, peer->hostname, len); + peer->sni[len] = 0; + } + } + } connssl->port = eport; return CURLE_OK; @@ -1525,7 +1670,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, goto out; *done = FALSE; - result = reinit_hostname(cf); + result = Curl_ssl_peer_init(&connssl->peer, cf); if(result) goto out; @@ -1599,22 +1744,17 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, return nread; } -static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, +static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks) + struct easy_pollset *ps) { struct cf_call_data save; - int fds = GETSOCK_BLANK; - if(!cf->next->connected) { - fds = cf->next->cft->get_select_socks(cf->next, data, socks); - } - else if(!cf->connected) { + if(!cf->connected) { CF_DATA_SAVE(save, cf, data); - fds = Curl_ssl->get_select_socks(cf, data, socks); + Curl_ssl->adjust_pollset(cf, data, ps); CF_DATA_RESTORE(cf, save); } - return fds; } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, @@ -1705,7 +1845,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, - ssl_cf_get_select_socks, + ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, @@ -1723,7 +1863,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, - ssl_cf_get_select_socks, + ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, @@ -1837,6 +1977,16 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option) return (Curl_ssl->supports & option)? TRUE : FALSE; } +static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) + return cf; + } + return NULL; +} + + void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n) { @@ -1844,8 +1994,8 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, (void)n; if(data->conn) { struct Curl_cfilter *cf; - /* get first filter in chain, if any is present */ - cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]); + /* get first SSL filter in chain, if any is present */ + cf = get_ssl_filter(data->conn->cfilter[sockindex]); if(cf) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); @@ -1875,23 +2025,6 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, return result; } -static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf, *lowest_ssl_cf = NULL; - - for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) { - 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 */ - return cf; - } - } - } - return lowest_ssl_cf; -} - bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) { return (cf->cft == &Curl_cft_ssl_proxy); @@ -1908,17 +2041,6 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) #endif } -struct ssl_config_data * -Curl_ssl_get_config(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - (void)data; - DEBUGASSERT(data->conn); - cf = get_ssl_cf_engaged(data->conn, sockindex); - return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl; -} - struct ssl_primary_config * Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) { @@ -1930,15 +2052,6 @@ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) #endif } -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf) -{ - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) - return cf; - } - return NULL; -} - CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, const struct alpn_spec *spec) { @@ -2005,10 +2118,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { *palpn = 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)) { - *palpn = CURL_HTTP_VERSION_1_0; - } #ifdef USE_HTTP2 else if(proto_len == ALPN_H2_LENGTH && !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 8ad1cf6..f1856bd 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -65,15 +65,54 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); -bool Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle); -bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest); -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); - curl_sslbackend Curl_ssl_backend(void); +/** + * Init ssl config for a new easy handle. + */ +void Curl_ssl_easy_config_init(struct Curl_easy *data); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `conn` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * been changed on the easy handle. Will work for `verifypeer`, + * `verifyhost` and `verifystatus`. */ +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); + +/** + * Init SSL peer information for filter. Can be called repeatedly. + */ +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf); +/** + * Free all allocated data and reset peer information. + */ +void Curl_ssl_peer_cleanup(struct ssl_peer *peer); + #ifdef USE_SSL int Curl_ssl_init(void); void Curl_ssl_cleanup(void); @@ -160,18 +199,6 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, #endif /* !CURL_DISABLE_PROXY */ /** - * Get the SSL configuration that is used on the connection. - * This returns NULL if no SSL is configured. - * Otherwise it returns the config of the first (highest) one that is - * either connected, in handshake or about to start - * (e.g. all filters below it are connected). If SSL filters are present, - * but neither can start operating, return the config of the lowest one - * that will first come into effect when connecting. - */ -struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data, - int sockindex); - -/** * True iff the underlying SSL implementation supports the option. * Option is one of the defined SSLSUPP_* values. * `data` maybe NULL for the features of the default implementation. @@ -188,6 +215,18 @@ 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); +/** + * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. + */ +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data); + +/** + * Get the primary config relevant for the filter from its connection. + */ +struct ssl_primary_config * + Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); + extern struct Curl_cftype Curl_cft_ssl; extern struct Curl_cftype Curl_cft_ssl_proxy; @@ -209,8 +248,9 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_ssl_get_config(a,b) NULL #define Curl_ssl_cfilter_remove(a,b) CURLE_OK +#define Curl_ssl_cf_get_config(a,b) NULL +#define Curl_ssl_cf_get_primary_config(a) NULL #endif #endif /* HEADER_CURL_VTLS_H */ diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index a6e4544..af7ae55 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h @@ -32,8 +32,6 @@ /* 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 @@ -70,14 +68,14 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct ssl_connect_data { ssl_connection_state state; ssl_connect_state connecting_state; - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ + struct ssl_peer peer; const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ int port; /* remote port at origin */ BIT(use_alpn); /* if ALPN shall be used in handshake */ + BIT(reused_session); /* session-ID was reused for this */ }; @@ -118,14 +116,11 @@ struct Curl_ssl { struct Curl_easy *data, bool *done); - /* If the SSL backend wants to read or write on this connection during a - handshake, set socks[0] to the connection's FIRSTSOCKET, and return - a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or - GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. - Mandatory. */ - int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - + /* During handshake, adjust the pollset to include the socket + * for POLLOUT or POLLIN as needed. + * Mandatory. */ + void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); void (*close_all)(struct Curl_easy *data); @@ -169,25 +164,8 @@ CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); CURLcode Curl_none_set_engine_default(struct Curl_easy *data); struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); bool Curl_none_false_start(void); -int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, - curl_socket_t *socks); - -/** - * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. - */ -struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, - struct Curl_easy *data); - -/** - * Get the primary config relevant for the filter from its connection. - */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); - -/** - * Get the first SSL filter in the chain starting with `cf`, or NULL. - */ -struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf); +void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, + struct easy_pollset *ps); /** * Get the SSL filter below the given one or NULL if there is none. diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index b1384a6..5890bb6 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -480,6 +480,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } #endif + default: break; } @@ -513,7 +514,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } -#ifndef NO_FILESYSTEM +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) /* load native CA certificates */ if(ssl_config->native_ca_store) { if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) { @@ -608,24 +609,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI - if(sni) { - struct in_addr addr4; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif - size_t hostname_len = strlen(connssl->hostname); - - if((hostname_len < USHRT_MAX) && - !Curl_inet_pton(AF_INET, connssl->hostname, &addr4) -#ifdef ENABLE_IPV6 - && !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6) -#endif - ) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen); - if(!snihost || - wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, - (unsigned short)snilen) != 1) { + if(sni && connssl->peer.sni) { + size_t sni_len = strlen(connssl->peer.sni); + if((sni_len < USHRT_MAX)) { + if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, + connssl->peer.sni, + (unsigned short)sni_len) != 1) { failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -763,9 +752,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* Enable RFC2818 checks */ if(conn_config->verifyhost) { - char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL); - if(!snihost || - (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) + char *snihost = connssl->peer.sni? + connssl->peer.sni : connssl->peer.hostname; + if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE) return CURLE_SSL_CONNECT_ERROR; } @@ -813,7 +802,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) else if(DOMAIN_NAME_MISMATCH == detail) { #if 1 failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->dispname); + connssl->peer.dispname); return CURLE_PEER_FAILED_VERIFICATION; #else /* When the wolfssl_check_domain_name() is used and you desire to @@ -1398,7 +1387,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { Curl_none_cert_status_request, /* cert_status_request */ wolfssl_connect, /* connect */ wolfssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index c3fd3a3..8b1eed6 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -1317,16 +1317,16 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, if(Curl_parseX509(&cert, beg, end)) return CURLE_PEER_FAILED_VERIFICATION; - hostlen = strlen(connssl->hostname); + hostlen = strlen(connssl->peer.hostname); /* Get the server IP address. */ #ifdef ENABLE_IPV6 if(cf->conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) + Curl_inet_pton(AF_INET6, connssl->peer.hostname, &addr)) addrlen = sizeof(struct in6_addr); else #endif - if(Curl_inet_pton(AF_INET, connssl->hostname, &addr)) + if(Curl_inet_pton(AF_INET, connssl->peer.hostname, &addr)) addrlen = sizeof(struct in_addr); /* Process extensions. */ @@ -1361,7 +1361,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, name.beg, name.end); if(len > 0 && (size_t)len == strlen(dnsname)) matched = Curl_cert_hostcheck(dnsname, (size_t)len, - connssl->hostname, hostlen); + connssl->peer.hostname, hostlen); else matched = 0; free(dnsname); @@ -1421,7 +1421,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, 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, - len, connssl->hostname, hostlen)) { + len, connssl->peer.hostname, hostlen)) { infof(data, " common name: %s (matched)", dnsname); free(dnsname); return CURLE_OK; diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c index 7e077f8..c80937b 100644 --- a/Utilities/cmcurl/lib/warnless.c +++ b/Utilities/cmcurl/lib/warnless.c @@ -37,7 +37,7 @@ #include "warnless.h" -#ifdef WIN32 +#ifdef _WIN32 #undef read #undef write #endif @@ -367,7 +367,7 @@ curl_socket_t curlx_sitosk(int i) #endif /* USE_WINSOCK */ -#if defined(WIN32) +#if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count) { @@ -379,8 +379,8 @@ ssize_t curlx_write(int fd, const void *buf, size_t count) return (ssize_t)write(fd, buf, curlx_uztoui(count)); } -/* Ensure that warnless.h continues to have an effect in "unity" builds. */ -#undef HEADER_CURL_WARNLESS_H - -#endif /* WIN32 */ +#endif /* _WIN32 */ +/* Ensure that warnless.h redefinitions continue to have an effect + in "unity" builds. */ +#undef HEADER_CURL_WARNLESS_H_REDEFS diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h index 2a53016..e5a02c8 100644 --- a/Utilities/cmcurl/lib/warnless.h +++ b/Utilities/cmcurl/lib/warnless.h @@ -69,18 +69,13 @@ curl_socket_t curlx_sitosk(int i); #endif /* USE_WINSOCK */ -#if defined(WIN32) +#if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count); ssize_t curlx_write(int fd, const void *buf, size_t count); -#undef read -#define read(fd, buf, count) curlx_read(fd, buf, count) -#undef write -#define write(fd, buf, count) curlx_write(fd, buf, count) - -#endif /* WIN32 */ +#endif /* _WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) @@ -97,3 +92,15 @@ unsigned short curlx_ntohs(unsigned short usnum); #endif /* __INTEL_COMPILER && __unix__ */ #endif /* HEADER_CURL_WARNLESS_H */ + +#ifndef HEADER_CURL_WARNLESS_H_REDEFS +#define HEADER_CURL_WARNLESS_H_REDEFS + +#if defined(_WIN32) +#undef read +#define read(fd, buf, count) curlx_read(fd, buf, count) +#undef write +#define write(fd, buf, count) curlx_write(fd, buf, count) +#endif + +#endif /* HEADER_CURL_WARNLESS_H_REDEFS */ diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 3c1964b..adde531 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c @@ -274,8 +274,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; /* infof(data, "WS-DEC: passed %zd bytes payload, %" - CURL_FORMAT_CURL_OFF_T " remain", - nwritten, remain); */ + CURL_FORMAT_CURL_OFF_T " remain", + nwritten, remain); */ } return remain? CURLE_AGAIN : CURLE_OK; @@ -925,8 +925,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, *metap = &ws->frame; *nread = ws->frame.len; /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", - buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ + CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", + buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ return CURLE_OK; } diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index 027de5c..1237608 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt @@ -69,6 +69,7 @@ SET(VERSION "${_major}.${_trimmed_minor}.${_trimmed_revision} SET(BSDCPIO_VERSION_STRING "${VERSION}") SET(BSDTAR_VERSION_STRING "${VERSION}") SET(BSDCAT_VERSION_STRING "${VERSION}") +SET(BSDUNZIP_VERSION_STRING "${VERSION}") SET(LIBARCHIVE_VERSION_NUMBER "${_version_number}") SET(LIBARCHIVE_VERSION_STRING "${VERSION}") @@ -224,6 +225,8 @@ ENDIF() # Enable CTest/CDash support include(CTest) +option(BUILD_SHARED_LIBS "Build shared libraries" ON) + OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF) OPTION(ENABLE_NETTLE "Enable use of Nettle" OFF) OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON) @@ -248,6 +251,13 @@ OPTION(ENABLE_CPIO "Enable cpio building" ON) OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE) OPTION(ENABLE_CAT "Enable cat building" ON) OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE) +IF(WIN32 AND NOT CYGWIN) + SET(ENABLE_UNZIP FALSE) + SET(ENABLE_UNZIP_SHARED FALSE) +ELSE() + OPTION(ENABLE_UNZIP "Enable unzip building" ON) + OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE) +ENDIF() OPTION(ENABLE_XATTR "Enable extended attribute support" ON) OPTION(ENABLE_ACL "Enable ACL support" ON) OPTION(ENABLE_ICONV "Enable iconv support" ON) @@ -324,6 +334,7 @@ ENDIF() IF(MINGW) ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO) + ADD_DEFINITIONS(-D__MINGW_USE_VC2005_COMPAT) ENDIF() # @@ -394,7 +405,11 @@ MACRO (TRY_MACRO_FOR_LIBRARY INCLUDES LIBRARIES IF("${TRY_TYPE}" MATCHES "COMPILES") CHECK_C_SOURCE_COMPILES("${SAMPLE_SOURCE}" ${VAR}) ELSEIF("${TRY_TYPE}" MATCHES "RUNS") - CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR}) + IF(CMAKE_CROSSCOMPILING) + MESSAGE(WARNING "Cannot test run \"${VAR}\" when cross-compiling") + ELSE(CMAKE_CROSSCOMPILING) + CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR}) + ENDIF(CMAKE_CROSSCOMPILING) ELSE("${TRY_TYPE}" MATCHES "COMPILES") MESSAGE(FATAL_ERROR "UNKNOWN KEYWORD \"${TRY_TYPE}\" FOR TRY_TYPE") ENDIF("${TRY_TYPE}" MATCHES "COMPILES") @@ -533,15 +548,19 @@ IF(LIBLZMA_FOUND) COMPILES "#include <lzma.h>\nint main() {return (int)lzma_version_number(); }" "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC") + CHECK_C_SOURCE_COMPILES( + "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}" + HAVE_LZMA_STREAM_ENCODER_MT) IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) ADD_DEFINITIONS(-DLZMA_API_STATIC) - ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) + ENDIF() ELSE() ADD_DEFINITIONS(-DLZMA_API_STATIC) ENDIF() CMAKE_POP_CHECK_STATE() ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. + SET(HAVE_LZMA_STREAM_ENCODER_MT 0) ENDIF(LIBLZMA_FOUND) # # Find LZO2 @@ -590,6 +609,7 @@ IF(LIBB2_FOUND) SET(HAVE_BLAKE2_H 1) SET(ARCHIVE_BLAKE2 FALSE) LIST(APPEND ADDITIONAL_LIBS ${LIBB2_LIBRARY}) + INCLUDE_DIRECTORIES(${LIBB2_INCLUDE_DIR}) CMAKE_PUSH_CHECK_STATE() SET(CMAKE_REQUIRED_LIBRARIES ${LIBB2_LIBRARY}) SET(CMAKE_REQUIRED_INCLUDES ${LIBB2_INCLUDE_DIR}) @@ -708,6 +728,7 @@ CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h> int main(void) { return EXT2_IOC_GETFLAGS; }" HAVE_WORKING_EXT2_IOC_GETFLAGS) LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H) +LA_CHECK_INCLUDE_FILE("fnmatch.h" HAVE_FNMATCH_H) LA_CHECK_INCLUDE_FILE("grp.h" HAVE_GRP_H) LA_CHECK_INCLUDE_FILE("io.h" HAVE_IO_H) LA_CHECK_INCLUDE_FILE("langinfo.h" HAVE_LANGINFO_H) @@ -745,6 +766,7 @@ LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H) LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H) LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H) LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H) +LA_CHECK_INCLUDE_FILE("sys/queue.h" HAVE_SYS_QUEUE_H) LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H) LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) @@ -764,9 +786,9 @@ LA_CHECK_INCLUDE_FILE("wchar.h" HAVE_WCHAR_H) LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H) LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H) IF(ENABLE_CNG) - LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H) + LA_CHECK_INCLUDE_FILE("bcrypt.h" HAVE_BCRYPT_H) IF(HAVE_BCRYPT_H) - LIST(APPEND ADDITIONAL_LIBS "Bcrypt") + LIST(APPEND ADDITIONAL_LIBS "bcrypt") ENDIF(HAVE_BCRYPT_H) ELSE(ENABLE_CNG) UNSET(HAVE_BCRYPT_H CACHE) @@ -842,6 +864,10 @@ IF(ENABLE_OPENSSL AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") IF(OPENSSL_FOUND) SET(HAVE_LIBCRYPTO 1) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) + SET(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + LA_CHECK_INCLUDE_FILE("openssl/evp.h" HAVE_OPENSSL_EVP_H) + CHECK_FUNCTION_EXISTS(PKCS5_PBKDF2_HMAC_SHA1 HAVE_PKCS5_PBKDF2_HMAC_SHA1) ENDIF(OPENSSL_FOUND) ELSE() SET(OPENSSL_FOUND FALSE) # Override cached value @@ -1379,6 +1405,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD) CHECK_FUNCTION_EXISTS_GLIBC(fchown HAVE_FCHOWN) CHECK_FUNCTION_EXISTS_GLIBC(fcntl HAVE_FCNTL) CHECK_FUNCTION_EXISTS_GLIBC(fdopendir HAVE_FDOPENDIR) +CHECK_FUNCTION_EXISTS_GLIBC(fnmatch HAVE_FNMATCH) CHECK_FUNCTION_EXISTS_GLIBC(fork HAVE_FORK) CHECK_FUNCTION_EXISTS_GLIBC(fstat HAVE_FSTAT) CHECK_FUNCTION_EXISTS_GLIBC(fstatat HAVE_FSTATAT) @@ -1391,6 +1418,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(futimesat HAVE_FUTIMESAT) CHECK_FUNCTION_EXISTS_GLIBC(geteuid HAVE_GETEUID) CHECK_FUNCTION_EXISTS_GLIBC(getgrgid_r HAVE_GETGRGID_R) CHECK_FUNCTION_EXISTS_GLIBC(getgrnam_r HAVE_GETGRNAM_R) +CHECK_FUNCTION_EXISTS_GLIBC(getline HAVE_GETLINE) CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R) CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R) CHECK_FUNCTION_EXISTS_GLIBC(getpid HAVE_GETPID) @@ -1443,12 +1471,12 @@ CHECK_FUNCTION_EXISTS_GLIBC(wcscmp HAVE_WCSCMP) CHECK_FUNCTION_EXISTS_GLIBC(wcscpy HAVE_WCSCPY) CHECK_FUNCTION_EXISTS_GLIBC(wcslen HAVE_WCSLEN) CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB) -CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S) CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64) CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE) -CHECK_FUNCTION_EXISTS_GLIBC(_gmtime64_s HAVE__GMTIME64_S) -CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S) -CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64) +CHECK_SYMBOL_EXISTS(ctime_s "time.h" HAVE_CTIME_S) +CHECK_SYMBOL_EXISTS(gmtime_s "time.h" HAVE_GMTIME_S) +CHECK_SYMBOL_EXISTS(localtime_s "time.h" HAVE_LOCALTIME_S) +CHECK_SYMBOL_EXISTS(_mkgmtime "time.h" HAVE__MKGMTIME) SET(CMAKE_REQUIRED_LIBRARIES "") CHECK_FUNCTION_EXISTS(cygwin_conv_path HAVE_CYGWIN_CONV_PATH) @@ -1491,7 +1519,6 @@ CHECK_C_SOURCE_COMPILES( "#include <fcntl.h>\n#include <unistd.h>\nint main() {char buf[10]; return readlinkat(AT_FDCWD, \"\", buf, 0);}" HAVE_READLINKAT) - # To verify major(), we need to both include the header # of interest and verify that the result can be linked. # CHECK_FUNCTION_EXISTS doesn't accept a header argument, @@ -1503,20 +1530,6 @@ CHECK_C_SOURCE_COMPILES( "#include <sys/sysmacros.h>\nint main() { return major(256); }" MAJOR_IN_SYSMACROS) -IF(ENABLE_LZMA) -CMAKE_PUSH_CHECK_STATE() -SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES}) -SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR}) - -CHECK_C_SOURCE_COMPILES( - "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}" - HAVE_LZMA_STREAM_ENCODER_MT) - -CMAKE_POP_CHECK_STATE() -ELSE() - SET(HAVE_LZMA_STREAM_ENCODER_MT 0) -ENDIF(ENABLE_LZMA) - IF(HAVE_STRERROR_R) SET(HAVE_DECL_STRERROR_R 1) ENDIF(HAVE_STRERROR_R) @@ -1578,7 +1591,7 @@ ENDIF() # # CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec - "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME) + "sys/types.h;sys/time.h;time.h" HAVE_SYS_TIME_H) CHECK_TYPE_SIZE(dev_t DEV_T) IF(NOT HAVE_DEV_T) @@ -2076,6 +2089,7 @@ IF(0) # CMake does not build libarchive's command-line tools. add_subdirectory(cat) add_subdirectory(tar) add_subdirectory(cpio) +add_subdirectory(unzip) ENDIF() install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibarchive) diff --git a/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake index a916395..aa40485 100644 --- a/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake +++ b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake @@ -7,7 +7,7 @@ find_library(MBEDCRYPTO_LIBRARY mbedcrypto) set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MBEDTLS DEFAULT_MSG +find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index e44a514..493c388 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in @@ -38,6 +38,9 @@ /* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1 +/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_MD5_MBEDTLS 1 + /* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_NETTLE 1 @@ -53,6 +56,9 @@ /* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_RMD160_MBEDTLS 1 + /* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_OPENSSL 1 @@ -62,6 +68,9 @@ /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA1_MBEDTLS 1 + /* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_NETTLE 1 @@ -83,6 +92,9 @@ /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA256_MBEDTLS 1 + /* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_NETTLE 1 @@ -104,6 +116,9 @@ /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1 +/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA384_MBEDTLS 1 + /* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA384_NETTLE 1 @@ -125,6 +140,9 @@ /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA512_MBEDTLS 1 + /* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_NETTLE 1 @@ -155,6 +173,9 @@ /* Version number of bsdcat */ #cmakedefine BSDCAT_VERSION_STRING "@BSDCAT_VERSION_STRING@" +/* Version number of bsdunzip */ +#cmakedefine BSDUNZIP_VERSION_STRING "@BSDUNZIP_VERSION_STRING@" + /* Define to 1 if you have the `acl_create_entry' function. */ #cmakedefine HAVE_ACL_CREATE_ENTRY 1 @@ -197,7 +218,7 @@ /* Define to 1 if you have the <attr/xattr.h> header file. */ #cmakedefine HAVE_ATTR_XATTR_H 1 -/* Define to 1 if you have the <Bcrypt.h> header file. */ +/* Define to 1 if you have the <bcrypt.h> header file. */ #cmakedefine HAVE_BCRYPT_H 1 /* Define to 1 if you have the <bsdxml.h> header file. */ @@ -357,6 +378,12 @@ /* Define to 1 if you have the `flistxattr' function. */ #cmakedefine HAVE_FLISTXATTR 1 +/* Define to 1 if you have the `fnmatch' function. */ +#cmakedefine HAVE_FNMATCH 1 + +/* Define to 1 if you have the <fnmatch.h> header file. */ +#cmakedefine HAVE_FNMATCH_H 1 + /* Define to 1 if you have the `fork' function. */ #cmakedefine HAVE_FORK 1 @@ -405,6 +432,9 @@ /* Define to 1 if you have the `getgrnam_r' function. */ #cmakedefine HAVE_GETGRNAM_R 1 +/* Define to 1 if you have the `getline' function. */ +#cmakedefine HAVE_GETLINE 1 + /* Define to 1 if you have the `getpid' function. */ #cmakedefine HAVE_GETPID 1 @@ -611,6 +641,15 @@ /* Define to 1 if you have the <lzo/lzoconf.h> header file. */ #cmakedefine HAVE_LZO_LZOCONF_H 1 +/* Define to 1 if you have the <mbedtls/aes.h> header file. */ +#cmakedefine HAVE_MBEDTLS_AES_H 1 + +/* Define to 1 if you have the <mbedtls/md.h> header file. */ +#cmakedefine HAVE_MBEDTLS_MD_H 1 + +/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */ +#cmakedefine HAVE_MBEDTLS_PKCS5_H 1 + /* Define to 1 if you have the `mbrtowc' function. */ #cmakedefine HAVE_MBRTOWC 1 @@ -662,6 +701,9 @@ /* Define to 1 if you have the `openat' function. */ #cmakedefine HAVE_OPENAT 1 +/* Define to 1 if you have the <openssl/evp.h> header file. */ +#cmakedefine HAVE_OPENSSL_EVP_H 1 + /* Define to 1 if you have the <paths.h> header file. */ #cmakedefine HAVE_PATHS_H 1 @@ -771,6 +813,12 @@ /* Define to 1 if you have the `strrchr' function. */ #cmakedefine HAVE_STRRCHR 1 +/* Define to 1 if the system has the type `struct statfs'. */ +#cmakedefine HAVE_STRUCT_STATFS 1 + +/* Define to 1 if `f_iosize' is a member of `struct statfs'. */ +#cmakedefine HAVE_STRUCT_STATFS_F_IOSIZE 1 + /* Define to 1 if `f_namemax' is a member of `struct statfs'. */ #cmakedefine HAVE_STRUCT_STATFS_F_NAMEMAX 1 @@ -854,6 +902,9 @@ /* Define to 1 if you have the <sys/poll.h> header file. */ #cmakedefine HAVE_SYS_POLL_H 1 +/* Define to 1 if you have the <sys/queue.h> header file. */ +#cmakedefine HAVE_SYS_QUEUE_H 1 + /* Define to 1 if you have the <sys/richacl.h> header file. */ #cmakedefine HAVE_SYS_RICHACL_H 1 @@ -993,8 +1044,8 @@ /* Define to 1 if you have the <zstd.h> header file. */ #cmakedefine HAVE_ZSTD_H 1 -/* Define to 1 if you have the `_ctime64_s' function. */ -#cmakedefine HAVE__CTIME64_S 1 +/* Define to 1 if you have the `ctime_s' function. */ +#cmakedefine HAVE_CTIME_S 1 /* Define to 1 if you have the `_fseeki64' function. */ #cmakedefine HAVE__FSEEKI64 1 @@ -1002,14 +1053,14 @@ /* Define to 1 if you have the `_get_timezone' function. */ #cmakedefine HAVE__GET_TIMEZONE 1 -/* Define to 1 if you have the `_gmtime64_s' function. */ -#cmakedefine HAVE__GMTIME64_S 1 +/* Define to 1 if you have the `gmtime_s' function. */ +#cmakedefine HAVE_GMTIME_S 1 -/* Define to 1 if you have the `_localtime64_s' function. */ -#cmakedefine HAVE__LOCALTIME64_S 1 +/* Define to 1 if you have the `localtime_s' function. */ +#cmakedefine HAVE_LOCALTIME_S 1 -/* Define to 1 if you have the `_mkgmtime64' function. */ -#cmakedefine HAVE__MKGMTIME64 1 +/* Define to 1 if you have the `_mkgmtime' function. */ +#cmakedefine HAVE__MKGMTIME 1 /* Define as const if the declaration of iconv() needs const. */ #define ICONV_CONST @ICONV_CONST@ diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version index 1af1bec..414ae6d 100644 --- a/Utilities/cmlibarchive/build/version +++ b/Utilities/cmlibarchive/build/version @@ -1 +1 @@ -3006002 +3007002 diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt index e820853..ac0bd2c 100644 --- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt @@ -252,10 +252,12 @@ endif() IF(0) # CMake does not build libarchive's full package. # Libarchive is a shared library -ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS}) -TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .) -TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS}) -SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION}) +IF(BUILD_SHARED_LIBS) + ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS}) + TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .) + TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS}) + SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION}) +ENDIF(BUILD_SHARED_LIBS) # archive_static is a static library ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS}) @@ -263,13 +265,19 @@ TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS}) SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS LIBARCHIVE_STATIC) # On Posix systems, libarchive.so and libarchive.a can co-exist. -IF(NOT WIN32 OR CYGWIN) +IF(NOT WIN32 OR CYGWIN OR NOT BUILD_SHARED_LIBS) SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive) -ENDIF(NOT WIN32 OR CYGWIN) +ENDIF(NOT WIN32 OR CYGWIN OR NOT BUILD_SHARED_LIBS) IF(ENABLE_INSTALL) # How to install the libraries - INSTALL(TARGETS archive archive_static + IF(BUILD_SHARED_LIBS) + INSTALL(TARGETS archive + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + ENDIF(BUILD_SHARED_LIBS) + INSTALL(TARGETS archive_static RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h index 180f3e4..a89f33b 100644 --- a/Utilities/cmlibarchive/libarchive/archive.h +++ b/Utilities/cmlibarchive/libarchive/archive.h @@ -36,7 +36,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3006002 +#define ARCHIVE_VERSION_NUMBER 3007002 #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ @@ -154,7 +154,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.6.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.7.2" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); diff --git a/Utilities/cmlibarchive/libarchive/archive_digest.c b/Utilities/cmlibarchive/libarchive/archive_digest.c index 3361b19..3776831 100644 --- a/Utilities/cmlibarchive/libarchive/archive_digest.c +++ b/Utilities/cmlibarchive/libarchive/archive_digest.c @@ -36,6 +36,11 @@ #error Cannot use both OpenSSL and libmd. #endif +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + /* * Message digest functions for Windows platform. */ @@ -48,6 +53,26 @@ /* * Initialize a Message digest. */ +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +static int +win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) +{ + NTSTATUS status; + ctx->valid = 0; + + status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return (ARCHIVE_FAILED); + status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + return (ARCHIVE_FAILED); + } + + ctx->valid = 1; + return (ARCHIVE_OK); +} +#else static int win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) { @@ -70,6 +95,7 @@ win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) ctx->valid = 1; return (ARCHIVE_OK); } +#endif /* * Update a Message digest. @@ -81,23 +107,37 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptHashData(ctx->hHash, + (PUCHAR)(uintptr_t)buf, + (ULONG)len, 0); +#else CryptHashData(ctx->hash, (unsigned char *)(uintptr_t)buf, (DWORD)len, 0); +#endif return (ARCHIVE_OK); } static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { +#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) DWORD siglen = (DWORD)bufsize; +#endif if (!ctx->valid) return (ARCHIVE_FAILED); +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0); + BCryptDestroyHash(ctx->hHash); + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); +#else CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->cryptProv, 0); +#endif ctx->valid = 0; return (ARCHIVE_OK); } @@ -276,7 +316,11 @@ __archive_md5final(archive_md5_ctx *ctx, void *md) static int __archive_md5init(archive_md5_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5)); +#endif } static int @@ -659,7 +703,11 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md) static int __archive_sha1init(archive_sha1_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1)); +#endif } static int @@ -919,7 +967,11 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md) static int __archive_sha256init(archive_sha256_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256)); +#endif } static int @@ -1155,7 +1207,11 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md) static int __archive_sha384init(archive_sha384_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384)); +#endif } static int @@ -1415,7 +1471,11 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md) static int __archive_sha512init(archive_sha512_ctx *ctx) { +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM)); +#else return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512)); +#endif } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_digest_private.h b/Utilities/cmlibarchive/libarchive/archive_digest_private.h index 9b3bd66..339b4ed 100644 --- a/Utilities/cmlibarchive/libarchive/archive_digest_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_digest_private.h @@ -164,6 +164,15 @@ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> +typedef struct { + int valid; + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; +} Digest_CTX; +#else #include <windows.h> #include <wincrypt.h> typedef struct { @@ -172,6 +181,7 @@ typedef struct { HCRYPTHASH hash; } Digest_CTX; #endif +#endif /* typedefs */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index 91ef0c9..0e4ccbb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h @@ -30,7 +30,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3006002 +#define ARCHIVE_VERSION_NUMBER 3007002 /* * Note: archive_entry.h is for use outside of libarchive; the diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.c b/Utilities/cmlibarchive/libarchive/archive_getdate.c index 5b0b775..fc9516e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_getdate.c +++ b/Utilities/cmlibarchive/libarchive/archive_getdate.c @@ -700,13 +700,9 @@ Convert(time_t Month, time_t Day, time_t Year, time_t Julian; int i; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (Year < 69) Year += 2000; @@ -733,15 +729,10 @@ Convert(time_t Month, time_t Day, time_t Year, Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Julian, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Julian; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Julian); #endif @@ -757,36 +748,21 @@ DSTcorrect(time_t Start, time_t Future) time_t StartDay; time_t FutureDay; struct tm *ltime; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif - -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Start); #endif StartDay = (ltime->tm_hour + 1) % 24; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) ltime = localtime_r(&Future, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Future; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - ltime = NULL; - else - ltime = &tmbuf; #else ltime = localtime(&Future); #endif @@ -801,24 +777,15 @@ RelativeDate(time_t Start, time_t zone, int dstmode, { struct tm *tm; time_t t, now; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif t = Start - zone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif defined(HAVE_GMTIME_R) tm = gmtime_r(&t, &tmbuf); -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = gmtime(&t); #endif @@ -837,25 +804,16 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) struct tm *tm; time_t Month; time_t Year; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (RelMonth == 0) return 0; -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&Start, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = Start; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&Start); #endif @@ -995,10 +953,6 @@ __archive_get_date(time_t now, const char *p) time_t Start; time_t tod; long tzone; -#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); @@ -1007,36 +961,26 @@ __archive_get_date(time_t now, const char *p) gds = &_gds; /* Look up the current time. */ -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&local, &now) ? NULL : &local; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&now, &local); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = now; - terr = _localtime64_s(&local, &tmptime); - if (terr) - tm = NULL; - else - tm = &local; #else memset(&local, 0, sizeof(local)); tm = localtime(&now); #endif if (tm == NULL) return -1; -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S) +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) local = *tm; #endif /* Look up UTC if we can and use that to determine the current * timezone offset. */ -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else memset(&gmt, 0, sizeof(gmt)); gmt_ptr = gmtime(&now); @@ -1078,15 +1022,10 @@ __archive_get_date(time_t now, const char *p) * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt; +#elif defined(HAVE_GMTIME_R) gmt_ptr = gmtime_r(&now, &gmt); -#elif defined(HAVE__GMTIME64_S) - tmptime = now; - terr = _gmtime64_s(&gmt, &tmptime); - if (terr) - gmt_ptr = NULL; - else - gmt_ptr = &gmt; #else gmt_ptr = gmtime(&now); #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac.c b/Utilities/cmlibarchive/libarchive/archive_hmac.c index 012fe15..edb3bf5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_hmac.c +++ b/Utilities/cmlibarchive/libarchive/archive_hmac.c @@ -231,15 +231,20 @@ static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L - OSSL_PARAM params[2]; + EVP_MAC *mac; - EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + char sha1[] = "SHA1"; + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1), + OSSL_PARAM_END + }; + + mac = EVP_MAC_fetch(NULL, "HMAC", NULL); *ctx = EVP_MAC_CTX_new(mac); + EVP_MAC_free(mac); if (*ctx == NULL) return -1; - EVP_MAC_free(mac); - params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA1", 0); - params[1] = OSSL_PARAM_construct_end(); + EVP_MAC_init(*ctx, key, key_len, params); #else *ctx = HMAC_CTX_new(); diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h index 50044a0..d0fda7f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h @@ -77,6 +77,8 @@ typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx; #include <openssl/opensslv.h> #include <openssl/hmac.h> #if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/params.h> + typedef EVP_MAC_CTX *archive_hmac_sha1_ctx; #else diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h index ebb0670..8ac4772 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h @@ -33,7 +33,8 @@ #include <openssl/evp.h> #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) #include <stdlib.h> /* malloc, free */ #include <string.h> /* memset */ static inline EVP_MD_CTX *EVP_MD_CTX_new(void) diff --git a/Utilities/cmlibarchive/libarchive/archive_random.c b/Utilities/cmlibarchive/libarchive/archive_random.c index 9d1aa49..a410dc0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_random.c +++ b/Utilities/cmlibarchive/libarchive/archive_random.c @@ -51,16 +51,27 @@ __FBSDID("$FreeBSD$"); #include <pthread.h> #endif -static void arc4random_buf(void *, size_t); +static void la_arc4random_buf(void *, size_t); #endif /* HAVE_ARC4RANDOM_BUF */ #include "archive.h" #include "archive_random_private.h" -#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include <wincrypt.h> #endif +#endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 @@ -75,6 +86,20 @@ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg; + + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + status = BCryptGenRandom(hAlg, buf, (ULONG)nbytes, 0); + BCryptCloseAlgorithmProvider(hAlg, 0); + if (!BCRYPT_SUCCESS(status)) + return ARCHIVE_FAILED; + + return ARCHIVE_OK; +# else HCRYPTPROV hProv; BOOL success; @@ -92,6 +117,10 @@ archive_random(void *buf, size_t nbytes) } /* TODO: Does this case really happen? */ return ARCHIVE_FAILED; +# endif +#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) + la_arc4random_buf(buf, nbytes); + return ARCHIVE_OK; #else arc4random_buf(buf, nbytes); return ARCHIVE_OK; @@ -256,7 +285,7 @@ arc4_getbyte(void) } static void -arc4random_buf(void *_buf, size_t n) +la_arc4random_buf(void *_buf, size_t n) { uint8_t *buf = (uint8_t *)_buf; _ARC4_LOCK(); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c index b4398f1..f16ca5c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c @@ -95,8 +95,13 @@ archive_read_data_into_fd(struct archive *a, int fd) "archive_read_data_into_fd"); can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode); - if (!can_lseek) + if (!can_lseek) { nulls = calloc(1, nulls_size); + if (!nulls) { + r = ARCHIVE_FATAL; + goto cleanup; + } + } while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) == ARCHIVE_OK) { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c index c964d3f..ab5306d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c @@ -1678,6 +1678,11 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; #endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -1868,8 +1873,17 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(USE_READDIR_R) /* Set maximum filename length. */ +#if defined(HAVE_STATVFS) + t->current_filesystem->name_max = svfs.f_namemax; +#else t->current_filesystem->name_max = sfs.f_namelen; #endif + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } +#endif return (ARCHIVE_OK); } @@ -1950,6 +1964,11 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = svfs.f_namemax; + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif return (ARCHIVE_OK); } @@ -2004,6 +2023,11 @@ setup_current_filesystem(struct archive_read_disk *a) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ + if (t->current_filesystem->name_max == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot determine name_max"); + return (ARCHIVE_FAILED); + } #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } @@ -2554,7 +2578,11 @@ tree_current_lstat(struct tree *t) #else if (tree_enter_working_dir(t) != 0) return NULL; +#ifdef HAVE_LSTAT if (lstat(tree_current_access_path(t), &t->lst) != 0) +#else + if (la_stat(tree_current_access_path(t), &t->lst) != 0) +#endif #endif return NULL; t->flags |= hasLstat; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c index f9d1395..f92a78a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c @@ -418,9 +418,19 @@ la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype) FILE_FLAG_OPEN_REPARSE_POINT; int ret; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(path, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); return (-1); @@ -1067,16 +1077,29 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (archive_entry_filetype(entry) == AE_IFREG && archive_entry_size(entry) > 0) { DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (t->async_io) flags |= FILE_FLAG_OVERLAPPED; if (t->direct_io) flags |= FILE_FLAG_NO_BUFFERING; else flags |= FILE_FLAG_SEQUENTIAL_SCAN; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flags; + t->entry_fh = CreateFile2(tree_current_access_path(t), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else t->entry_fh = CreateFileW(tree_current_access_path(t), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL); +#endif if (t->entry_fh == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, @@ -1547,6 +1570,9 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) { HANDLE handle; int r = 0; +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype) return (0); @@ -1560,8 +1586,16 @@ close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt) if ((t->flags & needsRestoreTimes) == 0) return (r); +#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES, + 0, OPEN_EXISTING, &createExParams); +#else handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif if (handle == INVALID_HANDLE_VALUE) { errno = EINVAL; return (-1); @@ -2046,12 +2080,24 @@ tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st, HANDLE h; int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; - +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif + if (sim_lstat && tree_current_is_physical_link(t)) flag |= FILE_FLAG_OPEN_REPARSE_POINT; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(tree_current_access_path(t), 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); t->tree_errno = errno; @@ -2257,7 +2303,10 @@ archive_read_disk_entry_from_file(struct archive *_a, } else { WIN32_FIND_DATAW findData; DWORD flag, desiredAccess; - +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif + h = FindFirstFileW(path, &findData); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); @@ -2279,9 +2328,18 @@ archive_read_disk_entry_from_file(struct archive *_a, } else desiredAccess = GENERIC_READ; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(path, desiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, desiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, @@ -2342,9 +2400,19 @@ archive_read_disk_entry_from_file(struct archive *_a, if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); } else { +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + h = CreateFile2(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c index 101dae6..03719e8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c @@ -154,10 +154,10 @@ file_skip(struct archive *a, void *client_data, int64_t request) #ifdef __ANDROID__ /* fileno() isn't safe on all platforms ... see above. */ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0) -#elif HAVE_FSEEKO - if (fseeko(mine->f, skip, SEEK_CUR) != 0) #elif HAVE__FSEEKI64 if (_fseeki64(mine->f, skip, SEEK_CUR) != 0) +#elif HAVE_FSEEKO + if (fseeko(mine->f, skip, SEEK_CUR) != 0) #else if (fseek(mine->f, skip, SEEK_CUR) != 0) #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 index b2db4cb..162b79d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 @@ -255,6 +255,27 @@ have been concatenated together. Without this option, only the contents of the first concatenated archive would be read. .El +.It Format zip +.Bl -tag -compact -width indent +.It Cm compat-2x +Libarchive 2.x incorrectly encoded Unicode filenames on +some platforms. +This option mimics the libarchive 2.x filename handling +so that such archives can be read correctly. +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating file names. +.It Cm ignorecrc32 +Skip the CRC32 check. +Mostly used for testing. +.It Cm mac-ext +Support Mac OS metadata extension that records data in special +files beginning with a period and underscore. +Defaults to enabled on Mac OS, disabled on other platforms. +Use +.Cm !mac-ext +to disable. +.El .El .\" .Sh ERRORS diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c index a5243af..9e5f6d9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c @@ -230,7 +230,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) /* Empty our output buffer. */ state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; + state->stream.avail_out = (uint32_t)state->out_block_size; /* Try to fill the output buffer. */ for (;;) { @@ -288,7 +288,7 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) return (ARCHIVE_FATAL); } state->stream.next_in = (char *)(uintptr_t)read_buf; - state->stream.avail_in = ret; + state->stream.avail_in = (uint32_t)ret; /* There is no more data, return whatever we have. */ if (ret == 0) { state->eof = 1; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c index 1e99542..d0fc1a8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c @@ -584,7 +584,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) state->out_block + prefix64k, (int)compressed_size, state->flags.block_maximum_size, state->out_block, - prefix64k); + (int)prefix64k); #else uncompressed_size = LZ4_decompress_safe_withPrefix64k( read_buf + 4, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c index c66c247..802165c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #endif #include "archive.h" +#include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" @@ -61,12 +62,17 @@ struct uudecode { #define ST_UUEND 2 #define ST_READ_BASE64 3 #define ST_IGNORE 4 + mode_t mode; + int mode_set; + char *name; }; static int uudecode_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *filter); static int uudecode_bidder_init(struct archive_read_filter *); +static int uudecode_read_header(struct archive_read_filter *, + struct archive_entry *entry); static ssize_t uudecode_filter_read(struct archive_read_filter *, const void **); static int uudecode_filter_close(struct archive_read_filter *); @@ -359,6 +365,7 @@ static const struct archive_read_filter_vtable uudecode_reader_vtable = { .read = uudecode_filter_read, .close = uudecode_filter_close, + .read_header = uudecode_read_header }; static int @@ -389,6 +396,8 @@ uudecode_bidder_init(struct archive_read_filter *self) uudecode->in_allocated = IN_BUFF_SIZE; uudecode->out_buff = out_buff; uudecode->state = ST_FIND_HEAD; + uudecode->mode_set = 0; + uudecode->name = NULL; self->vtable = &uudecode_reader_vtable; return (ARCHIVE_OK); @@ -434,6 +443,22 @@ ensure_in_buff_size(struct archive_read_filter *self, return (ARCHIVE_OK); } +static int +uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry) +{ + + struct uudecode *uudecode; + uudecode = (struct uudecode *)self->data; + + if (uudecode->mode_set != 0) + archive_entry_set_mode(entry, S_IFREG | uudecode->mode); + + if (uudecode->name != NULL) + archive_entry_set_pathname(entry, uudecode->name); + + return (ARCHIVE_OK); +} + static ssize_t uudecode_filter_read(struct archive_read_filter *self, const void **buff) { @@ -443,7 +468,7 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) ssize_t avail_in, ravail; ssize_t used; ssize_t total; - ssize_t len, llen, nl; + ssize_t len, llen, nl, namelen; uudecode = (struct uudecode *)self->data; @@ -551,6 +576,28 @@ read_more: uudecode->state = ST_READ_UU; else uudecode->state = ST_READ_BASE64; + uudecode->mode = (mode_t)( + ((int)(b[l] - '0') * 64) + + ((int)(b[l+1] - '0') * 8) + + (int)(b[l+2] - '0')); + uudecode->mode_set = 1; + namelen = len - nl - 4 - l; + if (namelen > 1) { + if (uudecode->name != NULL) + free(uudecode->name); + uudecode->name = malloc(namelen + 1); + if (uudecode->name == NULL) { + archive_set_error( + &self->archive->archive, + ENOMEM, + "Can't allocate data for uudecode"); + return (ARCHIVE_FATAL); + } + strncpy(uudecode->name, + (const char *)(b + l + 4), + namelen); + uudecode->name[namelen] = '\0'; + } } break; case ST_READ_UU: @@ -683,6 +730,7 @@ uudecode_filter_close(struct archive_read_filter *self) uudecode = (struct uudecode *)self->data; free(uudecode->in_buff); free(uudecode->out_buff); + free(uudecode->name); free(uudecode); return (ARCHIVE_OK); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c index 29d4d62..8d20d7c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c @@ -115,9 +115,9 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, unsigned prefix; /* Zstd frame magic values */ - const unsigned zstd_magic = 0xFD2FB528U; - const unsigned zstd_magic_skippable_start = 0x184D2A50U; - const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; + unsigned zstd_magic = 0xFD2FB528U; + unsigned zstd_magic_skippable_start = 0x184D2A50U; + unsigned zstd_magic_skippable_mask = 0xFFFFFFF0; (void) self; /* UNUSED */ @@ -170,7 +170,7 @@ static int zstd_bidder_init(struct archive_read_filter *self) { struct private_data *state; - const size_t out_block_size = ZSTD_DStreamOutSize(); + size_t out_block_size = ZSTD_DStreamOutSize(); void *out_block; ZSTD_DStream *dstream; @@ -211,6 +211,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) ssize_t avail_in; ZSTD_outBuffer out; ZSTD_inBuffer in; + size_t ret; state = (struct private_data *)self->data; @@ -219,7 +220,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) /* Try to fill the output buffer. */ while (out.pos < out.size && !state->eof) { if (!state->in_frame) { - const size_t ret = ZSTD_initDStream(state->dstream); + ret = ZSTD_initDStream(state->dstream); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, @@ -249,8 +250,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) in.pos = 0; { - const size_t ret = - ZSTD_decompressStream(state->dstream, &out, &in); + ret = ZSTD_decompressStream(state->dstream, &out, &in); if (ZSTD_isError(ret)) { archive_set_error(&self->archive->archive, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index a4d9dcf..0bfbf1f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c @@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ZLIB_H #include <cm3p/zlib.h> #endif +#ifdef HAVE_ZSTD_H +#include <cm3p/zstd.h> +#endif #ifdef __clang_analyzer__ #include <assert.h> @@ -84,8 +87,11 @@ __FBSDID("$FreeBSD$"); #define _7Z_IA64 0x03030401 #define _7Z_ARM 0x03030501 #define _7Z_ARMTHUMB 0x03030701 +#define _7Z_ARM64 0xa #define _7Z_SPARC 0x03030805 +#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */ + /* * 7-Zip header property IDs. */ @@ -114,6 +120,30 @@ __FBSDID("$FreeBSD$"); #define kEncodedHeader 0x17 #define kDummy 0x19 +// Check that some windows file attribute constants are defined. +// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants +#ifndef FILE_ATTRIBUTE_READONLY +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#endif + +#ifndef FILE_ATTRIBUTE_HIDDEN +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#endif + +#ifndef FILE_ATTRIBUTE_SYSTEM +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#endif + +#ifndef FILE_ATTRIBUTE_DIRECTORY +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#endif + +// This value is defined in 7zip with the comment "trick for Unix". +// +// 7z archives created on unix have this bit set in the high 16 bits of +// the attr field along with the unix permissions. +#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 + struct _7z_digests { unsigned char *defineds; uint32_t *digests; @@ -282,6 +312,11 @@ struct _7zip { z_stream stream; int stream_valid; #endif + /* Decoding Zstandard data. */ +#if HAVE_ZSTD_H + ZSTD_DStream *zstd_dstream; + int zstdstream_valid; +#endif /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; @@ -401,6 +436,9 @@ static int setup_decode_folder(struct archive_read *, struct _7z_folder *, int); static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); +static void arm_Init(struct _7zip *); +static size_t arm_Convert(struct _7zip *, uint8_t *, size_t); +static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); @@ -729,6 +767,37 @@ archive_read_format_7zip_read_header(struct archive_read *a, archive_entry_set_size(entry, 0); } + // These attributes are supported by the windows implementation of archive_write_disk. + const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; + + if (zip_entry->attr & supported_attrs) { + char *fflags_text, *ptr; + /* allocate for "rdonly,hidden,system," */ + fflags_text = malloc(22 * sizeof(char)); + if (fflags_text != NULL) { + ptr = fflags_text; + if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) { + strcpy(ptr, "rdonly,"); + ptr = ptr + 7; + } + if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) { + strcpy(ptr, "hidden,"); + ptr = ptr + 7; + } + if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) { + strcpy(ptr, "system,"); + ptr = ptr + 7; + } + if (ptr > fflags_text) { + /* Delete trailing comma */ + *(ptr - 1) = '\0'; + archive_entry_copy_fflags_text(entry, + fflags_text); + } + free(fflags_text); + } + } + /* If there's no body, force read_data() to return EOF immediately. */ if (zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; @@ -1034,10 +1103,13 @@ init_decompression(struct archive_read *a, struct _7zip *zip, case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: + case _7Z_ZSTD: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && - coder2->codec != _7Z_X86_BCJ2) { + coder2->codec != _7Z_X86_BCJ2 && + coder2->codec != _7Z_ARM && + coder2->codec != _7Z_ARM64) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %lx for %lx", @@ -1048,6 +1120,8 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); + else if (coder2->codec == _7Z_ARM) + arm_Init(zip); } break; default: @@ -1144,6 +1218,12 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].id = LZMA_FILTER_ARMTHUMB; fi++; break; +#ifdef LZMA_FILTER_ARM64 + case _7Z_ARM64: + filters[fi].id = LZMA_FILTER_ARM64; + fi++; + break; +#endif case _7Z_SPARC: filters[fi].id = LZMA_FILTER_SPARC; fi++; @@ -1229,6 +1309,22 @@ init_decompression(struct archive_read *a, struct _7zip *zip, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif + case _7Z_ZSTD: + { +#if defined(HAVE_ZSTD_H) + if (zip->zstdstream_valid) { + ZSTD_freeDStream(zip->zstd_dstream); + zip->zstdstream_valid = 0; + } + zip->zstd_dstream = ZSTD_createDStream(); + zip->zstdstream_valid = 1; + break; +#else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZSTD codec is unsupported"); + return (ARCHIVE_FAILED); +#endif + } case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H if (zip->stream_valid) @@ -1299,6 +1395,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, case _7Z_IA64: case _7Z_ARM: case _7Z_ARMTHUMB: + case _7Z_ARM64: case _7Z_SPARC: case _7Z_DELTA: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -1443,9 +1540,9 @@ decompress(struct archive_read *a, struct _7zip *zip, #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; - zip->bzstream.avail_in = t_avail_in; + zip->bzstream.avail_in = (uint32_t)t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; - zip->bzstream.avail_out = t_avail_out; + zip->bzstream.avail_out = (uint32_t)t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ @@ -1495,6 +1592,22 @@ decompress(struct archive_read *a, struct _7zip *zip, t_avail_out = zip->stream.avail_out; break; #endif +#ifdef HAVE_ZSTD_H + case _7Z_ZSTD: + { + ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos + ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos + + size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input); + if (ZSTD_isError(zret)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret)); + return ARCHIVE_FAILED; + } + t_avail_in -= input.pos; + t_avail_out -= output.pos; + break; + } +#endif case _7Z_PPMD: { uint64_t flush_bytes; @@ -1579,16 +1692,23 @@ decompress(struct archive_read *a, struct _7zip *zip, /* * Decord BCJ. */ - if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { - size_t l = x86_Convert(zip, buff, *outbytes); - zip->odd_bcj_size = *outbytes - l; - if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && - o_avail_in && ret != ARCHIVE_EOF) { - memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, - zip->odd_bcj_size); - *outbytes = l; - } else - zip->odd_bcj_size = 0; + if (zip->codec != _7Z_LZMA2) { + if (zip->codec2 == _7Z_X86) { + size_t l = x86_Convert(zip, buff, *outbytes); + + zip->odd_bcj_size = *outbytes - l; + if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && + o_avail_in && ret != ARCHIVE_EOF) { + memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, + zip->odd_bcj_size); + *outbytes = l; + } else + zip->odd_bcj_size = 0; + } else if (zip->codec2 == _7Z_ARM) { + *outbytes = arm_Convert(zip, buff, *outbytes); + } else if (zip->codec2 == _7Z_ARM64) { + *outbytes = arm64_Convert(zip, buff, *outbytes); + } } /* @@ -2612,6 +2732,28 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, entries[i].flg |= HAS_STREAM; /* The high 16 bits of attributes is a posix file mode. */ entries[i].mode = entries[i].attr >> 16; + + if (!(entries[i].attr & FILE_ATTRIBUTE_UNIX_EXTENSION)) { + // Only windows permissions specified for this entry. Translate to + // reasonable corresponding unix permissions. + + if (entries[i].attr & FILE_ATTRIBUTE_DIRECTORY) { + if (entries[i].attr & FILE_ATTRIBUTE_READONLY) { + // Read-only directory. + entries[i].mode = AE_IFDIR | 0555; + } else { + // Read-write directory. + entries[i].mode = AE_IFDIR | 0755; + } + } else if (entries[i].attr & FILE_ATTRIBUTE_READONLY) { + // Readonly file. + entries[i].mode = AE_IFREG | 0444; + } else { + // Assume read-write file. + entries[i].mode = AE_IFREG | 0644; + } + } + if (entries[i].flg & HAS_STREAM) { if ((size_t)sindex >= si->ss.unpack_streams) return (-1); @@ -2652,7 +2794,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, } entries[i].ssIndex = -1; } - if (entries[i].attr & 0x01) + if (entries[i].attr & FILE_ATTRIBUTE_READONLY) entries[i].mode &= ~0222;/* Read only. */ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) { @@ -3737,6 +3879,116 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) return (bufferPos); } +static void +arm_Init(struct _7zip *zip) +{ + zip->bcj_ip = 8; +} + +static size_t +arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin <lasse.collin@tukaani.org> + * Igor Pavlov <https://7-zip.org/> + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + // Calculate the transformed addr. + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= zip->bcj_ip + (uint32_t)i; + addr >>= 2; + + // Store the transformed addr in buf. + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + +static size_t +arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin <lasse.collin@tukaani.org> + * Igor Pavlov <https://7-zip.org/> + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + + size_t i; + uint32_t instr; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = (uint32_t)buf[i] + | ((uint32_t)buf[i+1] << 8) + | ((uint32_t)buf[i+2] << 16) + | ((uint32_t)buf[i+3] << 24); + + if ((instr >> 26) == 0x25) { + /* BL instruction */ + addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2); + instr = 0x94000000 | (addr & 0x03FFFFFF); + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } else if ((instr & 0x9F000000) == 0x90000000) { + /* ADRP instruction */ + addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); + + /* Only convert values in the range +/-512 MiB. */ + if ((addr + 0x020000) & 0x1C0000) + continue; + + addr -= (zip->bcj_ip + (uint32_t)i) >> 12; + + instr &= 0x9000001F; + instr |= (addr & 3) << 29; + instr |= (addr & 0x03FFFC) << 3; + instr |= (0U - (addr & 0x020000)) & 0xE00000; + + buf[i] = (uint8_t)instr; + buf[i+1] = (uint8_t)(instr >> 8); + buf[i+2] = (uint8_t)(instr >> 16); + buf[i+3] = (uint8_t)(instr >> 24); + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + /* * Brought from LZMA SDK. * diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c index 6fcfbfc..e57b8c3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c @@ -2294,10 +2294,10 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) (br->cache_buffer << 48) | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[0]) << 32 | - ((uint32_t)strm->next_in[3]) << 24 | - ((uint32_t)strm->next_in[2]) << 16 | - ((uint32_t)strm->next_in[5]) << 8 | - (uint32_t)strm->next_in[4]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[2]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_t)strm->next_in[4]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index 6b8ae33..9adcfd3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c @@ -441,7 +441,7 @@ archive_read_format_cpio_read_header(struct archive_read *a, /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", - 11) == 0) { + 10) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); @@ -985,14 +985,14 @@ archive_read_format_cpio_cleanup(struct archive_read *a) static int64_t le4(const unsigned char *p) { - return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); + return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8)); } static int64_t be4(const unsigned char *p) { - return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); + return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); } /* diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c index 91b9187..a6219fa 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c @@ -1901,7 +1901,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, * NUMBER of RRIP "PX" extension. * Note: Old mkisofs did not record that FILE SERIAL NUMBER * in ISO images. - * Note2: xorriso set 0 to the location of a symlink file. + * Note2: xorriso set 0 to the location of a symlink file. */ if (file->size == 0 && location >= 0) { /* If file->size is zero, its location points wrong place, @@ -1955,7 +1955,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, * made by makefs is not zero and its location is * the same as those of next regular file. That is * the same as hard like file and it causes unexpected - * error. + * error. */ if (file->size > 0 && (file->mode & AE_IFMT) == AE_IFLNK) { @@ -2747,7 +2747,7 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, * If directory entries all which are descendant of * rr_moved are still remaining, expose their. */ - if (iso9660->re_files.first != NULL && + if (iso9660->re_files.first != NULL && iso9660->rr_moved != NULL && iso9660->rr_moved->rr_moved_has_re_only) /* Expose "rr_moved" entry. */ @@ -3182,11 +3182,11 @@ isodate17(const unsigned char *v) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index 8b7bf66..1c64b29 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c @@ -1819,7 +1819,7 @@ lha_crc16(uint16_t crc, const void *pp, size_t len) * remove the statement which will not be executed. */ #undef bswap16 #ifndef __has_builtin -# define __has_builtin(x) 0 +#define __has_builtin(x) 0 #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) @@ -1827,7 +1827,7 @@ lha_crc16(uint16_t crc, const void *pp, size_t len) /* GCC 4.8 and later has __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #elif defined(__clang__) && __has_builtin(__builtin_bswap16) -/* All clang versions have __builtin_bswap16() */ +/* Newer clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) @@ -2012,10 +2012,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | - ((uint32_t)strm->next_in[3]) << 24 | - ((uint32_t)strm->next_in[4]) << 16 | - ((uint32_t)strm->next_in[5]) << 8 | - (uint32_t)strm->next_in[6]; + ((uint64_t)strm->next_in[3]) << 24 | + ((uint64_t)strm->next_in[4]) << 16 | + ((uint64_t)strm->next_in[5]) << 8 | + (uint64_t)strm->next_in[6]; strm->next_in += 7; strm->avail_in -= 7; br->cache_avail += 7 * 8; @@ -2025,10 +2025,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | ((uint64_t)strm->next_in[1]) << 32 | - ((uint32_t)strm->next_in[2]) << 24 | - ((uint32_t)strm->next_in[3]) << 16 | - ((uint32_t)strm->next_in[4]) << 8 | - (uint32_t)strm->next_in[5]; + ((uint64_t)strm->next_in[2]) << 24 | + ((uint64_t)strm->next_in[3]) << 16 | + ((uint64_t)strm->next_in[4]) << 8 | + (uint64_t)strm->next_in[5]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index 2bc3ba0..a5fa30e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c @@ -1280,7 +1280,13 @@ parse_file(struct archive_read *a, struct archive_entry *entry, mtree->fd = -1; st = NULL; } - } else if (lstat(path, st) == -1) { + } +#ifdef HAVE_LSTAT + else if (lstat(path, st) == -1) +#else + else if (la_stat(path, st) == -1) +#endif + { st = NULL; } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c index 41d6cb2..a1c5495 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c @@ -1064,7 +1064,7 @@ archive_read_format_rar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } p = h; - crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read); + crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read); __archive_read_consume(a, to_read); skip -= to_read; } @@ -1832,13 +1832,9 @@ read_exttime(const char *p, struct rar *rar, const char *endp) struct tm *tm; time_t t; long nsec; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif if (p + 2 > endp) return (-1); @@ -1870,15 +1866,10 @@ read_exttime(const char *p, struct rar *rar, const char *endp) rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8); p++; } -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) tm = localtime_r(&t, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = t; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - tm = NULL; - else - tm = &tmbuf; #else tm = localtime(&t); #endif @@ -3451,7 +3442,7 @@ compile_program(const uint8_t *bytes, size_t length) prog = calloc(1, sizeof(*prog)); if (!prog) return NULL; - prog->fingerprint = crc32(0, bytes, length) | ((uint64_t)length << 32); + prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32); if (membr_bits(&br, 1)) { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c index aa7b861..7f1efb8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c @@ -2475,7 +2475,7 @@ static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { * `stored_crc32` info filled in. */ if(rar->file.stored_crc32 > 0) { rar->file.calculated_crc32 = - crc32(rar->file.calculated_crc32, p, to_read); + crc32(rar->file.calculated_crc32, p, (unsigned int)to_read); } /* Check if the file uses an optional BLAKE2sp checksum diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c index 2732996..61ab29e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c @@ -530,11 +530,11 @@ strtoi_lim(const char *str, const char **ep, int llim, int ulim) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c index 330df58..efed86d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c @@ -1127,7 +1127,7 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) x |= p[1] - '0'; else return (-1); - + *b++ = x; bsize--; p += 2; @@ -1139,11 +1139,11 @@ atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) static time_t time_from_tm(struct tm *t) { -#if HAVE_TIMEGM +#if HAVE__MKGMTIME + return _mkgmtime(t); +#elif HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index e126ae3..e8b20f5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -2186,11 +2186,11 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, /* Setup buffer boundaries. */ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; - zip->bzstream.avail_in = in_bytes; + zip->bzstream.avail_in = (uint32_t)in_bytes; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_in_lo32 = 0; zip->bzstream.next_out = (char*) zip->uncompressed_buffer; - zip->bzstream.avail_out = zip->uncompressed_buffer_size; + zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size; zip->bzstream.total_out_hi32 = 0; zip->bzstream.total_out_lo32 = 0; @@ -2227,7 +2227,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, to_consume = zip->bzstream.total_in_lo32; __archive_read_consume(a, to_consume); - total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + + total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) | zip->bzstream.total_out_lo32; zip->entry_bytes_remaining -= to_consume; diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index 69458e1..accf526 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c @@ -1324,6 +1324,10 @@ free_sconv_object(struct archive_string_conv *sc) } #if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GetOEMCP() CP_OEMCP +# endif + static unsigned my_atoi(const char *p) { diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c index 83586b5..0680711 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.c +++ b/Utilities/cmlibarchive/libarchive/archive_util.c @@ -42,9 +42,20 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #ifdef HAVE_STRING_H #include <string.h> #endif -#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +/* don't use bcrypt when XP needs to be supported */ +#include <bcrypt.h> + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#elif defined(HAVE_WINCRYPT_H) #include <wincrypt.h> #endif +#endif #ifdef HAVE_ZLIB_H #include <cm3p/zlib.h> #endif @@ -233,14 +244,16 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) L'm', L'n', L'o', L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z' }; - HCRYPTPROV hProv; struct archive_wstring temp_name; wchar_t *ws; DWORD attr; wchar_t *xp, *ep; int fd; - - hProv = (HCRYPTPROV)NULL; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + BCRYPT_ALG_HANDLE hAlg = NULL; +#else + HCRYPTPROV hProv = (HCRYPTPROV)NULL; +#endif fd = -1; ws = NULL; @@ -314,23 +327,42 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) abort(); } +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, + NULL, 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif for (;;) { wchar_t *p; HANDLE h; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif /* Generate a random file name through CryptGenRandom(). */ p = xp; +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, + (DWORD)(ep - p)*sizeof(wchar_t), 0))) { + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } +#else if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } +#endif for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; @@ -347,6 +379,17 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) /* mkstemp */ attr = FILE_ATTRIBUTE_NORMAL; } +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = attr & 0xFFFF; + createExParams.dwFileFlags = attr & 0xFFF00000; + h = CreateFile2(ws, + GENERIC_READ | GENERIC_WRITE | DELETE, + 0,/* Not share */ + CREATE_NEW, + &createExParams); +#else h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ @@ -354,6 +397,7 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) CREATE_NEW,/* Create a new file only */ attr, NULL); +#endif if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with * a new filename. */ @@ -372,8 +416,13 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) break;/* success! */ } exit_tmpfile: +#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (hAlg != NULL) + BCryptCloseAlgorithmProvider(hAlg, 0); +#else if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); +#endif free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.c b/Utilities/cmlibarchive/libarchive/archive_windows.c index 624e270..ebc5eef 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_windows.c @@ -234,7 +234,11 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, { wchar_t *wpath; HANDLE handle; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) handle = CreateFileA(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -242,12 +246,25 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, return (handle); if (GetLastError() != ERROR_PATH_NOT_FOUND) return (handle); +#endif wpath = __la_win_permissive_name(path); if (wpath == NULL) - return (handle); + return INVALID_HANDLE_VALUE; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF; + createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000; + createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000; + createExParams.lpSecurityAttributes = lpSecurityAttributes; + createExParams.hTemplateFile = hTemplateFile; + handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode, + dwCreationDisposition, &createExParams); +#else /* !WINAPI_PARTITION_DESKTOP */ handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); +#endif /* !WINAPI_PARTITION_DESKTOP */ free(wpath); return (handle); } @@ -305,7 +322,10 @@ __la_open(const char *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesA(path); - if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) { +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) + if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) +#endif + { ws = __la_win_permissive_name(path); if (ws == NULL) { errno = EINVAL; @@ -320,7 +340,7 @@ __la_open(const char *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; - +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (ws != NULL) handle = CreateFileW(ws, 0, 0, NULL, OPEN_EXISTING, @@ -333,6 +353,15 @@ __la_open(const char *path, int flags, ...) FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL); +#else /* !WINAPI_PARTITION_DESKTOP */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY; + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + handle = CreateFile2(ws, 0, 0, + OPEN_EXISTING, &createExParams); +#endif /* !WINAPI_PARTITION_DESKTOP */ free(ws); if (handle == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c index 27626b5..ec3c95c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.c +++ b/Utilities/cmlibarchive/libarchive/archive_write.c @@ -310,6 +310,25 @@ __archive_write_output(struct archive_write *a, const void *buff, size_t length) return (__archive_write_filter(a->filter_first, buff, length)); } +static int +__archive_write_filters_flush(struct archive_write *a) +{ + struct archive_write_filter *f; + int ret, ret1; + + ret = ARCHIVE_OK; + for (f = a->filter_first; f != NULL; f = f->next_filter) { + if (f->flush != NULL && f->bytes_written > 0) { + ret1 = (f->flush)(f); + if (ret1 < ret) + ret = ret1; + if (ret1 < ARCHIVE_WARN) + f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; + } + } + return (ret); +} + int __archive_write_nulls(struct archive_write *a, size_t length) { @@ -740,6 +759,18 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) return (ARCHIVE_FAILED); } + /* Flush filters at boundary. */ + r2 = __archive_write_filters_flush(a); + if (r2 == ARCHIVE_FAILED) { + return (ARCHIVE_FAILED); + } + if (r2 == ARCHIVE_FATAL) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + if (r2 < ret) + ret = r2; + /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); if (r2 == ARCHIVE_FAILED) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c index 0637e96..9c2144a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c @@ -190,7 +190,7 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) memset(&data->stream, 0, sizeof(data->stream)); data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = (uint32_t)data->compressed_buffer_size; f->write = archive_compressor_bzip2_write; /* Initialize compression library */ @@ -244,7 +244,7 @@ archive_compressor_bzip2_write(struct archive_write_filter *f, /* Compress input data to output buffer */ SET_NEXT_IN(data, buff); - data->stream.avail_in = length; + data->stream.avail_in = (uint32_t)length; if (drive_compressor(f, data, 0)) return (ARCHIVE_FATAL); return (ARCHIVE_OK); @@ -313,7 +313,7 @@ drive_compressor(struct archive_write_filter *f, return (ARCHIVE_FATAL); } data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = (uint32_t)data->compressed_buffer_size; } /* If there's nothing to do, we're done. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c index d404fae..3ed269f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c @@ -352,7 +352,7 @@ archive_compressor_compress_write(struct archive_write_filter *f, while (length--) { c = *bp++; state->in_count++; - state->cur_fcode = (c << 16) + state->cur_code; + state->cur_fcode = (c << 16) | state->cur_code; i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */ if (state->hashtab[i] == state->cur_fcode) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c index cf19fad..6ac4503 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c @@ -518,10 +518,10 @@ drive_compressor_independence(struct archive_write_filter *f, const char *p, } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, length | 0x80000000); + archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); data->out += 4; memcpy(data->out, p, length); - outsize = length; + outsize = (uint32_t)length; } data->out += outsize; if (data->block_checksum) { @@ -603,10 +603,10 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p, } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, length | 0x80000000); + archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); data->out += 4; memcpy(data->out, p, length); - outsize = length; + outsize = (uint32_t)length; } data->out += outsize; if (data->block_checksum) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c index 7d36d58..3d6b3d1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c @@ -31,6 +31,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include <errno.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif @@ -50,10 +53,22 @@ __FBSDID("$FreeBSD$"); struct private_data { int compression_level; - int threads; + int threads; + int long_distance; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + enum { + running, + finishing, + resetting, + } state; + int frame_per_file; + size_t min_frame_size; + size_t max_frame_size; + size_t cur_frame; + size_t cur_frame_in; + size_t cur_frame_out; + size_t total_in; ZSTD_CStream *cstream; - int64_t total_in; ZSTD_outBuffer out; #else struct archive_write_program_data *pdata; @@ -67,14 +82,18 @@ struct private_data { #define CLEVEL_STD_MAX 19 /* without using --ultra */ #define CLEVEL_MAX 22 +#define LONG_STD 27 + #define MINVER_NEGCLEVEL 10304 #define MINVER_MINCLEVEL 10306 +#define MINVER_LONG 10302 static int archive_compressor_zstd_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_zstd_open(struct archive_write_filter *); static int archive_compressor_zstd_write(struct archive_write_filter *, const void *, size_t); +static int archive_compressor_zstd_flush(struct archive_write_filter *); static int archive_compressor_zstd_close(struct archive_write_filter *); static int archive_compressor_zstd_free(struct archive_write_filter *); #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR @@ -103,13 +122,20 @@ archive_write_add_filter_zstd(struct archive *_a) f->data = data; f->open = &archive_compressor_zstd_open; f->options = &archive_compressor_zstd_options; + f->flush = &archive_compressor_zstd_flush; f->close = &archive_compressor_zstd_close; f->free = &archive_compressor_zstd_free; f->code = ARCHIVE_FILTER_ZSTD; f->name = "zstd"; data->compression_level = CLEVEL_DEFAULT; data->threads = 0; + data->long_distance = 0; #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + data->frame_per_file = 0; + data->min_frame_size = 0; + data->max_frame_size = SIZE_MAX; + data->cur_frame_in = 0; + data->cur_frame_out = 0; data->cstream = ZSTD_createCStream(); if (data->cstream == NULL) { free(data); @@ -147,29 +173,18 @@ archive_compressor_zstd_free(struct archive_write_filter *f) return (ARCHIVE_OK); } -static int string_is_numeric (const char* value) +static int string_to_number(const char *string, intmax_t *numberp) { - size_t len = strlen(value); - size_t i; - - if (len == 0) { - return (ARCHIVE_WARN); - } - else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) { - return (ARCHIVE_WARN); - } - else if (!(value[0] >= '0' && value[0] <= '9') && - value[0] != '-' && value[0] != '+') { - return (ARCHIVE_WARN); - } - - for (i = 1; i < len; i++) { - if (!(value[i] >= '0' && value[i] <= '9')) { - return (ARCHIVE_WARN); - } - } - - return (ARCHIVE_OK); + char *end; + + if (string == NULL || *string == '\0') + return (ARCHIVE_WARN); + *numberp = strtoimax(string, &end, 10); + if (end == string || *end != '\0' || errno == EOVERFLOW) { + *numberp = 0; + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); } /* @@ -182,13 +197,13 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, struct private_data *data = (struct private_data *)f->data; if (strcmp(key, "compression-level") == 0) { - int level = atoi(value); + intmax_t level; + if (string_to_number(value, &level) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } /* If we don't have the library, hard-code the max level */ int minimum = CLEVEL_MIN; int maximum = CLEVEL_MAX; - if (string_is_numeric(value) != ARCHIVE_OK) { - return (ARCHIVE_WARN); - } #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR maximum = ZSTD_maxCLevel(); #if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL @@ -204,21 +219,65 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, if (level < minimum || level > maximum) { return (ARCHIVE_WARN); } - data->compression_level = level; + data->compression_level = (int)level; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { - int threads = atoi(value); - if (string_is_numeric(value) != ARCHIVE_OK) { + intmax_t threads; + if (string_to_number(value, &threads) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - - int minimum = 0; - - if (threads < minimum) { + if (threads < 0) { return (ARCHIVE_WARN); } - - data->threads = threads; + data->threads = (int)threads; + return (ARCHIVE_OK); +#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR + } else if (strcmp(key, "frame-per-file") == 0) { + data->frame_per_file = 1; + return (ARCHIVE_OK); + } else if (strcmp(key, "min-frame-size") == 0) { + intmax_t min_frame_size; + if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (min_frame_size < 0) { + return (ARCHIVE_WARN); + } + data->min_frame_size = min_frame_size; + return (ARCHIVE_OK); + } else if (strcmp(key, "max-frame-size") == 0) { + intmax_t max_frame_size; + if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } + if (max_frame_size < 1024) { + return (ARCHIVE_WARN); + } + data->max_frame_size = max_frame_size; + return (ARCHIVE_OK); +#endif + } + else if (strcmp(key, "long") == 0) { + intmax_t long_distance; + if (string_to_number(value, &long_distance) != ARCHIVE_OK) { + return (ARCHIVE_WARN); + } +#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR && ZSTD_VERSION_NUMBER >= MINVER_LONG + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog); + if (ZSTD_isError(bounds.error)) { + int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31)); + if (((int)long_distance) < 10 || (int)long_distance > max_distance) + return (ARCHIVE_WARN); + } else { + if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound) + return (ARCHIVE_WARN); + } +#else + int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31)); + if (((int)long_distance) < 10 || (int)long_distance > max_distance) + return (ARCHIVE_WARN); +#endif + data->long_distance = (int)long_distance; return (ARCHIVE_OK); } @@ -270,6 +329,10 @@ archive_compressor_zstd_open(struct archive_write_filter *f) ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads); +#if ZSTD_VERSION_NUMBER >= MINVER_LONG + ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance); +#endif + return (ARCHIVE_OK); } @@ -281,15 +344,22 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; - int ret; - /* Update statistics */ - data->total_in += length; + return (drive_compressor(f, data, 0, buff, length)); +} - if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK) - return (ret); +/* + * Flush the compressed stream. + */ +static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; - return (ARCHIVE_OK); + if (data->frame_per_file && data->state == running && + data->cur_frame_out > data->min_frame_size) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* @@ -300,57 +370,72 @@ archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - /* Finish zstd frame */ - return drive_compressor(f, data, 1, NULL, 0); + if (data->state == running) + data->state = finishing; + return (drive_compressor(f, data, 1, NULL, 0)); } /* * Utility function to push input data through compressor, * writing full output blocks as necessary. - * - * Note that this handles both the regular write case (finishing == - * false) and the end-of-archive case (finishing == true). */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int finishing, const void *src, size_t length) + struct private_data *data, int flush, const void *src, size_t length) { - ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 }; + ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 }; + size_t ipos, opos, zstdret = 0; + int ret; for (;;) { - if (data->out.pos == data->out.size) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.size); + ipos = in.pos; + opos = data->out.pos; + switch (data->state) { + case running: + if (in.pos == in.size) + return (ARCHIVE_OK); + zstdret = ZSTD_compressStream(data->cstream, + &data->out, &in); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + break; + case finishing: + zstdret = ZSTD_endStream(data->cstream, &data->out); + if (ZSTD_isError(zstdret)) + goto zstd_fatal; + if (zstdret == 0) + data->state = resetting; + break; + case resetting: + ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only); + data->cur_frame++; + data->cur_frame_in = 0; + data->cur_frame_out = 0; + data->state = running; + break; + } + data->total_in += in.pos - ipos; + data->cur_frame_in += in.pos - ipos; + data->cur_frame_out += data->out.pos - opos; + if (data->state == running && + data->cur_frame_in >= data->max_frame_size) { + data->state = finishing; + } + if (data->out.pos == data->out.size || + (flush && data->out.pos > 0)) { + ret = __archive_write_filter(f->next_filter, + data->out.dst, data->out.pos); if (ret != ARCHIVE_OK) - return (ARCHIVE_FATAL); + goto fatal; data->out.pos = 0; } - - /* If there's nothing to do, we're done. */ - if (!finishing && in.pos == in.size) - return (ARCHIVE_OK); - - { - const size_t zstdret = !finishing ? - ZSTD_compressStream(data->cstream, &data->out, &in) - : ZSTD_endStream(data->cstream, &data->out); - - if (ZSTD_isError(zstdret)) { - archive_set_error(f->archive, - ARCHIVE_ERRNO_MISC, - "Zstd compression failed: %s", - ZSTD_getErrorName(zstdret)); - return (ARCHIVE_FATAL); - } - - /* If we're finishing, 0 means nothing left to flush */ - if (finishing && zstdret == 0) { - const int ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.pos); - return (ret); - } - } } +zstd_fatal: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Zstd compression failed: %s", + ZSTD_getErrorName(zstdret)); +fatal: + return (ARCHIVE_FATAL); } #else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ @@ -367,17 +452,9 @@ archive_compressor_zstd_open(struct archive_write_filter *f) archive_strcpy(&as, "zstd --no-check"); if (data->compression_level < CLEVEL_STD_MIN) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --fast=%d", -data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --fast=%d", -data->compression_level); } else { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " -%d", data->compression_level); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " -%d", data->compression_level); } if (data->compression_level > CLEVEL_STD_MAX) { @@ -385,11 +462,11 @@ archive_compressor_zstd_open(struct archive_write_filter *f) } if (data->threads != 0) { - struct archive_string as2; - archive_string_init(&as2); - archive_string_sprintf(&as2, " --threads=%d", data->threads); - archive_string_concat(&as, &as2); - archive_string_free(&as2); + archive_string_sprintf(&as, " --threads=%d", data->threads); + } + + if (data->long_distance != 0) { + archive_string_sprintf(&as, " --long=%d", data->long_distance); } f->write = archive_compressor_zstd_write; @@ -408,6 +485,14 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, } static int +archive_compressor_zstd_flush(struct archive_write_filter *f) +{ + (void)f; /* UNUSED */ + + return (ARCHIVE_OK); +} + +static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index bd5180e..d676ed6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c @@ -397,6 +397,7 @@ static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); +static void close_file_descriptor(struct archive_write_disk *); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); @@ -514,7 +515,12 @@ lazy_stat(struct archive_write_disk *a) * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ - if (lstat(a->name, &a->st) == 0) { +#ifdef HAVE_LSTAT + if (lstat(a->name, &a->st) == 0) +#else + if (la_stat(a->name, &a->st) == 0) +#endif + { a->pst = &a->st; return (ARCHIVE_OK); } @@ -1605,12 +1611,12 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { - int64_t skip = a->offset - a->fd_offset; + uint64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { - if (skip > (int64_t)sizeof(nullblock)) + if (skip > sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else @@ -1725,8 +1731,10 @@ _archive_write_disk_finish_entry(struct archive *_a) else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); - if (r < 0) + if (r < 0) { + close_file_descriptor(a); return ((int)r); + } } #endif } else { @@ -1735,6 +1743,7 @@ _archive_write_disk_finish_entry(struct archive *_a) a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); + close_file_descriptor(a); return (ARCHIVE_FAILED); } #endif @@ -1744,8 +1753,10 @@ _archive_write_disk_finish_entry(struct archive *_a) * to see what happened. */ a->pst = NULL; - if ((ret = lazy_stat(a)) != ARCHIVE_OK) - return (ret); + if ((ret = lazy_stat(a)) != ARCHIVE_OK) { + close_file_descriptor(a); + return (ret); + } /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { @@ -1753,11 +1764,13 @@ _archive_write_disk_finish_entry(struct archive *_a) if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); + close_file_descriptor(a); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); + close_file_descriptor(a); return (ARCHIVE_FATAL); } a->pst = NULL; @@ -2154,7 +2167,11 @@ restore_entry(struct archive_write_disk *a) * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) +#ifdef HAVE_LSTAT r = lstat(a->name, &a->st); +#else + r = la_stat(a->name, &a->st); +#endif if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); @@ -2550,7 +2567,12 @@ _archive_write_disk_close(struct archive *_a) goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; @@ -2565,7 +2587,12 @@ _archive_write_disk_close(struct archive *_a) goto skip_fixup_entry; } else #endif - if (lstat(p->name, &st) != 0 || + if ( +#ifdef HAVE_LSTAT + lstat(p->name, &st) != 0 || +#else + la_stat(p->name, &st) != 0 || +#endif la_verify_filetype(st.st_mode, p->filetype) == 0) { goto skip_fixup_entry; @@ -2785,8 +2812,8 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ - (void)error_number; /* UNUSED */ - (void)error_string; /* UNUSED */ + (void)a_eno; /* UNUSED */ + (void)a_estr; /* UNUSED */ (void)flags; /* UNUSED */ (void)checking_linkname; /* UNUSED */ return (ARCHIVE_OK); @@ -2859,8 +2886,10 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, /* Check that we haven't hit a symlink. */ #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT) r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW); -#else +#elif defined(HAVE_LSTAT) r = lstat(head, &st); +#else + r = la_stat(head, &st); #endif if (r != 0) { tail[0] = c; @@ -3558,7 +3587,9 @@ set_time(int fd, int mode, const char *name, (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ + (void)atime; /* UNUSED */ (void)atime_nsec; /* UNUSED */ + (void)mtime; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif @@ -4391,7 +4422,12 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname) */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); - if (lstat(datafork.s, &st) == -1 || + if ( +#ifdef HAVE_LSTAT + lstat(datafork.s, &st) == -1 || +#else + la_stat(datafork.s, &st) == -1 || +#endif (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; @@ -4707,5 +4743,17 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, } #endif +/* + * Close the file descriptor if one is open. + */ +static void close_file_descriptor(struct archive_write_disk* a) +{ + if (a->fd >= 0) { + close(a->fd); + a->fd = -1; + } +} + + #endif /* !_WIN32 || __CYGWIN__ */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c index 88df3ce..7b9ea74 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c @@ -254,9 +254,9 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *, * which is high-16-bits of nFileIndexHigh. */ #define bhfi_ino(bhfi) \ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \ - + (bhfi)->nFileIndexLow) + | (bhfi)->nFileIndexLow) #define bhfi_size(bhfi) \ - ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow) + ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow) static int file_information(struct archive_write_disk *a, wchar_t *path, @@ -266,6 +266,9 @@ file_information(struct archive_write_disk *a, wchar_t *path, int r; DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; WIN32_FIND_DATAW findData; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (sim_lstat || mode != NULL) { h = FindFirstFileW(path, &findData); @@ -290,14 +293,27 @@ file_information(struct archive_write_disk *a, wchar_t *path, (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))) flag |= FILE_FLAG_OPEN_REPARSE_POINT; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = flag; + h = CreateFile2(a->name, 0, 0, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(a->name, 0, 0, NULL, OPEN_EXISTING, flag, NULL); +#endif if (h == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME) { wchar_t *full; full = __la_win_permissive_name_w(path); +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + h = CreateFile2(full, 0, 0, + OPEN_EXISTING, &createExParams); +#else h = CreateFileW(full, 0, 0, NULL, OPEN_EXISTING, flag, NULL); +#endif free(full); } if (h == INVALID_HANDLE_VALUE) { @@ -559,6 +575,7 @@ la_mktemp(struct archive_write_disk *a) return (fd); } +#if _WIN32_WINNT < _WIN32_WINNT_VISTA static void * la_GetFunctionKernel32(const char *name) { @@ -574,18 +591,24 @@ la_GetFunctionKernel32(const char *name) } return (void *)GetProcAddress(lib, name); } +#endif static int la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) { - static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES); - static int set; + static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); BOOL ret; +#if _WIN32_WINNT < _WIN32_WINNT_XP + static int set; +/* CreateHardLinkW is available since XP and always loaded */ if (!set) { set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } +#else + f = CreateHardLinkW; +#endif if (!f) { errno = ENOTSUP; return (0); @@ -624,7 +647,6 @@ static int la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, int linktype) { static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); - static int set; wchar_t *ttarget, *p; size_t len; DWORD attrs = 0; @@ -632,10 +654,20 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, DWORD newflags = 0; BOOL ret = 0; +#if _WIN32_WINNT < _WIN32_WINNT_VISTA +/* CreateSymbolicLinkW is available since Vista and always loaded */ + static int set; if (!set) { set = 1; f = la_GetFunctionKernel32("CreateSymbolicLinkW"); } +#else +# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + f = CreateSymbolicLinkW; +# else + f = NULL; +# endif +#endif if (!f) return (0); @@ -1185,6 +1217,8 @@ _archive_write_disk_finish_entry(struct archive *_a) if (la_ftruncate(a->fh, a->filesize) == -1) { archive_set_error(&a->archive, errno, "File size could not be restored"); + CloseHandle(a->fh); + a->fh = INVALID_HANDLE_VALUE; return (ARCHIVE_FAILED); } } @@ -1656,6 +1690,9 @@ create_filesystem_object(struct archive_write_disk *a) mode_t final_mode, mode; int r; DWORD attrs = 0; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ @@ -1719,8 +1756,16 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + a->fh = CreateFile2(namefull, GENERIC_WRITE, 0, + TRUNCATE_EXISTING, &createExParams); +#else a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif if (a->fh == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); r = errno; @@ -1783,14 +1828,27 @@ create_filesystem_object(struct archive_write_disk *a) a->tmpname = NULL; fullname = a->name; /* O_WRONLY | O_CREAT | O_EXCL */ +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + a->fh = CreateFile2(fullname, GENERIC_WRITE, 0, + CREATE_NEW, &createExParams); +#else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); +#endif if (a->fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME && fullname == a->name) { fullname = __la_win_permissive_name_w(a->name); +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + a->fh = CreateFile2(fullname, GENERIC_WRITE, 0, + CREATE_NEW, &createExParams); +#else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); +#endif } if (a->fh == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_ACCESS_DENIED) { @@ -2551,14 +2609,25 @@ set_times(struct archive_write_disk *a, hw = NULL; } else { wchar_t *ws; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + CREATEFILE2_EXTENDED_PARAMETERS createExParams; +#endif if (S_ISLNK(mode)) return (ARCHIVE_OK); ws = __la_win_permissive_name_w(name); if (ws == NULL) goto settimes_failed; +# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ + ZeroMemory(&createExParams, sizeof(createExParams)); + createExParams.dwSize = sizeof(createExParams); + createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS; + hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0, + OPEN_EXISTING, &createExParams); +#else hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); +#endif free(ws); if (hw == INVALID_HANDLE_VALUE) goto settimes_failed; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_private.h b/Utilities/cmlibarchive/libarchive/archive_write_private.h index 155fdd7..6522e65 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_private.h @@ -53,6 +53,7 @@ struct archive_write_filter { const char *key, const char *value); int (*open)(struct archive_write_filter *); int (*write)(struct archive_write_filter *, const void *, size_t); + int (*flush)(struct archive_write_filter *); int (*close)(struct archive_write_filter *); int (*free)(struct archive_write_filter *); void *data; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c index 87b3586..1d7249f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c @@ -91,6 +91,26 @@ __FBSDID("$FreeBSD$"); #define kAttributes 0x15 #define kEncodedHeader 0x17 +// Check that some windows file attribute constants are defined. +// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants +#ifndef FILE_ATTRIBUTE_READONLY +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#endif + +#ifndef FILE_ATTRIBUTE_DIRECTORY +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#endif + +#ifndef FILE_ATTRIBUTE_ARCHIVE +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#endif + +// This value is defined in 7zip with the comment "trick for Unix". +// +// 7z archives created on unix have this bit set in the high 16 bits of +// the attr field along with the unix permissions. +#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 + enum la_zaction { ARCHIVE_Z_FINISH, ARCHIVE_Z_RUN @@ -165,7 +185,7 @@ struct file { mode_t mode; uint32_t crc32; - signed int dir:1; + unsigned dir:1; }; struct _7zip { @@ -1424,14 +1444,19 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, * High 16bits is unix mode. * Low 16bits is Windows attributes. */ - uint32_t encattr, attr; + uint32_t encattr, attr = 0; + if (file->dir) - attr = 0x8010; + attr |= FILE_ATTRIBUTE_DIRECTORY; else - attr = 0x8020; + attr |= FILE_ATTRIBUTE_ARCHIVE; + if ((file->mode & 0222) == 0) - attr |= 1;/* Read Only. */ + attr |= FILE_ATTRIBUTE_READONLY; + + attr |= FILE_ATTRIBUTE_UNIX_EXTENSION; attr |= ((uint32_t)file->mode) << 16; + archive_le32enc(&encattr, attr); r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN); if (r < 0) @@ -1809,11 +1834,11 @@ compression_init_encoder_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uint32_t)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uint32_t)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) { @@ -1842,11 +1867,11 @@ compression_code_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uint32_t)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uint32_t)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); r = BZ2_bzCompress(strm, diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c index ebd33c5..1b03170 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c @@ -293,12 +293,12 @@ struct isoent { struct extr_rec *current; } extr_rec_list; - signed int virtual:1; + unsigned int virtual:1; /* If set to one, this file type is a directory. * A convenience flag to be used as * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". */ - signed int dir:1; + unsigned int dir:1; }; struct hardlink { @@ -656,7 +656,7 @@ struct iso_option { #define VOLUME_IDENTIFIER_SIZE 32 /* - * Usage : !zisofs [DEFAULT] + * Usage : !zisofs [DEFAULT] * : Disable to generate RRIP 'ZF' extension. * : zisofs * : Make files zisofs file and generate RRIP 'ZF' @@ -693,7 +693,7 @@ struct iso9660 { uint64_t bytes_remaining; int need_multi_extent; - /* Temporary string buffer for Joliet extension. */ + /* Temporary string buffer for Joliet extension. */ struct archive_string utf16be; struct archive_string mbs; @@ -759,9 +759,9 @@ struct iso9660 { /* Used for making zisofs. */ struct { - signed int detect_magic:1; - signed int making:1; - signed int allzero:1; + unsigned int detect_magic:1; + unsigned int making:1; + unsigned int allzero:1; unsigned char magic_buffer[64]; int magic_cnt; @@ -2525,12 +2525,11 @@ get_gmoffset(struct tm *tm) static void get_tmfromtime(struct tm *tm, time_t *t) { -#if HAVE_LOCALTIME_R +#if HAVE_LOCALTIME_S + localtime_s(tm, t); +#elif HAVE_LOCALTIME_R tzset(); localtime_r(t, tm); -#elif HAVE__LOCALTIME64_S - __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits - _localtime64_s(tm, &tmp_t); #else memcpy(tm, localtime(t), sizeof(*tm)); #endif @@ -4078,11 +4077,8 @@ write_information_block(struct archive_write *a) } memset(info.s, 0, info_size); opt = 0; -#if defined(HAVE__CTIME64_S) - { - __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits - _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); - } +#if defined(HAVE_CTIME_S) + ctime_s(buf, sizeof(buf), &(iso9660->birth_time)); #elif defined(HAVE_CTIME_R) ctime_r(&(iso9660->birth_time), buf); #else @@ -7811,8 +7807,8 @@ struct zisofs_extract { uint64_t pz_uncompressed_size; size_t uncompressed_buffer_size; - signed int initialized:1; - signed int header_passed:1; + unsigned int initialized:1; + unsigned int header_passed:1; uint32_t pz_offset; unsigned char *block_pointers; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c index cf1f477..1eb9a9a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c @@ -100,6 +100,7 @@ static int has_non_ASCII(const char *); static void sparse_list_clear(struct pax *); static int sparse_list_add(struct pax *, int64_t, int64_t); static char *url_encode(const char *in); +static time_t get_ustar_max_mtime(void); /* * Set output format to 'restricted pax' format. @@ -367,10 +368,12 @@ archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name, struct archive_string s; char *encoded_value; + if (encoded_name == NULL) + return; + if (pax->flags & WRITE_LIBARCHIVE_XATTR) { encoded_value = base64_encode((const char *)value, value_len); - - if (encoded_name != NULL && encoded_value != NULL) { + if (encoded_value != NULL) { archive_string_init(&s); archive_strcpy(&s, "LIBARCHIVE.xattr."); archive_strcat(&s, encoded_name); @@ -403,17 +406,22 @@ archive_write_pax_header_xattrs(struct archive_write *a, archive_entry_xattr_next(entry, &name, &value, &size); url_encoded_name = url_encode(name); - if (url_encoded_name != NULL) { + if (url_encoded_name == NULL) + goto malloc_error; + else { /* Convert narrow-character to UTF-8. */ r = archive_strcpy_l(&(pax->l_url_encoded_name), url_encoded_name, pax->sconv_utf8); free(url_encoded_name); /* Done with this. */ if (r == 0) encoded_name = pax->l_url_encoded_name.s; - else if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Linkname"); - return (ARCHIVE_FATAL); + else if (r == -1) + goto malloc_error; + else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Error encoding pax extended attribute"); + return (ARCHIVE_FAILED); } } @@ -422,6 +430,9 @@ archive_write_pax_header_xattrs(struct archive_write *a, } return (ARCHIVE_OK); +malloc_error: + archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int @@ -595,6 +606,8 @@ archive_write_pax_header(struct archive_write *a, need_extension = 0; pax = (struct pax *)a->format_data; + const time_t ustar_max_mtime = get_ustar_max_mtime(); + /* Sanity check. */ if (archive_entry_pathname(entry_original) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -1116,16 +1129,13 @@ archive_write_pax_header(struct archive_write *a, } /* - * Technically, the mtime field in the ustar header can - * support 33 bits, but many platforms use signed 32-bit time - * values. The cutoff of 0x7fffffff here is a compromise. * Yes, this check is duplicated just below; this helps to * avoid writing an mtime attribute just to handle a * high-resolution timestamp in "restricted pax" mode. */ if (!need_extension && ((archive_entry_mtime(entry_main) < 0) - || (archive_entry_mtime(entry_main) >= 0x7fffffff))) + || (archive_entry_mtime(entry_main) >= ustar_max_mtime))) need_extension = 1; /* I use a star-compatible file flag attribute. */ @@ -1190,7 +1200,7 @@ archive_write_pax_header(struct archive_write *a, if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED || need_extension) { if (archive_entry_mtime(entry_main) < 0 || - archive_entry_mtime(entry_main) >= 0x7fffffff || + archive_entry_mtime(entry_main) >= ustar_max_mtime || archive_entry_mtime_nsec(entry_main) != 0) add_pax_attr_time(&(pax->pax_header), "mtime", archive_entry_mtime(entry_main), @@ -1428,7 +1438,7 @@ archive_write_pax_header(struct archive_write *a, /* Copy mtime, but clip to ustar limits. */ s = archive_entry_mtime(entry_main); if (s < 0) { s = 0; } - if (s >= 0x7fffffff) { s = 0x7fffffff; } + if (s > ustar_max_mtime) { s = ustar_max_mtime; } archive_entry_set_mtime(pax_attr_entry, s, 0); /* Standard ustar doesn't support atime. */ @@ -1904,14 +1914,19 @@ url_encode(const char *in) { const char *s; char *d; - int out_len = 0; + size_t out_len = 0; char *out; for (s = in; *s != '\0'; s++) { - if (*s < 33 || *s > 126 || *s == '%' || *s == '=') + if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { + if (SIZE_MAX - out_len < 4) + return (NULL); out_len += 3; - else + } else { + if (SIZE_MAX - out_len < 2) + return (NULL); out_len++; + } } out = (char *)malloc(out_len + 1); @@ -2046,3 +2061,18 @@ sparse_list_add(struct pax *pax, int64_t offset, int64_t length) return (_sparse_list_add_block(pax, offset, length, 0)); } +static time_t +get_ustar_max_mtime(void) +{ + /* + * Technically, the mtime field in the ustar header can + * support 33 bits. We are using all of them to keep + * tar/test/test_option_C_mtree.c simple and passing after 2038. + * For platforms that use signed 32-bit time values we + * use the 32-bit maximum. + */ + if (sizeof(time_t) > sizeof(int32_t)) + return (time_t)0x1ffffffff; + else + return (time_t)0x7fffffff; +} diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c index 46b0573..0ef003e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c @@ -329,30 +329,21 @@ xstrftime(struct archive_string *as, const char *fmt, time_t t) { /** like strftime(3) but for time_t objects */ struct tm *rt; -#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) struct tm timeHere; #endif -#if defined(HAVE__GMTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif char strtime[100]; size_t len; -#ifdef HAVE_GMTIME_R - if ((rt = gmtime_r(&t, &timeHere)) == NULL) - return; -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - terr = _gmtime64_s(&timeHere, &tmptime); - if (terr) - rt = NULL; - else - rt = &timeHere; +#if defined(HAVE_GMTIME_S) + rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere; +#elif defined(HAVE_GMTIME_R) + rt = gmtime_r(&t, &timeHere); #else - if ((rt = gmtime(&t)) == NULL) - return; + rt = gmtime(&t); #endif + if (!rt) + return; /* leave the hard yacker to our role model strftime() */ len = strftime(strtime, sizeof(strtime)-1, fmt, rt); archive_strncat(as, strtime, len); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c index 1e35375..1e82aa2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c @@ -212,8 +212,8 @@ struct file { struct heap_data data; struct archive_string script; - signed int virtual:1; - signed int dir:1; + unsigned int virtual:1; + unsigned int dir:1; }; struct hardlink { @@ -906,15 +906,11 @@ xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer, { char timestr[100]; struct tm tm; -#if defined(HAVE__GMTIME64_S) - __time64_t tmptime; -#endif -#if defined(HAVE_GMTIME_R) +#if defined(HAVE_GMTIME_S) + gmtime_s(&tm, &t); +#elif defined(HAVE_GMTIME_R) gmtime_r(&t, &tm); -#elif defined(HAVE__GMTIME64_S) - tmptime = t; - _gmtime64_s(&tm, &tmptime); #else memcpy(&tm, gmtime(&t), sizeof(tm)); #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c index 530e1e8..d610300 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c @@ -1382,25 +1382,14 @@ dos_time(const time_t unix_time) { struct tm *t; unsigned int dt; -#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S) +#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S) struct tm tmbuf; #endif -#if defined(HAVE__LOCALTIME64_S) - errno_t terr; - __time64_t tmptime; -#endif - /* This will not preserve time when creating/extracting the archive - * on two systems with different time zones. */ -#if defined(HAVE_LOCALTIME_R) +#if defined(HAVE_LOCALTIME_S) + t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf; +#elif defined(HAVE_LOCALTIME_R) t = localtime_r(&unix_time, &tmbuf); -#elif defined(HAVE__LOCALTIME64_S) - tmptime = unix_time; - terr = _localtime64_s(&tmbuf, &tmptime); - if (terr) - t = NULL; - else - t = &tmbuf; #else t = localtime(&unix_time); #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 index dd57358..f4b5081 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 @@ -257,6 +257,15 @@ If supported, the default value is read from The value is interpreted as a decimal integer specifying the compression level. Supported values depend on the library version, common values are from 1 to 22. +.It Cm long +Enables long distance matching. The value is interpreted as a +decimal integer specifying log2 window size in bytes. Values from +10 to 30 for 32 bit, or 31 for 64 bit, are supported. +.It Cm threads +The value is interpreted as a decimal integer specifying the +number of threads for multi-threaded zstd compression. +If set to 0, zstd will attempt to detect and use the number +of physical CPU cores. .El .It Format 7zip .Bl -tag -compact -width indent diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h index 758621c..669f272 100644 --- a/Utilities/cmlibarchive/libarchive/config_freebsd.h +++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h @@ -111,6 +111,8 @@ #define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FDOPENDIR 1 +#define HAVE_FNMATCH 1 +#define HAVE_FNMATCH_H 1 #define HAVE_FORK 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 @@ -123,6 +125,8 @@ #define HAVE_GETEUID 1 #define HAVE_GETGRGID_R 1 #define HAVE_GETGRNAM_R 1 +#define HAVE_GETLINE 1 +#define HAVE_GETOPT_OPTRESET 1 #define HAVE_GETPID 1 #define HAVE_GETPWNAM_R 1 #define HAVE_GETPWUID_R 1 @@ -201,6 +205,7 @@ #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 +#define HAVE_SYS_QUEUE_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 @@ -234,7 +239,7 @@ #define HAVE_WMEMCPY 1 #define HAVE_WMEMMOVE 1 #define HAVE_ZLIB_H 1 -#define TIME_WITH_SYS_TIME 1 +#define HAVE_SYS_TIME_H 1 #if __FreeBSD_version >= 800505 #define HAVE_LIBLZMA 1 diff --git a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c index 0b96397..9e49c56 100644 --- a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c +++ b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c @@ -31,6 +31,7 @@ #include "filter_fork.h" +#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) /* There are some editions of Windows ("nano server," for example) that * do not host user32.dll. If we want to keep running on those editions, * we need to delay-load WaitForInputIdle. */ @@ -224,6 +225,14 @@ fail: __archive_cmdline_free(acmd); return ARCHIVE_FAILED; } +#else /* !WINAPI_PARTITION_DESKTOP */ +int +__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child) +{ + (void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child; + return ARCHIVE_FAILED; +} +#endif /* !WINAPI_PARTITION_DESKTOP */ void __archive_check_child(int in, int out) diff --git a/Utilities/cmlibarchive/libarchive/xxhash.c b/Utilities/cmlibarchive/libarchive/xxhash.c index f96e9d9..beacd23 100644 --- a/Utilities/cmlibarchive/libarchive/xxhash.c +++ b/Utilities/cmlibarchive/libarchive/xxhash.c @@ -149,6 +149,10 @@ typedef struct _U32_S { U32 v; } _PACKED U32_S; #if GCC_VERSION >= 409 __attribute__((__no_sanitize_undefined__)) +#else +# if defined(__clang__) +__attribute__((no_sanitize("undefined"))) +# endif #endif #if defined(_MSC_VER) static __inline U32 A32(const void * x) diff --git a/Utilities/cmlibrhash/CMakeLists.txt b/Utilities/cmlibrhash/CMakeLists.txt index 9f532ad..317c5f8 100644 --- a/Utilities/cmlibrhash/CMakeLists.txt +++ b/Utilities/cmlibrhash/CMakeLists.txt @@ -28,6 +28,7 @@ set(librhash_sources librhash/sha512.c librhash/sha512.h librhash/ustd.h + librhash/util.c librhash/util.h ) @@ -36,5 +37,6 @@ include_directories( ) add_library(cmlibrhash ${librhash_sources}) +target_compile_definitions(cmlibrhash PRIVATE NO_IMPORT_EXPORT) install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibrhash) diff --git a/Utilities/cmlibrhash/librhash/algorithms.c b/Utilities/cmlibrhash/librhash/algorithms.c index cdd4053..08e8e4e 100644 --- a/Utilities/cmlibrhash/librhash/algorithms.c +++ b/Utilities/cmlibrhash/librhash/algorithms.c @@ -14,16 +14,15 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include <stdio.h> -#include <assert.h> - +#include "algorithms.h" #include "byte_order.h" #include "rhash.h" -#include "algorithms.h" -/* header files of all supported hash sums */ +/* header files of all supported hash functions */ #if 0 #include "aich.h" +#include "blake2b.h" +#include "blake2s.h" #include "crc32.h" #include "ed2k.h" #include "edonr.h" @@ -48,6 +47,11 @@ #endif #ifdef USE_OPENSSL +# include "plug_openssl.h" +#endif /* USE_OPENSSL */ +#include <assert.h> + +#ifdef USE_OPENSSL /* note: BTIH and AICH depends on the used SHA1 algorithm */ # define NEED_OPENSSL_INIT (RHASH_MD4 | RHASH_MD5 | \ RHASH_SHA1 | RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | RHASH_SHA512 | \ @@ -55,6 +59,7 @@ #else # define NEED_OPENSSL_INIT 0 #endif /* USE_OPENSSL */ + #ifdef GENERATE_GOST94_LOOKUP_TABLE # define NEED_GOST94_INIT (RHASH_GOST94 | RHASH_GOST94_CRYPTOPRO) #else @@ -85,10 +90,10 @@ rhash_info info_md5 = { RHASH_MD5, F_LE32, 16, "MD5", "md5" }; rhash_info info_sha1 = { RHASH_SHA1, F_BE32, 20, "SHA1", "sha1" }; #if 0 rhash_info info_tiger = { RHASH_TIGER, F_LE64, 24, "TIGER", "tiger" }; -rhash_info info_tth = { RHASH_TTH, F_BS32, 24, "TTH", "tree:tiger" }; -rhash_info info_btih = { RHASH_BTIH, 0, 20, "BTIH", "btih" }; +rhash_info info_tth = { RHASH_TTH, F_BS32 | F_SPCEXP, 24, "TTH", "tree:tiger" }; +rhash_info info_btih = { RHASH_BTIH, F_SPCEXP, 20, "BTIH", "btih" }; rhash_info info_ed2k = { RHASH_ED2K, F_LE32, 16, "ED2K", "ed2k" }; -rhash_info info_aich = { RHASH_AICH, F_BS32, 20, "AICH", "aich" }; +rhash_info info_aich = { RHASH_AICH, F_BS32 | F_SPCEXP, 20, "AICH", "aich" }; rhash_info info_whirlpool = { RHASH_WHIRLPOOL, F_BE64, 64, "WHIRLPOOL", "whirlpool" }; rhash_info info_rmd160 = { RHASH_RIPEMD160, F_LE32, 20, "RIPEMD-160", "ripemd160" }; rhash_info info_gost12_256 = { RHASH_GOST12_256, F_LE64, 32, "GOST12-256", "gost12-256" }; @@ -106,6 +111,8 @@ rhash_info info_sha512 = { RHASH_SHA512, F_BE64, 64, "SHA-512", "sha512" }; #if 0 rhash_info info_edr256 = { RHASH_EDONR256, F_LE32, 32, "EDON-R256", "edon-r256" }; rhash_info info_edr512 = { RHASH_EDONR512, F_LE64, 64, "EDON-R512", "edon-r512" }; +rhash_info info_blake2s = { RHASH_BLAKE2S, F_LE32, 32, "BLAKE2S", "blake2s" }; +rhash_info info_blake2b = { RHASH_BLAKE2B, F_LE64, 64, "BLAKE2B", "blake2b" }; #endif rhash_info info_sha3_224 = { RHASH_SHA3_224, F_LE64, 28, "SHA3-224", "sha3-224" }; rhash_info info_sha3_256 = { RHASH_SHA3_256, F_LE64, 32, "SHA3-256", "sha3-256" }; @@ -113,14 +120,13 @@ rhash_info info_sha3_384 = { RHASH_SHA3_384, F_LE64, 48, "SHA3-384", "sha3-384" rhash_info info_sha3_512 = { RHASH_SHA3_512, F_LE64, 64, "SHA3-512", "sha3-512" }; /* some helper macros */ -#define dgshft(name) (((char*)&((name##_ctx*)0)->hash) - (char*)0) -#define dgshft2(name, field) (((char*)&((name##_ctx*)0)->field) - (char*)0) +#define dgshft(name) ((uintptr_t)((char*)&((name##_ctx*)0)->hash)) +#define dgshft2(name, field) ((uintptr_t)((char*)&((name##_ctx*)0)->field)) #define ini(name) ((pinit_t)(name##_init)) #define upd(name) ((pupdate_t)(name##_update)) #define fin(name) ((pfinal_t)(name##_final)) #define iuf(name) ini(name), upd(name), fin(name) #define iuf2(name1, name2) ini(name1), upd(name2), fin(name2) -#define diuf(name) dgshft(name), ini(name), upd(name), fin(name) /* information about all supported hash functions */ rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT] = @@ -160,6 +166,8 @@ rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT] = { &info_crc32c, sizeof(uint32_t), 0, iuf(rhash_crc32c), 0 }, /* 32 bit */ { &info_snf128, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru128, rhash_snefru), 0 }, /* 128 bit */ { &info_snf256, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru256, rhash_snefru), 0 }, /* 256 bit */ + { &info_blake2s, sizeof(blake2s_ctx), dgshft(blake2s), iuf(rhash_blake2s), 0 }, /* 256 bit */ + { &info_blake2b, sizeof(blake2b_ctx), dgshft(blake2b), iuf(rhash_blake2b), 0 }, /* 512 bit */ #endif }; @@ -280,3 +288,76 @@ static void rhash_crc32c_final(uint32_t* crc32c, unsigned char* result) #endif } #endif + +#if !defined(NO_IMPORT_EXPORT) +/** + * Export a hash function context to a memory region, + * or calculate the size required for context export. + * + * @param hash_id identifier of the hash function + * @param ctx the algorithm context containing current hashing state + * @param out pointer to the memory region or NULL + * @param size size of memory region + * @return the size of the exported data on success, 0 on fail. + */ +size_t rhash_export_alg(unsigned hash_id, const void* ctx, void* out, size_t size) +{ + switch (hash_id) + { + case RHASH_TTH: + return rhash_tth_export((const tth_ctx*)ctx, out, size); + case RHASH_AICH: + return rhash_aich_export((const aich_ctx*)ctx, out, size); + } + return 0; +} + +/** + * Import a hash function context from a memory region. + * + * @param hash_id identifier of the hash function + * @param ctx pointer to the algorithm context + * @param in pointer to the data to import + * @param size size of data to import + * @return the size of the imported data on success, 0 on fail. + */ +size_t rhash_import_alg(unsigned hash_id, void* ctx, const void* in, size_t size) +{ + switch (hash_id) + { + case RHASH_TTH: + return rhash_tth_import((tth_ctx*)ctx, in, size); + case RHASH_AICH: + return rhash_aich_import((aich_ctx*)ctx, in, size); + } + return 0; +} +#endif /* !defined(NO_IMPORT_EXPORT) */ + +#ifdef USE_OPENSSL +void rhash_load_sha1_methods(rhash_hashing_methods* methods, int methods_type) +{ + int use_openssl; + switch (methods_type) { + case METHODS_OPENSSL: + use_openssl = 1; + break; + case METHODS_SELECTED: + assert(rhash_info_table[3].info->hash_id == RHASH_SHA1); + use_openssl = ARE_OPENSSL_METHODS(rhash_info_table[3]); + break; + default: + use_openssl = 0; + break; + } + if (use_openssl) { + methods->init = rhash_ossl_sha1_init(); + methods->update = rhash_ossl_sha1_update(); + methods->final = rhash_ossl_sha1_final(); + } else { + methods->init = (pinit_t)&rhash_sha1_init; + methods->update = (pupdate_t)&rhash_sha1_update; + methods->final = (pfinal_t)&rhash_sha1_final; + } +} +#endif diff --git a/Utilities/cmlibrhash/librhash/algorithms.h b/Utilities/cmlibrhash/librhash/algorithms.h index 01dda88..510b2a6 100644 --- a/Utilities/cmlibrhash/librhash/algorithms.h +++ b/Utilities/cmlibrhash/librhash/algorithms.h @@ -47,10 +47,10 @@ typedef struct rhash_info const char* magnet_name; } rhash_info; -typedef void (*pinit_t)(void*); +typedef void (*pinit_t)(void* ctx); typedef void (*pupdate_t)(void* ctx, const void* msg, size_t size); -typedef void (*pfinal_t)(void*, unsigned char*); -typedef void (*pcleanup_t)(void*); +typedef void (*pfinal_t)(void* ctx, unsigned char* result); +typedef void (*pcleanup_t)(void* ctx); /** * Information about a hash function @@ -83,11 +83,11 @@ typedef struct rhash_context_ext struct rhash_context rc; unsigned hash_vector_size; /* number of contained hash sums */ unsigned flags; - unsigned state; - void* callback; + volatile unsigned state; + rhash_callback_t callback; void* callback_data; void* bt_ctx; - rhash_vector_item vector[1]; /* contexts of contained hash sums */ + rhash_vector_item vector[]; /* contexts of contained hash sums */ } rhash_context_ext; extern rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT]; @@ -125,8 +125,9 @@ extern rhash_info info_edr512; /* rhash_info flags */ #define F_BS32 1 /* default output in base32 */ -#define F_SWAP32 2 /* Big endian flag */ +#define F_SWAP32 2 /* big endian flag */ #define F_SWAP64 4 +#define F_SPCEXP 8 /* needs special import/export logic */ /* define endianness flags */ #if IS_LITTLE_ENDIAN @@ -144,10 +145,35 @@ extern rhash_info info_edr512; void rhash_init_algorithms(unsigned mask); const rhash_info* rhash_info_by_id(unsigned hash_id); /* get hash sum info by hash id */ +#if !defined(NO_IMPORT_EXPORT) +size_t rhash_export_alg(unsigned hash_id, const void* ctx, void* out, size_t size); +size_t rhash_import_alg(unsigned hash_id, void* ctx, const void* in, size_t size); +#endif /* !defined(NO_IMPORT_EXPORT) */ + #if defined(OPENSSL_RUNTIME) && !defined(USE_OPENSSL) # define USE_OPENSSL #endif +#ifdef USE_OPENSSL +typedef struct rhash_hashing_methods +{ + pinit_t init; + pupdate_t update; + pfinal_t final; +} rhash_hashing_methods; + +enum rhash_methods_type +{ + METHODS_RHASH, + METHODS_OPENSSL, + METHODS_SELECTED, +}; + +void rhash_load_sha1_methods(rhash_hashing_methods* methods, int methods_type); + +#define ARE_OPENSSL_METHODS(methods) ((methods).init != (void (*)(void*))&rhash_sha1_init) +#endif + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/Utilities/cmlibrhash/librhash/byte_order.c b/Utilities/cmlibrhash/librhash/byte_order.c index de2c583..7a05408 100644 --- a/Utilities/cmlibrhash/librhash/byte_order.c +++ b/Utilities/cmlibrhash/librhash/byte_order.c @@ -74,7 +74,7 @@ unsigned rhash_ctz(unsigned x) void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t length) { /* if all pointers and length are 32-bits aligned */ - if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | index | length ) & 3) ) { + if ( 0 == (( (uintptr_t)to | (uintptr_t)from | (uintptr_t)index | length ) & 3) ) { /* copy memory as 32-bit words */ const uint32_t* src = (const uint32_t*)from; const uint32_t* end = (const uint32_t*)((const char*)src + length); @@ -101,7 +101,7 @@ void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t le void rhash_swap_copy_str_to_u64(void* to, int index, const void* from, size_t length) { /* if all pointers and length are 64-bits aligned */ - if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | index | length ) & 7) ) { + if ( 0 == (( (uintptr_t)to | (uintptr_t)from | (uintptr_t)index | length ) & 7) ) { /* copy aligned memory block as 64-bit integers */ const uint64_t* src = (const uint64_t*)from; const uint64_t* end = (const uint64_t*)((const char*)src + length); @@ -124,7 +124,7 @@ void rhash_swap_copy_str_to_u64(void* to, int index, const void* from, size_t le void rhash_swap_copy_u64_to_str(void* to, const void* from, size_t length) { /* if all pointers and length are 64-bits aligned */ - if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | length ) & 7) ) { + if ( 0 == (( (uintptr_t)to | (uintptr_t)from | length ) & 7) ) { /* copy aligned memory block as 64-bit integers */ const uint64_t* src = (const uint64_t*)from; const uint64_t* end = (const uint64_t*)((const char*)src + length); diff --git a/Utilities/cmlibrhash/librhash/byte_order.h b/Utilities/cmlibrhash/librhash/byte_order.h index cfb9e25..73863e0 100644 --- a/Utilities/cmlibrhash/librhash/byte_order.h +++ b/Utilities/cmlibrhash/librhash/byte_order.h @@ -76,14 +76,15 @@ extern "C" { #ifdef RHASH_BYTE_ORDER #elif defined(CPU_IA32) || defined(CPU_X64) || defined(__ia64) || defined(__ia64__) || \ defined(__alpha__) || defined(_M_ALPHA) || defined(vax) || defined(MIPSEL) || \ - defined(_ARM_) || defined(__arm__) + defined(_ARM_) || defined(__arm__) || defined(_M_ARM64) || defined(_M_ARM64EC) || \ + defined(__loongarch64) # define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE #elif defined(__sparc) || defined(__sparc__) || defined(sparc) || \ defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_POWER) || \ defined(__POWERPC__) || defined(POWERPC) || defined(__powerpc) || \ defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || \ defined(__hpux) || defined(_MIPSEB) || defined(mc68000) || \ - defined(__s390__) || defined(__s390x__) || defined(sel) + defined(__s390__) || defined(__s390x__) || defined(sel) || defined(__hppa__) # define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE #else # error "Can't detect CPU architechture" @@ -97,8 +98,8 @@ extern "C" { # define __has_builtin(x) 0 #endif -#define IS_ALIGNED_32(p) (0 == (3 & ((const char*)(p) - (const char*)0))) -#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) +#define IS_ALIGNED_32(p) (0 == (3 & (uintptr_t)(p))) +#define IS_ALIGNED_64(p) (0 == (7 & (uintptr_t)(p))) #if defined(_MSC_VER) #define ALIGN_ATTR(n) __declspec(align(n)) @@ -179,9 +180,9 @@ static RHASH_INLINE uint64_t bswap_64(uint64_t x) # define le2me_32(x) bswap_32(x) # define le2me_64(x) bswap_64(x) -# define be32_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define be32_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length)) # define le32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length)) -# define be64_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define be64_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length)) # define le64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length)) # define me64_to_be_str(to, from, length) memcpy((to), (from), (length)) # define me64_to_le_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) @@ -193,9 +194,9 @@ static RHASH_INLINE uint64_t bswap_64(uint64_t x) # define le2me_64(x) (x) # define be32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length)) -# define le32_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define le32_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length)) # define be64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length)) -# define le64_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define le64_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length)) # define me64_to_be_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) # define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) #endif /* IS_BIG_ENDIAN */ diff --git a/Utilities/cmlibrhash/librhash/hex.c b/Utilities/cmlibrhash/librhash/hex.c index cfd5892..40c2089 100644 --- a/Utilities/cmlibrhash/librhash/hex.c +++ b/Utilities/cmlibrhash/librhash/hex.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ #include "hex.h" -#include <assert.h> +#include "util.h" #include <ctype.h> #include <string.h> @@ -113,8 +113,8 @@ size_t rhash_base64_url_encoded_helper(char* dst, const unsigned char* src, size #ifdef __clang_analyzer__ memset(buffer, 0, sizeof(buffer)); #endif - assert((BASE64_LENGTH(B64_CHUNK_SIZE) + 4) <= sizeof(buffer)); - assert((B64_CHUNK_SIZE % 6) == 0); + RHASH_ASSERT((BASE64_LENGTH(B64_CHUNK_SIZE) + 4) <= sizeof(buffer)); + RHASH_ASSERT((B64_CHUNK_SIZE % 6) == 0); if (url_encode) { size_t result_length = 0; for (; length > 0; src += B64_CHUNK_SIZE) { diff --git a/Utilities/cmlibrhash/librhash/md5.c b/Utilities/cmlibrhash/librhash/md5.c index 9b76822..f989e62 100644 --- a/Utilities/cmlibrhash/librhash/md5.c +++ b/Utilities/cmlibrhash/librhash/md5.c @@ -19,13 +19,13 @@ #include "md5.h" /** - * Initialize context before calculaing hash. + * Initialize context before calculating hash. * * @param ctx context to initialize */ void rhash_md5_init(md5_ctx* ctx) { - ctx->length = 0; + memset(ctx, 0, sizeof(*ctx)); /* initialize state */ ctx->hash[0] = 0x67452301; @@ -170,7 +170,7 @@ void rhash_md5_update(md5_ctx* ctx, const unsigned char* msg, size_t size) /* fill partial block */ if (index) { unsigned left = md5_block_size - index; - le32_copy((char*)ctx->message, index, msg, (size < left ? size : left)); + le32_copy(ctx->message, index, msg, (size < left ? size : left)); if (size < left) return; /* process partial block */ diff --git a/Utilities/cmlibrhash/librhash/md5.h b/Utilities/cmlibrhash/librhash/md5.h index 12a6b52..1f6c625 100644 --- a/Utilities/cmlibrhash/librhash/md5.h +++ b/Utilities/cmlibrhash/librhash/md5.h @@ -22,7 +22,7 @@ typedef struct md5_ctx void rhash_md5_init(md5_ctx* ctx); void rhash_md5_update(md5_ctx* ctx, const unsigned char* msg, size_t size); -void rhash_md5_final(md5_ctx* ctx, unsigned char result[16]); +void rhash_md5_final(md5_ctx* ctx, unsigned char* result); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/rhash.c b/Utilities/cmlibrhash/librhash/rhash.c index 2530112..4e60c21 100644 --- a/Utilities/cmlibrhash/librhash/rhash.c +++ b/Utilities/cmlibrhash/librhash/rhash.c @@ -38,15 +38,24 @@ #include <string.h> #define STATE_ACTIVE 0xb01dbabe -#define STATE_STOPED 0xdeadbeef +#define STATE_STOPPED 0xdeadbeef #define STATE_DELETED 0xdecea5ed +#define IS_BAD_STATE(s) ((s) != STATE_ACTIVE && (s) != STATE_STOPPED) #define RCTX_AUTO_FINAL 0x1 #define RCTX_FINALIZED 0x2 #define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED) #define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64) #define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_URLENCODE | RHPR_REVERSE) -void rhash_library_init(void) +#define HAS_ZERO_OR_ONE_BIT(id) (((id) & ((id) - 1)) == 0) +#define IS_VALID_HASH_MASK(bitmask) ((bitmask) != 0 && ((bitmask) & ~RHASH_ALL_HASHES) == 0) +#define IS_VALID_HASH_ID(id) (IS_VALID_HASH_MASK(id) && HAS_ZERO_OR_ONE_BIT(id)) + +/* each hash function context must be aligned to DEFAULT_ALIGNMENT bytes */ +#define GET_CTX_ALIGNED(size) ALIGN_SIZE_BY((size), DEFAULT_ALIGNMENT) +#define GET_EXPORT_ALIGNED(size) ALIGN_SIZE_BY((size), 8) + +RHASH_API void rhash_library_init(void) { rhash_init_algorithms(RHASH_ALL_HASHES); #ifdef USE_OPENSSL @@ -54,103 +63,120 @@ void rhash_library_init(void) #endif } -int RHASH_API rhash_count(void) +RHASH_API int rhash_count(void) { return rhash_info_size; } /* LOW-LEVEL LIBRHASH INTERFACE */ -RHASH_API rhash rhash_init(unsigned hash_id) +/** + * Allocate and initialize RHash context for calculating a single or multiple hash functions. + * The context after usage must be freed by calling rhash_free(). + * + * @param count the size of the hash_ids array, the count must be greater than zero + * @param hash_ids array of identifiers of hash functions. Each element must + * be an identifier of one hash function + * @param need_init initialize context for each hash function + * @return initialized rhash context, NULL on fail with error code stored in errno + */ +static rhash_context_ext* rhash_alloc_multi(size_t count, const unsigned hash_ids[], int need_init) { - unsigned tail_bit_index; /* index of hash_id trailing bit */ - unsigned num = 0; /* number of hashes to compute */ + struct rhash_hash_info* info; /* hash algorithm information */ rhash_context_ext* rctx = NULL; /* allocated rhash context */ - size_t hash_size_sum = 0; /* size of hash contexts to store in rctx */ - - unsigned i, bit_index, id; - struct rhash_hash_info* info; - size_t aligned_size; + const size_t header_size = GET_CTX_ALIGNED(sizeof(rhash_context_ext) + sizeof(rhash_vector_item) * count); + size_t ctx_size_sum = 0; /* size of hash contexts to store in rctx */ + size_t i; char* phash_ctx; + unsigned hash_bitmask = 0; - hash_id &= RHASH_ALL_HASHES; - if (hash_id == 0) { + if (count < 1) { errno = EINVAL; return NULL; } - - tail_bit_index = rhash_ctz(hash_id); /* get trailing bit index */ - assert(tail_bit_index < RHASH_HASH_COUNT); - - id = 1 << tail_bit_index; - - if (hash_id == id) { - /* handle the most common case of only one hash */ - num = 1; - info = &rhash_info_table[tail_bit_index]; - hash_size_sum = info->context_size; - } else { - /* another case: hash_id contains several hashes */ - for (bit_index = tail_bit_index; id <= hash_id; bit_index++, id = id << 1) { - assert(id != 0); - assert(bit_index < RHASH_HASH_COUNT); - info = &rhash_info_table[bit_index]; - if (hash_id & id) { - /* align sizes by 8 bytes */ - aligned_size = (info->context_size + 7) & ~7; - hash_size_sum += aligned_size; - num++; - } + for (i = 0; i < count; i++) { + unsigned hash_index; + if (!IS_VALID_HASH_ID(hash_ids[i])) { + errno = EINVAL; + return NULL; } - assert(num > 1); - } + hash_bitmask |= hash_ids[i]; + hash_index = rhash_ctz(hash_ids[i]); + assert(hash_index < RHASH_HASH_COUNT); /* correct until extended hash_ids are supported */ + info = &rhash_info_table[hash_index]; - /* align the size of the rhash context common part */ - aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7; - assert(aligned_size >= sizeof(rhash_context_ext)); + /* align context sizes and sum up */ + ctx_size_sum += GET_CTX_ALIGNED(info->context_size); + } - /* allocate rhash context with enough memory to store contexts of all used hashes */ - rctx = (rhash_context_ext*)malloc(aligned_size + hash_size_sum); - if (rctx == NULL) return NULL; + /* allocate rhash context with enough memory to store contexts of all selected hash functions */ + rctx = (rhash_context_ext*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, header_size + ctx_size_sum); + if (rctx == NULL) + return NULL; /* initialize common fields of the rhash context */ - memset(rctx, 0, sizeof(rhash_context_ext)); - rctx->rc.hash_id = hash_id; + memset(rctx, 0, header_size); + rctx->rc.hash_id = hash_bitmask; rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */ rctx->state = STATE_ACTIVE; - rctx->hash_vector_size = num; - - /* aligned hash contexts follows rctx->vector[num] in the same memory block */ - phash_ctx = (char*)rctx + aligned_size; - assert(phash_ctx >= (char*)&rctx->vector[num]); - - /* initialize context for every hash in a loop */ - for (bit_index = tail_bit_index, id = 1 << tail_bit_index, i = 0; - id <= hash_id; bit_index++, id = id << 1) - { - /* check if a hash function with given id shall be included into rctx */ - if ((hash_id & id) != 0) { - info = &rhash_info_table[bit_index]; - assert(info->context_size > 0); - assert(((phash_ctx - (char*)0) & 7) == 0); /* hash context is aligned */ - assert(info->init != NULL); - - rctx->vector[i].hash_info = info; - rctx->vector[i].context = phash_ctx; + rctx->hash_vector_size = count; + + /* calculate aligned pointer >= (&rctx->vector[count]) */ + phash_ctx = (char*)rctx + header_size; + assert(phash_ctx >= (char*)&rctx->vector[count]); + assert(phash_ctx < ((char*)&rctx->vector[count] + DEFAULT_ALIGNMENT)); + + for (i = 0; i < count; i++) { + unsigned hash_index = rhash_ctz(hash_ids[i]); + info = &rhash_info_table[hash_index]; + assert(info->context_size > 0); + assert(info->init != NULL); + assert(IS_PTR_ALIGNED_BY(phash_ctx, DEFAULT_ALIGNMENT)); /* hash context is aligned */ + + rctx->vector[i].hash_info = info; + rctx->vector[i].context = phash_ctx; #if 0 - /* BTIH initialization is complex, save pointer for later */ - if ((id & RHASH_BTIH) != 0) rctx->bt_ctx = phash_ctx; + /* BTIH initialization is a bit complicated, so store the context pointer for later usage */ + if ((hash_ids[i] & RHASH_BTIH) != 0) + rctx->bt_ctx = phash_ctx; #endif - phash_ctx += (info->context_size + 7) & ~7; + phash_ctx += GET_CTX_ALIGNED(info->context_size); - /* initialize the i-th hash context */ + /* initialize the i-th hash context */ + if (need_init) info->init(rctx->vector[i].context); - i++; - } } + return rctx; +} - return &rctx->rc; /* return allocated and initialized rhash context */ +RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[]) +{ + rhash_context_ext* ectx = rhash_alloc_multi(count, hash_ids, 1); + return &ectx->rc; /* return initialized rhash context */ +} + +RHASH_API rhash rhash_init(unsigned hash_id) +{ + if (!IS_VALID_HASH_MASK(hash_id)) { + errno = EINVAL; + return NULL; + } + if (HAS_ZERO_OR_ONE_BIT(hash_id)) { + return rhash_init_multi(1, &hash_id); + } else { + /* handle the depricated case, when hash_id is a bitwise union of several hash function identifiers */ + size_t count; + unsigned hash_ids[32]; + unsigned id = hash_id & -hash_id; /* get the trailing bit */ + for (count = 0; id <= hash_id; id = id << 1) { + assert(id != 0); + if (hash_id & id) + hash_ids[count++] = id; + } + assert(count > 1); + return rhash_init_multi(count, hash_ids); + } } void rhash_free(rhash ctx) @@ -159,7 +185,6 @@ void rhash_free(rhash ctx) unsigned i; if (ctx == 0) return; - assert(ectx->hash_vector_size <= RHASH_HASH_COUNT); ectx->state = STATE_DELETED; /* mark memory block as being removed */ /* clean the hash functions, which require additional clean up */ @@ -169,8 +194,7 @@ void rhash_free(rhash ctx) info->cleanup(ectx->vector[i].context); } } - - free(ectx); + rhash_aligned_free(ectx); } RHASH_API void rhash_reset(rhash ctx) @@ -239,6 +263,161 @@ RHASH_API int rhash_final(rhash ctx, unsigned char* first_result) } /** + * Header block for rhash context import/export. + */ +typedef struct export_header +{ + uint32_t state; + uint16_t hash_vector_size; + uint16_t flags; + uint64_t msg_size; +} export_header; + +/** + * Process export error. Returns 0 and set errno to EINVAL. + * + * @return NULL + */ +static size_t export_error_einval(void) +{ + errno = EINVAL; + return 0; +} + +/** + * Process import error. Returns NULL and set errno to EINVAL. + * + * @return NULL + */ +static rhash import_error_einval(void) +{ + errno = EINVAL; + return NULL; +} + +RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size) +{ +#if !defined(NO_IMPORT_EXPORT) + size_t export_size; + size_t i; + rhash_context_ext* const ectx = (rhash_context_ext*)ctx; + export_header* header = (export_header*)out; + unsigned* hash_ids = NULL; + if (!ctx || (out && size < sizeof(export_header)) || IS_BAD_STATE(ectx->state)) + return export_error_einval(); + export_size = sizeof(export_header) + sizeof(unsigned) * ectx->hash_vector_size; + if (out != NULL) { + memset(out, 0, size); + header->state = ectx->state; + header->hash_vector_size = (uint16_t)(ectx->hash_vector_size); + header->flags = (uint16_t)(ectx->flags); + header->msg_size = ctx->msg_size; + hash_ids = (unsigned*)(void*)(header + 1); + } + for (i = 0; i < ectx->hash_vector_size; i++) { + void* src_context = ectx->vector[i].context; + struct rhash_hash_info* hash_info = ectx->vector[i].hash_info; + unsigned is_special = (hash_info->info->flags & F_SPCEXP); + size_t item_size; + if (out != NULL) { + if (size <= export_size) + return export_error_einval(); + hash_ids[i] = hash_info->info->hash_id; + if (is_special) { + char* dst_item; + size_t left_size; + export_size = GET_EXPORT_ALIGNED(export_size); + dst_item = (char*)out + export_size; + left_size = size - export_size; + item_size = rhash_export_alg(hash_info->info->hash_id, + src_context, dst_item, left_size); + if (!item_size) + return export_error_einval(); + } else { + char* dst_item = (char*)out + export_size; + item_size = hash_info->context_size; + if (size < (export_size + item_size)) + return export_error_einval(); + memcpy(dst_item, src_context, item_size); + } + } else { + if (is_special) { + export_size = GET_EXPORT_ALIGNED(export_size); + item_size = rhash_export_alg( + hash_info->info->hash_id, src_context, NULL, 0); + } else + item_size = hash_info->context_size; + } + export_size += item_size; + } + if (export_size < size) + return export_error_einval(); + return export_size; +#else + return export_error_einval(); +#endif /* !defined(NO_IMPORT_EXPORT) */ +} + +RHASH_API rhash rhash_import(const void* in, size_t size) +{ +#if !defined(NO_IMPORT_EXPORT) + const export_header* header = (const export_header*)in; + size_t i; + size_t imported_size; + const unsigned* hash_ids; + const char* src_item; + rhash_context_ext* ectx; + if (!header || IS_BAD_STATE(header->state) || size < sizeof(export_header)) + return import_error_einval(); + imported_size = sizeof(export_header) + sizeof(unsigned) * header->hash_vector_size; + if (!header->hash_vector_size || size < imported_size) + return import_error_einval(); + hash_ids = (const unsigned*)(const void*)(header + 1); + ectx = (rhash_context_ext*)rhash_alloc_multi(header->hash_vector_size, hash_ids, 0); + if (!ectx) + return NULL; /* errno must be set by the previous function */ + ectx->state = header->state; + ectx->hash_vector_size = header->hash_vector_size; + ectx->flags = header->flags; + ectx->rc.msg_size = header->msg_size; + for (i = 0; i < ectx->hash_vector_size; i++) { + void* dst_context = ectx->vector[i].context; + struct rhash_hash_info* hash_info = ectx->vector[i].hash_info; + unsigned is_special = (hash_info->info->flags & F_SPCEXP); + size_t item_size; + + if (is_special) { + size_t left_size; + imported_size = GET_EXPORT_ALIGNED(imported_size); + src_item = (const char*)in + imported_size; + left_size = size - imported_size; + assert(size >= imported_size); + item_size = rhash_import_alg(hash_ids[i], dst_context, src_item, left_size); + imported_size += item_size; + if (!item_size || size < imported_size) { + ectx->hash_vector_size = i + 1; /* clean only initialized contextes */ + rhash_free(&ectx->rc); + return import_error_einval(); + } + } else { + src_item = (const char*)in + imported_size; + item_size = hash_info->context_size; + imported_size += item_size; + if (size < imported_size) { + ectx->hash_vector_size = i + 1; + rhash_free(&ectx->rc); + return import_error_einval(); + } + memcpy(dst_context, src_item, item_size); + } + } + return &ectx->rc; +#else + return import_error_einval(); +#endif /* !defined(NO_IMPORT_EXPORT) */ +} + +/** * Store digest for given hash_id. * If hash_id is zero, function stores digest for a hash with the lowest id found in the context. * For nonzero hash_id the context must contain it, otherwise function silently does nothing. @@ -290,7 +469,7 @@ static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result) RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data) { - ((rhash_context_ext*)ctx)->callback = (void*)callback; + ((rhash_context_ext*)ctx)->callback = callback; ((rhash_context_ext*)ctx)->callback_data = callback_data; } @@ -313,26 +492,21 @@ RHASH_API int rhash_file_update(rhash ctx, FILE* fd) rhash_context_ext* const ectx = (rhash_context_ext*)ctx; const size_t block_size = 8192; unsigned char* buffer; - unsigned char* pmem; - size_t length = 0, align8; + size_t length = 0; int res = 0; - if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */ - + if (ectx->state != STATE_ACTIVE) + return 0; /* do nothing if canceled */ if (ctx == NULL) { errno = EINVAL; return -1; } - - pmem = (unsigned char*)malloc(block_size + 8); - if (!pmem) return -1; /* errno is set to ENOMEM according to UNIX 98 */ - - align8 = ((unsigned char*)0 - pmem) & 7; - buffer = pmem + align8; + buffer = (unsigned char*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, block_size); + if (!buffer) + return -1; /* errno is set to ENOMEM according to UNIX 98 */ while (!feof(fd)) { - /* stop if canceled */ - if (ectx->state != STATE_ACTIVE) break; - + if (ectx->state != STATE_ACTIVE) + break; /* stop if canceled */ length = fread(buffer, 1, block_size, fd); if (ferror(fd)) { @@ -346,11 +520,16 @@ RHASH_API int rhash_file_update(rhash ctx, FILE* fd) } } } - - free(buffer); + rhash_aligned_free(buffer); return res; } +#ifdef _WIN32 +# define FOPEN_MODE "rbS" +#else +# define FOPEN_MODE "rb" +#endif + RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result) { FILE* fd; @@ -363,17 +542,19 @@ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* return -1; } - if ((fd = fopen(filepath, "rb")) == NULL) return -1; + fd = fopen(filepath, FOPEN_MODE); + if (!fd) + return -1; - if ((ctx = rhash_init(hash_id)) == NULL) { + ctx = rhash_init(hash_id); + if (!ctx) { fclose(fd); return -1; } - res = rhash_file_update(ctx, fd); /* hash the file */ fclose(fd); - - rhash_final(ctx, result); + if (res >= 0) + rhash_final(ctx, result); rhash_free(ctx); return res; } @@ -393,17 +574,19 @@ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned ch return -1; } - if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1; + fd = _wfsopen(filepath, L"rbS", _SH_DENYWR); + if (!fd) + return -1; - if ((ctx = rhash_init(hash_id)) == NULL) { + ctx = rhash_init(hash_id); + if (!ctx) { fclose(fd); return -1; } - res = rhash_file_update(ctx, fd); /* hash the file */ fclose(fd); - - rhash_final(ctx, result); + if (res >= 0) + rhash_final(ctx, result); rhash_free(ctx); return res; } @@ -576,7 +759,7 @@ size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, return result_length; } -size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags) +RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id, int flags) { const rhash_info* info; unsigned char digest[80]; @@ -654,6 +837,7 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l { /* for messages working with rhash context */ rhash_context_ext* const ctx = (rhash_context_ext*)dst; + (void)rdata; switch (msg_id) { case RMSG_GET_CONTEXT: @@ -669,11 +853,11 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l case RMSG_CANCEL: /* mark rhash context as canceled, in a multithreaded program */ - atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPED); + atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPPED); return 0; case RMSG_IS_CANCELED: - return (ctx->state == STATE_STOPED); + return (ctx->state == STATE_STOPPED); case RMSG_GET_FINALIZED: return ((ctx->flags & RCTX_FINALIZED) != 0); @@ -695,6 +879,9 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l case RMSG_GET_OPENSSL_AVAILABLE_MASK: return rhash_get_openssl_available_hash_mask(); + case RMSG_GET_LIBRHASH_VERSION: + return RHASH_XVERSION; + default: return RHASH_ERROR; /* unknown message */ } diff --git a/Utilities/cmlibrhash/librhash/rhash.h b/Utilities/cmlibrhash/librhash/rhash.h index c011762..07b6d9f 100644 --- a/Utilities/cmlibrhash/librhash/rhash.h +++ b/Utilities/cmlibrhash/librhash/rhash.h @@ -52,9 +52,11 @@ enum rhash_ids RHASH_CRC32C = 0x4000000, RHASH_SNEFRU128 = 0x8000000, RHASH_SNEFRU256 = 0x10000000, + RHASH_BLAKE2S = 0x20000000, + RHASH_BLAKE2B = 0x40000000, /** - * The bit-mask containing all supported hashe functions. + * The bit-mask containing all supported hash functions. */ RHASH_ALL_HASHES = RHASH_CRC32 | RHASH_CRC32C | RHASH_MD4 | RHASH_MD5 | RHASH_ED2K | RHASH_SHA1 |RHASH_TIGER | RHASH_TTH | @@ -63,14 +65,18 @@ enum rhash_ids RHASH_HAS160 | RHASH_SNEFRU128 | RHASH_SNEFRU256 | RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | RHASH_SHA512 | RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512 | - RHASH_EDONR256 | RHASH_EDONR512, + RHASH_EDONR256 | RHASH_EDONR512 | RHASH_BLAKE2S | RHASH_BLAKE2B, RHASH_GOST = RHASH_GOST94, /* deprecated constant name */ RHASH_GOST_CRYPTOPRO = RHASH_GOST94_CRYPTOPRO, /* deprecated constant name */ + + /* bit-flag for extra hash identifiers */ + RHASH_EXTENDED_BIT = (int)0x80000000, + /** * The number of supported hash functions. */ - RHASH_HASH_COUNT = 29 + RHASH_HASH_COUNT = 31 #else RHASH_MD5 = 0x01, RHASH_SHA1 = 0x02, @@ -100,7 +106,7 @@ enum rhash_ids /** * The rhash context structure contains contexts for several hash functions. */ -typedef struct rhash_context +struct rhash_context { /** * The size of the hashed message. @@ -108,10 +114,10 @@ typedef struct rhash_context unsigned long long msg_size; /** - * The bit-mask containing identifiers of the hashes being calculated. + * The bit-mask containing identifiers of the hash functions being calculated. */ unsigned hash_id; -} rhash_context; +}; #ifndef LIBRHASH_RHASH_CTX_DEFINED #define LIBRHASH_RHASH_CTX_DEFINED @@ -135,34 +141,34 @@ RHASH_API void rhash_library_init(void); /* HIGH-LEVEL LIBRHASH INTERFACE */ /** - * Compute a hash of the given message. + * Compute a message digest of the given message. * - * @param hash_id id of hash sum to compute + * @param hash_id id of message digest to compute * @param message the message to process * @param length message length - * @param result buffer to receive binary hash string + * @param result buffer to receive the binary message digest value * @return 0 on success, -1 on error */ RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result); /** - * Compute a single hash for given file. + * Compute a single message digest for the given file. * - * @param hash_id id of hash sum to compute - * @param filepath path to the file to hash - * @param result buffer to receive hash value with the lowest requested id - * @return 0 on success, -1 on error and errno is set + * @param hash_id id of hash function to compute + * @param filepath path to the file to process + * @param result buffer to receive message digest + * @return 0 on success, -1 on fail with error code stored in errno */ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result); #ifdef _WIN32 /** - * Compute a single hash for given file (Windows-specific function). + * Compute a single message digest for the given file (Windows-specific function). * - * @param hash_id id of hash sum to compute - * @param filepath path to the file to hash - * @param result buffer to receive hash value with the lowest requested id - * @return 0 on success, -1 on error, -1 on error and errno is set + * @param hash_id id of hash function to compute + * @param filepath path to the file to process + * @param result buffer to receive the binary message digest value + * @return 0 on success, -1 on fail with error code stored in errno */ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result); #endif @@ -171,45 +177,59 @@ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned ch /* LOW-LEVEL LIBRHASH INTERFACE */ /** - * Allocate and initialize RHash context for calculating hash(es). - * After initializing rhash_update()/rhash_final() functions should be used. - * Then the context must be freed by calling rhash_free(). + * Allocate and initialize RHash context for calculating a single or multiple hash functions. + * The context after usage must be freed by calling rhash_free(). * - * @param hash_id union of bit flags, containing ids of hashes to calculate. - * @return initialized rhash context, NULL on error and errno is set + * @param count the size of the hash_ids array, the count must be greater than zero + * @param hash_ids array of identifiers of hash functions. Each element must + * be an identifier of one hash function + * @return initialized rhash context, NULL on fail with error code stored in errno + */ +RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[]); + +/** + * Allocate and initialize RHash context for calculating a single hash function. + * + * This function also supports a depricated way to initialize rhash context + * for multiple hash functions, by passing a bitwise union of several hash + * identifiers. Only single-bit identifiers (not greater than RHASH_SNEFRU256) + * can be used in such bitwise union. + * + * @param hash_id identifier of a hash function + * @return initialized rhash context, NULL on fail with error code stored in errno */ RHASH_API rhash rhash_init(unsigned hash_id); /** - * Calculate hashes of message. + * Calculate message digests of message. * Can be called repeatedly with chunks of the message to be hashed. * * @param ctx the rhash context * @param message message chunk * @param length length of the message chunk - * @return 0 on success; On fail return -1 and set errno + * @return 0 on success, -1 on fail with error code stored in errno */ RHASH_API int rhash_update(rhash ctx, const void* message, size_t length); /** - * Hash a file or stream. Multiple hashes can be computed. + * Process a file or stream. Multiple message digests can be computed. * First, inintialize ctx parameter with rhash_init() before calling * rhash_file_update(). Then use rhash_final() and rhash_print() - * to retrive hash values. Finaly call rhash_free() on ctx + * to retrive message digests. Finaly call rhash_free() on ctx * to free allocated memory or call rhash_reset() to reuse ctx. * * @param ctx rhash context * @param fd descriptor of the file to hash - * @return 0 on success, -1 on error and errno is set + * @return 0 on success, -1 on fail with error code stored in errno */ RHASH_API int rhash_file_update(rhash ctx, FILE* fd); /** - * Finalize hash calculation and optionally store the first hash. + * Finalize message digest calculation and optionally store the first message digest. * * @param ctx the rhash context - * @param first_result optional buffer to store a calculated hash with the lowest available id - * @return 0 on success; On fail return -1 and set errno + * @param first_result optional buffer to store a calculated message digest with the lowest available id + * @return 0 on success, -1 on fail with error code stored in errno */ RHASH_API int rhash_final(rhash ctx, unsigned char* first_result); @@ -224,7 +244,7 @@ RHASH_API void rhash_reset(rhash ctx); /** * Free RHash context memory. * - * @param ctx the context to free. + * @param ctx the context to free */ RHASH_API void rhash_free(rhash ctx); @@ -238,8 +258,33 @@ RHASH_API void rhash_free(rhash ctx); * @param callback pointer to the callback function * @param callback_data pointer to data passed to the callback */ -RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data); +RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data); +/** + * Export RHash context data to a memory region. + * The size of the memory required for export + * is returned by rhash_export(ctx, NULL, 0). + * + * @param ctx the rhash context to export + * @param out pointer to a memory region, or NULL + * @param size the size of a memory region + * @return the size of exported data on success export. + * The size of memory required for export if out is NULL. + * 0 on fail with error code stored in errno + */ +RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size); + +/** + * Import rhash context from a memory region. + * The returned rhash context must be released after usage + * by rhash_free(). + * + * @param in pointer to a memory region + * @param size the size of a memory region + * @return imported rhash context on success, + * NULL on fail with error code stored in errno + */ +RHASH_API rhash rhash_import(const void* in, size_t size); /* INFORMATION FUNCTIONS */ @@ -248,37 +293,37 @@ RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* c * * @return the number of supported hash functions */ -RHASH_API int rhash_count(void); /* number of supported hashes */ +RHASH_API int rhash_count(void); /** - * Returns size of binary digest for given hash algorithm. + * Returns the size of binary message digest for given hash function. * - * @param hash_id the id of hash algorithm - * @return digest size in bytes + * @param hash_id the id of the hash function + * @return the size of the message digest in bytes */ -RHASH_API int rhash_get_digest_size(unsigned hash_id); /* size of binary message digest */ +RHASH_API int rhash_get_digest_size(unsigned hash_id); /** - * Returns length of digest hash string in default output format. + * Returns the length of message digest string in its default output format. * - * @param hash_id the id of hash algorithm - * @return the length of hash string + * @param hash_id the id of the hash function + * @return the length of the message digest */ -RHASH_API int rhash_get_hash_length(unsigned hash_id); /* length of formatted hash string */ +RHASH_API int rhash_get_hash_length(unsigned hash_id); /** - * Detect default digest output format for given hash algorithm. + * Detect default message digest output format for the given hash algorithm. * * @param hash_id the id of hash algorithm * @return 1 for base32 format, 0 for hexadecimal */ -RHASH_API int rhash_is_base32(unsigned hash_id); /* default digest output format */ +RHASH_API int rhash_is_base32(unsigned hash_id); /** - * Returns a name of given hash algorithm. + * Returns the name of the given hash function. * - * @param hash_id the id of hash algorithm - * @return algorithm name + * @param hash_id id of the hash function + * @return hash function name */ RHASH_API const char* rhash_get_name(unsigned hash_id); /* get hash function name */ @@ -287,7 +332,7 @@ RHASH_API const char* rhash_get_name(unsigned hash_id); /* get hash function nam * Such magnet_name is used to generate a magnet link of the form * urn:<magnet_name>=<hash_value>. * - * @param hash_id the id of hash algorithm + * @param hash_id id of the hash algorithm * @return name */ RHASH_API const char* rhash_get_magnet_name(unsigned hash_id); /* get name part of magnet urn */ @@ -296,7 +341,7 @@ RHASH_API const char* rhash_get_magnet_name(unsigned hash_id); /* get name part #if 0 /** - * Flags for printing a hash sum. + * Flags for printing a message digest. */ enum rhash_print_sum_flags { @@ -326,7 +371,7 @@ enum rhash_print_sum_flags */ RHPR_UPPERCASE = 0x8, /* - * Reverse hash bytes. Can be used for GOST hash. + * Reverse message digest bytes. Can be used for GOST hash functions. */ RHPR_REVERSE = 0x10, /* @@ -346,12 +391,12 @@ enum rhash_print_sum_flags /** - * Print a text presentation of a given hash sum to the specified buffer. + * Print to the specified buffer the text representation of the given message digest. * - * @param output a buffer to print the hash to - * @param bytes a hash sum to print - * @param size a size of hash sum in bytes - * @param flags a bit-mask controlling how to format the hash sum, + * @param output a buffer to print the message digest to + * @param bytes a binary message digest to print + * @param size a size of the message digest in bytes + * @param flags a bit-mask controlling how to format the message digest, * can be a mix of the flags: RHPR_RAW, RHPR_HEX, RHPR_BASE32, * RHPR_BASE64, RHPR_URLENCODE, RHPR_UPPERCASE, RHPR_REVERSE * @return the number of written characters @@ -360,33 +405,33 @@ RHASH_API size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, int flags); /** - * Print text presentation of a hash sum with given hash_id to the specified - * output buffer. If the hash_id is zero, then print the hash sum with - * the lowest id stored in the hash context. - * The function call fails if the context doesn't include a hash with the + * Print to the specified output buffer the text representation of the message digest + * with the given hash_id. If the hash_id is zero, then print the message digest with + * the lowest hash_id calculated by the hash context. + * The function call fails if the context doesn't include the message digest with the * given hash_id. * - * @param output a buffer to print the hash to - * @param ctx algorithms state - * @param hash_id id of the hash sum to print or 0 to print the first hash - * saved in the context. - * @param flags a bitmask controlling how to print the hash. Can contain flags - * RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc. + * @param output a buffer to print the message digest to + * @param ctx algorithms state + * @param hash_id id of the message digest to print or 0 to print the first + * message digest saved in the context. + * @param flags a bitmask controlling how to print the message digest. Can contain + * flags RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc. * @return the number of written characters on success or 0 on fail */ RHASH_API size_t rhash_print(char* output, rhash ctx, unsigned hash_id, int flags); /** - * Print magnet link with given filepath and calculated hash sums into the - * output buffer. The hash_mask can limit which hash values will be printed. + * Print magnet link with given filepath and calculated message digest into the + * output buffer. The hash_mask can limit which message digests will be printed. * The function returns the size of the required buffer. * If output is NULL the . * * @param output a string buffer to receive the magnet link or NULL * @param filepath the file path to be printed or NULL * @param context algorithms state - * @param hash_mask bit mask of the hash sums to add to the link + * @param hash_mask bit mask of the message digest to add to the link * @param flags can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET, * RHPR_FILESIZE * @return number of written characters, including terminating '\0' on success, 0 on fail @@ -445,19 +490,20 @@ RHASH_API rhash_uptr_t rhash_transmit( #define RMSG_GET_OPENSSL_MASK 11 #define RMSG_GET_OPENSSL_SUPPORTED_MASK 12 #define RMSG_GET_OPENSSL_AVAILABLE_MASK 13 +#define RMSG_GET_LIBRHASH_VERSION 20 /* HELPER MACROS */ /** - * Get a pointer to context of the specified hash function. + * Get a pointer to the context of the specified hash function. */ #define rhash_get_context_ptr(ctx, hash_id) RHASH_UPTR2PVOID(rhash_transmit(RMSG_GET_CONTEXT, ctx, hash_id, 0)) /** - * Cancel hash calculation of a file. + * Cancel file processing. */ #define rhash_cancel(ctx) rhash_transmit(RMSG_CANCEL, ctx, 0, 0) /** - * Return non-zero if hash calculation was canceled, zero otherwise. + * Return non-zero if a message digest calculation was canceled, zero otherwise. */ #define rhash_is_canceled(ctx) rhash_transmit(RMSG_IS_CANCELED, ctx, 0, 0) /** @@ -468,7 +514,7 @@ RHASH_API rhash_uptr_t rhash_transmit( /** * Turn on/off the auto-final flag for the given rhash_context. By default * auto-final is on, which means rhash_final is called automatically, if - * needed when a hash value is retrieved by rhash_print call. + * needed when a message digest is retrieved by rhash_print call. */ #define rhash_set_autofinal(ctx, on) rhash_transmit(RMSG_SET_AUTOFINAL, ctx, on, 0) @@ -500,9 +546,13 @@ RHASH_API rhash_uptr_t rhash_transmit( */ #define rhash_get_openssl_available_mask() rhash_transmit(RMSG_GET_OPENSSL_AVAILABLE_MASK, NULL, 0, 0) +/** + * Return librhash version. + */ +#define rhash_get_version() rhash_transmit(RMSG_GET_LIBRHASH_VERSION, NULL, 0, 0) /** - * Return non-zero if LibRHash hash been compiled with OpenSSL support, + * Return non-zero if LibRHash has been compiled with OpenSSL support, * and zero otherwise. */ #define rhash_is_openssl_supported() (rhash_get_openssl_mask() != RHASH_ERROR) diff --git a/Utilities/cmlibrhash/librhash/sha1.c b/Utilities/cmlibrhash/librhash/sha1.c index b226925..cbc2b72 100644 --- a/Utilities/cmlibrhash/librhash/sha1.c +++ b/Utilities/cmlibrhash/librhash/sha1.c @@ -20,7 +20,7 @@ #include "sha1.h" /** - * Initialize context before calculaing hash. + * Initialize context before calculating hash. * * @param ctx context to initialize */ @@ -36,6 +36,23 @@ void rhash_sha1_init(sha1_ctx* ctx) ctx->hash[4] = 0xc3d2e1f0; } +/* constants for SHA1 rounds */ +static const uint32_t K0 = 0x5a827999; +static const uint32_t K1 = 0x6ed9eba1; +static const uint32_t K2 = 0x8f1bbcdc; +static const uint32_t K3 = 0xca62c1d6; + +/* round functions for SHA1 */ +#define CHO(X,Y,Z) (((X)&(Y))|((~(X))&(Z))) +#define PAR(X,Y,Z) ((X)^(Y)^(Z)) +#define MAJ(X,Y,Z) (((X)&(Y))|((X)&(Z))|((Y)&(Z))) + +#define ROUND_0(a,b,c,d,e, FF, k, w) e += FF(b, c, d )+ROTL32(a,5)+k+w +#define ROUND_1(a,b,c,d,e, FF, k, w) e += FF(b,ROTL32(c,30), d )+ROTL32(a,5)+k+w +#define ROUND_2(a,b,c,d,e, FF, k, w) e += FF(b,ROTL32(c,30),ROTL32(d,30))+ROTL32(a,5)+k+w +#define ROUND(a,b,c,d,e, FF, k, w) e = ROTL32(e,30)+FF(b,ROTL32(c,30),ROTL32(d,30))+ROTL32(a,5)+k+w + + /** * The core transformation. Process a 512-bit block. * The function has been taken from RFC 3174 with little changes. @@ -45,21 +62,9 @@ void rhash_sha1_init(sha1_ctx* ctx) */ static void rhash_sha1_process_block(unsigned* hash, const unsigned* block) { - int t; /* Loop counter */ - uint32_t temp; /* Temporary word value */ uint32_t W[80]; /* Word sequence */ uint32_t A, B, C, D, E; /* Word buffers */ - /* initialize the first 16 words in the array W */ - for (t = 0; t < 16; t++) { - /* note: it is much faster to apply be2me here, then using be32_copy */ - W[t] = be2me_32(block[t]); - } - - /* initialize the rest */ - for (t = 16; t < 80; t++) { - W[t] = ROTL32(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); - } A = hash[0]; B = hash[1]; @@ -67,50 +72,189 @@ static void rhash_sha1_process_block(unsigned* hash, const unsigned* block) D = hash[3]; E = hash[4]; - for (t = 0; t < 20; t++) { - /* the following is faster than ((B & C) | ((~B) & D)) */ - temp = ROTL32(A, 5) + (((C ^ D) & B) ^ D) - + E + W[t] + 0x5A827999; - E = D; - D = C; - C = ROTL32(B, 30); - B = A; - A = temp; - } + /* 0..19 */ + W[ 0] = be2me_32(block[ 0]); + ROUND_0(A,B,C,D,E, CHO, K0, W[ 0]); + W[ 1] = be2me_32(block[ 1]); + ROUND_1(E,A,B,C,D, CHO, K0, W[ 1]); + W[ 2] = be2me_32(block[ 2]); + ROUND_2(D,E,A,B,C, CHO, K0, W[ 2]); + W[ 3] = be2me_32(block[ 3]); + ROUND(C,D,E,A,B, CHO, K0, W[ 3]); + W[ 4] = be2me_32(block[ 4]); + ROUND(B,C,D,E,A, CHO, K0, W[ 4]); - for (t = 20; t < 40; t++) { - temp = ROTL32(A, 5) + (B ^ C ^ D) + E + W[t] + 0x6ED9EBA1; - E = D; - D = C; - C = ROTL32(B, 30); - B = A; - A = temp; - } + W[ 5] = be2me_32(block[ 5]); + ROUND(A,B,C,D,E, CHO, K0, W[ 5]); + W[ 6] = be2me_32(block[ 6]); + ROUND(E,A,B,C,D, CHO, K0, W[ 6]); + W[ 7] = be2me_32(block[ 7]); + ROUND(D,E,A,B,C, CHO, K0, W[ 7]); + W[ 8] = be2me_32(block[ 8]); + ROUND(C,D,E,A,B, CHO, K0, W[ 8]); + W[ 9] = be2me_32(block[ 9]); + ROUND(B,C,D,E,A, CHO, K0, W[ 9]); - for (t = 40; t < 60; t++) { - temp = ROTL32(A, 5) + ((B & C) | (B & D) | (C & D)) - + E + W[t] + 0x8F1BBCDC; - E = D; - D = C; - C = ROTL32(B, 30); - B = A; - A = temp; - } + W[10] = be2me_32(block[10]); + ROUND(A,B,C,D,E, CHO, K0, W[10]); + W[11] = be2me_32(block[11]); + ROUND(E,A,B,C,D, CHO, K0, W[11]); + W[12] = be2me_32(block[12]); + ROUND(D,E,A,B,C, CHO, K0, W[12]); + W[13] = be2me_32(block[13]); + ROUND(C,D,E,A,B, CHO, K0, W[13]); + W[14] = be2me_32(block[14]); + ROUND(B,C,D,E,A, CHO, K0, W[14]); + + W[15] = be2me_32(block[15]); + ROUND(A,B,C,D,E, CHO, K0, W[15]); + W[16] = ROTL32(W[13] ^ W[ 8] ^ W[ 2] ^ W[ 0], 1); + ROUND(E,A,B,C,D, CHO, K0, W[16]); + W[17] = ROTL32(W[14] ^ W[ 9] ^ W[ 3] ^ W[ 1], 1); + ROUND(D,E,A,B,C, CHO, K0, W[17]); + W[18] = ROTL32(W[15] ^ W[10] ^ W[ 4] ^ W[ 2], 1); + ROUND(C,D,E,A,B, CHO, K0, W[18]); + W[19] = ROTL32(W[16] ^ W[11] ^ W[ 5] ^ W[ 3], 1); + ROUND(B,C,D,E,A, CHO, K0, W[19]); + /* 20..39 */ + W[20] = ROTL32(W[17] ^ W[12] ^ W[ 6] ^ W[ 4], 1); + ROUND(A,B,C,D,E, PAR, K1, W[20]); + W[21] = ROTL32(W[18] ^ W[13] ^ W[ 7] ^ W[ 5], 1); + ROUND(E,A,B,C,D, PAR, K1, W[21]); + W[22] = ROTL32(W[19] ^ W[14] ^ W[ 8] ^ W[ 6], 1); + ROUND(D,E,A,B,C, PAR, K1, W[22]); + W[23] = ROTL32(W[20] ^ W[15] ^ W[ 9] ^ W[ 7], 1); + ROUND(C,D,E,A,B, PAR, K1, W[23]); + W[24] = ROTL32(W[21] ^ W[16] ^ W[10] ^ W[ 8], 1); + ROUND(B,C,D,E,A, PAR, K1, W[24]); + + W[25] = ROTL32(W[22] ^ W[17] ^ W[11] ^ W[ 9], 1); + ROUND(A,B,C,D,E, PAR, K1, W[25]); + W[26] = ROTL32(W[23] ^ W[18] ^ W[12] ^ W[10], 1); + ROUND(E,A,B,C,D, PAR, K1, W[26]); + W[27] = ROTL32(W[24] ^ W[19] ^ W[13] ^ W[11], 1); + ROUND(D,E,A,B,C, PAR, K1, W[27]); + W[28] = ROTL32(W[25] ^ W[20] ^ W[14] ^ W[12], 1); + ROUND(C,D,E,A,B, PAR, K1, W[28]); + W[29] = ROTL32(W[26] ^ W[21] ^ W[15] ^ W[13], 1); + ROUND(B,C,D,E,A, PAR, K1, W[29]); + + W[30] = ROTL32(W[27] ^ W[22] ^ W[16] ^ W[14], 1); + ROUND(A,B,C,D,E, PAR, K1, W[30]); + W[31] = ROTL32(W[28] ^ W[23] ^ W[17] ^ W[15], 1); + ROUND(E,A,B,C,D, PAR, K1, W[31]); + W[32] = ROTL32(W[29] ^ W[24] ^ W[18] ^ W[16], 1); + ROUND(D,E,A,B,C, PAR, K1, W[32]); + W[33] = ROTL32(W[30] ^ W[25] ^ W[19] ^ W[17], 1); + ROUND(C,D,E,A,B, PAR, K1, W[33]); + W[34] = ROTL32(W[31] ^ W[26] ^ W[20] ^ W[18], 1); + ROUND(B,C,D,E,A, PAR, K1, W[34]); + + W[35] = ROTL32(W[32] ^ W[27] ^ W[21] ^ W[19], 1); + ROUND(A,B,C,D,E, PAR, K1, W[35]); + W[36] = ROTL32(W[33] ^ W[28] ^ W[22] ^ W[20], 1); + ROUND(E,A,B,C,D, PAR, K1, W[36]); + W[37] = ROTL32(W[34] ^ W[29] ^ W[23] ^ W[21], 1); + ROUND(D,E,A,B,C, PAR, K1, W[37]); + W[38] = ROTL32(W[35] ^ W[30] ^ W[24] ^ W[22], 1); + ROUND(C,D,E,A,B, PAR, K1, W[38]); + W[39] = ROTL32(W[36] ^ W[31] ^ W[25] ^ W[23], 1); + ROUND(B,C,D,E,A, PAR, K1, W[39]); + /* 40..59 */ + W[40] = ROTL32(W[37] ^ W[32] ^ W[26] ^ W[24], 1); + ROUND(A,B,C,D,E, MAJ, K2, W[40]); + W[41] = ROTL32(W[38] ^ W[33] ^ W[27] ^ W[25], 1); + ROUND(E,A,B,C,D, MAJ, K2, W[41]); + W[42] = ROTL32(W[39] ^ W[34] ^ W[28] ^ W[26], 1); + ROUND(D,E,A,B,C, MAJ, K2, W[42]); + W[43] = ROTL32(W[40] ^ W[35] ^ W[29] ^ W[27], 1); + ROUND(C,D,E,A,B, MAJ, K2, W[43]); + W[44] = ROTL32(W[41] ^ W[36] ^ W[30] ^ W[28], 1); + ROUND(B,C,D,E,A, MAJ, K2, W[44]); + + W[45] = ROTL32(W[42] ^ W[37] ^ W[31] ^ W[29], 1); + ROUND(A,B,C,D,E, MAJ, K2, W[45]); + W[46] = ROTL32(W[43] ^ W[38] ^ W[32] ^ W[30], 1); + ROUND(E,A,B,C,D, MAJ, K2, W[46]); + W[47] = ROTL32(W[44] ^ W[39] ^ W[33] ^ W[31], 1); + ROUND(D,E,A,B,C, MAJ, K2, W[47]); + W[48] = ROTL32(W[45] ^ W[40] ^ W[34] ^ W[32], 1); + ROUND(C,D,E,A,B, MAJ, K2, W[48]); + W[49] = ROTL32(W[46] ^ W[41] ^ W[35] ^ W[33], 1); + ROUND(B,C,D,E,A, MAJ, K2, W[49]); + + W[50] = ROTL32(W[47] ^ W[42] ^ W[36] ^ W[34], 1); + ROUND(A,B,C,D,E, MAJ, K2, W[50]); + W[51] = ROTL32(W[48] ^ W[43] ^ W[37] ^ W[35], 1); + ROUND(E,A,B,C,D, MAJ, K2, W[51]); + W[52] = ROTL32(W[49] ^ W[44] ^ W[38] ^ W[36], 1); + ROUND(D,E,A,B,C, MAJ, K2, W[52]); + W[53] = ROTL32(W[50] ^ W[45] ^ W[39] ^ W[37], 1); + ROUND(C,D,E,A,B, MAJ, K2, W[53]); + W[54] = ROTL32(W[51] ^ W[46] ^ W[40] ^ W[38], 1); + ROUND(B,C,D,E,A, MAJ, K2, W[54]); + + W[55] = ROTL32(W[52] ^ W[47] ^ W[41] ^ W[39], 1); + ROUND(A,B,C,D,E, MAJ, K2, W[55]); + W[56] = ROTL32(W[53] ^ W[48] ^ W[42] ^ W[40], 1); + ROUND(E,A,B,C,D, MAJ, K2, W[56]); + W[57] = ROTL32(W[54] ^ W[49] ^ W[43] ^ W[41], 1); + ROUND(D,E,A,B,C, MAJ, K2, W[57]); + W[58] = ROTL32(W[55] ^ W[50] ^ W[44] ^ W[42], 1); + ROUND(C,D,E,A,B, MAJ, K2, W[58]); + W[59] = ROTL32(W[56] ^ W[51] ^ W[45] ^ W[43], 1); + ROUND(B,C,D,E,A, MAJ, K2, W[59]); + /* 60..79 */ + W[60] = ROTL32(W[57] ^ W[52] ^ W[46] ^ W[44], 1); + ROUND(A,B,C,D,E, PAR, K3, W[60]); + W[61] = ROTL32(W[58] ^ W[53] ^ W[47] ^ W[45], 1); + ROUND(E,A,B,C,D, PAR, K3, W[61]); + W[62] = ROTL32(W[59] ^ W[54] ^ W[48] ^ W[46], 1); + ROUND(D,E,A,B,C, PAR, K3, W[62]); + W[63] = ROTL32(W[60] ^ W[55] ^ W[49] ^ W[47], 1); + ROUND(C,D,E,A,B, PAR, K3, W[63]); + W[64] = ROTL32(W[61] ^ W[56] ^ W[50] ^ W[48], 1); + ROUND(B,C,D,E,A, PAR, K3, W[64]); + + W[65] = ROTL32(W[62] ^ W[57] ^ W[51] ^ W[49], 1); + ROUND(A,B,C,D,E, PAR, K3, W[65]); + W[66] = ROTL32(W[63] ^ W[58] ^ W[52] ^ W[50], 1); + ROUND(E,A,B,C,D, PAR, K3, W[66]); + W[67] = ROTL32(W[64] ^ W[59] ^ W[53] ^ W[51], 1); + ROUND(D,E,A,B,C, PAR, K3, W[67]); + W[68] = ROTL32(W[65] ^ W[60] ^ W[54] ^ W[52], 1); + ROUND(C,D,E,A,B, PAR, K3, W[68]); + W[69] = ROTL32(W[66] ^ W[61] ^ W[55] ^ W[53], 1); + ROUND(B,C,D,E,A, PAR, K3, W[69]); + + W[70] = ROTL32(W[67] ^ W[62] ^ W[56] ^ W[54], 1); + ROUND(A,B,C,D,E, PAR, K3, W[70]); + W[71] = ROTL32(W[68] ^ W[63] ^ W[57] ^ W[55], 1); + ROUND(E,A,B,C,D, PAR, K3, W[71]); + W[72] = ROTL32(W[69] ^ W[64] ^ W[58] ^ W[56], 1); + ROUND(D,E,A,B,C, PAR, K3, W[72]); + W[73] = ROTL32(W[70] ^ W[65] ^ W[59] ^ W[57], 1); + ROUND(C,D,E,A,B, PAR, K3, W[73]); + W[74] = ROTL32(W[71] ^ W[66] ^ W[60] ^ W[58], 1); + ROUND(B,C,D,E,A, PAR, K3, W[74]); + + W[75] = ROTL32(W[72] ^ W[67] ^ W[61] ^ W[59], 1); + ROUND(A,B,C,D,E, PAR, K3, W[75]); + W[76] = ROTL32(W[73] ^ W[68] ^ W[62] ^ W[60], 1); + ROUND(E,A,B,C,D, PAR, K3, W[76]); + W[77] = ROTL32(W[74] ^ W[69] ^ W[63] ^ W[61], 1); + ROUND(D,E,A,B,C, PAR, K3, W[77]); + W[78] = ROTL32(W[75] ^ W[70] ^ W[64] ^ W[62], 1); + ROUND(C,D,E,A,B, PAR, K3, W[78]); + W[79] = ROTL32(W[76] ^ W[71] ^ W[65] ^ W[63], 1); + ROUND(B,C,D,E,A, PAR, K3, W[79]); - for (t = 60; t < 80; t++) { - temp = ROTL32(A, 5) + (B ^ C ^ D) + E + W[t] + 0xCA62C1D6; - E = D; - D = C; - C = ROTL32(B, 30); - B = A; - A = temp; - } hash[0] += A; hash[1] += B; - hash[2] += C; - hash[3] += D; - hash[4] += E; + hash[2] += ROTL32(C,30); + hash[3] += ROTL32(D,30); + hash[4] += ROTL32(E,30); } /** diff --git a/Utilities/cmlibrhash/librhash/sha256.c b/Utilities/cmlibrhash/librhash/sha256.c index 21a69aa..69d28ce 100644 --- a/Utilities/cmlibrhash/librhash/sha256.c +++ b/Utilities/cmlibrhash/librhash/sha256.c @@ -61,7 +61,7 @@ static const unsigned rhash_k256[64] = { ROUND(a,b,c,d,e,f,g,h, k[n], RECALCULATE_W(W, n)) /** - * Initialize context before calculaing hash. + * Initialize context before calculating hash. * * @param ctx context to initialize */ @@ -74,7 +74,7 @@ void rhash_sha256_init(sha256_ctx* ctx) 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; - + memset(ctx->message, 0, sizeof(ctx->message)); ctx->length = 0; ctx->digest_length = sha256_hash_size; @@ -83,7 +83,7 @@ void rhash_sha256_init(sha256_ctx* ctx) } /** - * Initialize context before calculaing hash. + * Initialize context before calculating hash. * * @param ctx context to initialize */ @@ -96,7 +96,7 @@ void rhash_sha224_init(struct sha256_ctx* ctx) 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 }; - + memset(ctx->message, 0, sizeof(ctx->message)); ctx->length = 0; ctx->digest_length = sha224_hash_size; diff --git a/Utilities/cmlibrhash/librhash/sha256.h b/Utilities/cmlibrhash/librhash/sha256.h index 3625cfe..33ce9d9 100644 --- a/Utilities/cmlibrhash/librhash/sha256.h +++ b/Utilities/cmlibrhash/librhash/sha256.h @@ -23,7 +23,7 @@ typedef struct sha256_ctx void rhash_sha224_init(sha256_ctx* ctx); void rhash_sha256_init(sha256_ctx* ctx); void rhash_sha256_update(sha256_ctx* ctx, const unsigned char* data, size_t length); -void rhash_sha256_final(sha256_ctx* ctx, unsigned char result[32]); +void rhash_sha256_final(sha256_ctx* ctx, unsigned char* result); #ifdef __cplusplus } /* extern "C" */ diff --git a/Utilities/cmlibrhash/librhash/sha512.c b/Utilities/cmlibrhash/librhash/sha512.c index 555e6ef..a9901dd 100644 --- a/Utilities/cmlibrhash/librhash/sha512.c +++ b/Utilities/cmlibrhash/librhash/sha512.c @@ -91,7 +91,7 @@ void rhash_sha512_init(sha512_ctx* ctx) I64(0xa54ff53a5f1d36f1), I64(0x510e527fade682d1), I64(0x9b05688c2b3e6c1f), I64(0x1f83d9abfb41bd6b), I64(0x5be0cd19137e2179) }; - + memset(ctx->message, 0, sizeof(ctx->message)); ctx->length = 0; ctx->digest_length = sha512_hash_size; @@ -100,7 +100,7 @@ void rhash_sha512_init(sha512_ctx* ctx) } /** - * Initialize context before calculaing hash. + * Initialize context before calculating hash. * * @param ctx context to initialize */ @@ -114,7 +114,7 @@ void rhash_sha384_init(struct sha512_ctx* ctx) I64(0x152fecd8f70e5939), I64(0x67332667ffc00b31), I64(0x8eb44a8768581511), I64(0xdb0c2e0d64f98fa7), I64(0x47b5481dbefa4fa4) }; - + memset(ctx->message, 0, sizeof(ctx->message)); ctx->length = 0; ctx->digest_length = sha384_hash_size; diff --git a/Utilities/cmlibrhash/librhash/util.c b/Utilities/cmlibrhash/librhash/util.c new file mode 100644 index 0000000..8266460 --- /dev/null +++ b/Utilities/cmlibrhash/librhash/util.c @@ -0,0 +1,61 @@ +/* util.c - memory functions. + * + * Copyright (c) 2020, Aleksey Kravchenko <rhash.admin@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include "util.h" + +#if defined(HAS_POSIX_ALIGNED_ALLOC) + +#include <errno.h> + +void* rhash_px_aalloc(size_t alignment, size_t size) +{ + void* ptr; + if ((errno = posix_memalign(&ptr, alignment, size)) != 0) + return NULL; + return ptr; +} + +#elif defined(HAS_GENERIC_ALIGNED_ALLOC) + +#include <assert.h> +#include <stdlib.h> + +void* rhash_aligned_alloc(size_t alignment, size_t size) +{ + unsigned char* block = (unsigned char*)malloc(size + alignment); + assert((alignment & (alignment - 1)) == 0); + assert(alignment >= sizeof(void*)); + if (block) { + const size_t alignment_mask = (alignment - 1); + unsigned char* basement = block + sizeof(void*); + size_t offset = ((unsigned char*)0 - basement) & alignment_mask; + void** result = (void**)(basement + offset); + assert((((unsigned char*)result - (unsigned char*)0) % alignment) == 0); + result[-1] = block; /* store original pointer */ + return result; + } + return NULL; +} + +void rhash_aligned_free(void* ptr) +{ + void** pfree = (void**)ptr; + if (ptr) + free(pfree[-1]); +} + +#else +typedef int dummy_declaration_required_by_strict_iso_c; +#endif /* HAS_POSIX_ALIGNED_ALLOC / HAS_GENERIC_ALIGNED_ALLOC */ diff --git a/Utilities/cmlibrhash/librhash/util.h b/Utilities/cmlibrhash/librhash/util.h index 57cae9b..51d5472 100644 --- a/Utilities/cmlibrhash/librhash/util.h +++ b/Utilities/cmlibrhash/librhash/util.h @@ -6,6 +6,9 @@ extern "C" { #endif +/* compile-time assert */ +#define RHASH_ASSERT(cond) (void)sizeof(char[1 - 2 * !(cond)]) + #if (defined(__GNUC__) && __GNUC__ >= 4 && (__GNUC__ > 4 || __GNUC_MINOR__ >= 1) \ && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) \ || (defined(__INTEL_COMPILER) && !defined(_WIN32)) @@ -24,6 +27,51 @@ extern "C" { # define NO_ATOMIC_BUILTINS #endif +/* alignment macros */ +#define DEFAULT_ALIGNMENT 64 +#define ALIGN_SIZE_BY(size, align) (((size) + ((align) - 1)) & ~((align) - 1)) +#define IS_SIZE_ALIGNED_BY(size, align) (((size) & ((align) - 1)) == 0) +#define IS_PTR_ALIGNED_BY(ptr, align) IS_SIZE_ALIGNED_BY((uintptr_t)(ptr), (align)) + +/* define rhash_aligned_alloc() and rhash_aligned_free() */ +#if !defined(NO_WIN32_ALIGNED_ALLOC) && defined(_WIN32) + +# define HAS_WIN32_ALIGNED_ALLOC +# include <malloc.h> +# define rhash_aligned_alloc(alignment, size) _aligned_malloc((size), (alignment)) +# define rhash_aligned_free(ptr) _aligned_free(ptr) + +#elif !defined(NO_STDC_ALIGNED_ALLOC) && (__STDC_VERSION__ >= 201112L || defined(_ISOC11_SOURCE)) \ + && !(defined(__ibmxl__) && defined(__clang__) && defined(__linux__)) \ + && !defined(__APPLE__) && !defined(__HAIKU__) && !defined(__sun) \ + && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 28) + +# define HAS_STDC_ALIGNED_ALLOC +# include <stdlib.h> +# define rhash_aligned_alloc(alignment, size) aligned_alloc((alignment), ALIGN_SIZE_BY(size, alignment)) +# define rhash_aligned_free(ptr) free(ptr) + +#else /* defined(_WIN32) ... */ + +# include "ustd.h" /* for _POSIX_VERSION macro */ + +# if !defined(NO_POSIX_ALIGNED_ALLOC) && (_POSIX_VERSION >= 200112L || _XOPEN_SOURCE >= 600) + +# define HAS_POSIX_ALIGNED_ALLOC +# include <stdlib.h> +# define rhash_aligned_alloc(alignment, size) rhash_px_aalloc((alignment), ALIGN_SIZE_BY(size, sizeof(void*))) +# define rhash_aligned_free(ptr) free(ptr) +void* rhash_px_aalloc(size_t size, size_t alignment); + +# else + +# define HAS_GENERIC_ALIGNED_ALLOC +void* rhash_aligned_alloc(size_t alignment, size_t size); +void rhash_aligned_free(void* ptr); + +# endif /* !defined(NO_POSIX_ALIGNED_ALLOC) ... */ +#endif /* defined(_WIN32) ... */ + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ @@ -364,6 +364,7 @@ CMAKE_CXX_SOURCES="\ cmFindFileCommand \ cmFindLibraryCommand \ cmFindPackageCommand \ + cmFindPackageStack \ cmFindPathCommand \ cmFindProgramCommand \ cmForEachCommand \ @@ -593,6 +594,7 @@ LIBRHASH_C_SOURCES="\ librhash/sha256.c \ librhash/sha3.c \ librhash/sha512.c \ + librhash/util.c \ " JSONCPP_CXX_SOURCES="\ @@ -1791,6 +1793,7 @@ else libs="${libs} -luv" fi +librhash_c_flags="-DNO_IMPORT_EXPORT" if test "x${bootstrap_system_librhash}" != "x"; then if test `which pkg-config`; then use_librhash_flags="`pkg-config --cflags librhash`" @@ -1945,7 +1948,7 @@ fi if test "x${bootstrap_system_librhash}" = "x"; then for a in ${LIBRHASH_C_SOURCES}; do src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmlibrhash/${a}"` - write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" "" + write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" "${librhash_c_flags}" done fi if test "x${bootstrap_system_jsoncpp}" = "x"; then |