diff options
78 files changed, 1136 insertions, 149 deletions
diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index 2859f6b..e49862f 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst @@ -13,6 +13,7 @@ Synopsis cmake_language(`CALL`_ <command> [<arg>...]) cmake_language(`EVAL`_ CODE <code>...) cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...]) + cmake_language(`SET_DEPENDENCY_PROVIDER`_ <command> SUPPORTED_METHODS <methods>...) Introduction ^^^^^^^^^^^^ @@ -225,3 +226,265 @@ also prints:: Immediate Message Deferred Message 1 Deferred Message 2 + + +.. _SET_DEPENDENCY_PROVIDER: +.. _dependency_providers: + +Dependency Providers +^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.24 + +.. code-block:: cmake + + cmake_language(SET_DEPENDENCY_PROVIDER <command> + SUPPORTED_METHODS <methods>...) + +When a call is made to :command:`find_package` or +:command:`FetchContent_MakeAvailable`, the call may be forwarded to a +dependency provider which then has the opportunity to fulfill the request. +If the request is for one of the ``<methods>`` specified when the provider +was set, CMake calls the provider's ``<command>`` with a set of +method-specific arguments. If the provider does not fulfill the request, +or if the provider doesn't support the request's method, or no provider +is set, the built-in :command:`find_package` or +:command:`FetchContent_MakeAvailable` implementation is used to fulfill +the request in the usual way. + +One or more of the following values can be specified for the ``<methods>`` +when setting the provider: + +``FIND_PACKAGE`` + The provider command accepts :command:`find_package` requests. + +``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` + The provider command accepts :command:`FetchContent_MakeAvailable` + requests. It expects each dependency to be fed to the provider command + one at a time, not the whole list in one go. + +Only one provider can be set at any point in time. If a provider is already +set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new +provider replaces the previously set one. The specified ``<command>`` must +already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called. +As a special case, providing an empty string for the ``<command>`` and no +``<methods>`` will discard any previously set provider. + +The dependency provider can only be set while processing one of the files +specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable. +Thus, dependency providers can only be set as part of the first call to +:command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)`` +outside of that context will result in an error. + +.. note:: + The choice of dependency provider should always be under the user's control. + As a convenience, a project may choose to provide a file that users can + list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but + the use of such a file should always be the user's choice. + +Provider commands +""""""""""""""""" + +Providers define a single ``<command>`` to accept requests. The name of +the command should be specific to that provider, not something overly +generic that another provider might also use. This enables users to compose +different providers in their own custom provider. The recommended form is +``xxx_provide_dependency()``, where ``xxx`` is the provider-specific part +(e.g. ``vcpkg_provide_dependency()``, ``conan_provide_dependency()``, +``ourcompany_provide_dependency()``, and so on). + +.. code-block:: cmake + + xxx_provide_dependency(<method> [<method-specific-args>...]) + +Because some methods expect certain variables to be set in the calling scope, +the provider command should typically be implemented as a macro rather than a +function. This ensures it does not introduce a new variable scope. + +The arguments CMake passes to the dependency provider depend on the type of +request. The first argument is always the method, and it will only ever +be one of the ``<methods>`` that was specified when setting the provider. + +``FIND_PACKAGE`` + The ``<method-specific-args>`` will be everything passed to the + :command:`find_package` call that requested the dependency. The first of + these ``<method-specific-args>`` will therefore always be the name of the + dependency. Dependency names are case-sensitive for this method because + :command:`find_package` treats them case-sensitively too. + + If the provider command fulfills the request, it must set the same variable + that :command:`find_package` expects to be set. For a dependency named + ``depName``, the provider must set ``depName_FOUND`` to true if it fulfilled + the request. If the provider returns without setting this variable, CMake + will assume the request was not fulfilled and will fall back to the + built-in implementation. + + If the provider needs to call the built-in :command:`find_package` + implementation as part of its processing, it can do so by including the + ``BYPASS_PROVIDER`` keyword as one of the arguments. + +``FETCHCONTENT_MAKEAVAILABE_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: + + * If ``SOURCE_DIR`` or ``BINARY_DIR`` were not part of the original + declared arguments, they will be added with their default values. + * If :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` is set to ``NEVER``, + any ``FIND_PACKAGE_ARGS`` will be omitted. + * The ``OVERRIDE_FIND_PACKAGE`` keyword is always omitted. + + The first of the ``<method-specific-args>`` will always be the name of the + dependency. Dependency names are case-insensitive for this method because + :module:`FetchContent` also treats them case-insensitively. + + If the provider fulfills the request, it should call + :command:`FetchContent_SetPopulated`, passing the name of the dependency as + the first argument. The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments to that + command should only be given if the provider makes the dependency's source + and build directories available in exactly the same way as the built-in + :command:`FetchContent_MakeAvailable` command. + + If the provider returns without calling :command:`FetchContent_SetPopulated` + for the named dependency, CMake will assume the request was not fulfilled + and will fall back to the built-in implementation. + + Note that empty arguments may be significant for this method (e.g. an empty + string following a ``GIT_SUBMODULES`` keyword). Therefore, if forwarding + these arguments on to another command, extra care must be taken to avoid such + arguments being silently dropped. + + If ``FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>`` is set, then the + dependency provider will never see requests for the ``<depName>`` dependency + for this method. When the user sets such a variable, they are explicitly + overriding where to get that dependency from and are taking on the + responsibility that their overriding version meets any requirements for that + dependency and is compatible with whatever else in the project uses it. + Depending on the value of :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` + and whether the ``OVERRIDE_FIND_PACKAGE`` option was given to + :command:`FetchContent_Declare`, having + ``FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>`` set may also prevent the + dependency provider from seeing requests for a ``find_package(depName)`` + call too. + +Provider Examples +""""""""""""""""" + +This first example only intercepts :command:`find_package` calls. The +provider command runs an external tool which copies the relevant artifacts +into a provider-specific directory, if that tool knows about the dependency. +It then relies on the built-in implementation to then find those artifacts. +:command:`FetchContent_MakeAvailable` calls would not go through the provider. + +.. code-block:: cmake + :caption: mycomp_provider.cmake + + # Always ensure we have the policy settings this provider expects + cmake_minimum_required(VERSION 3.24) + + set(MYCOMP_PROVIDER_INSTALL_DIR ${CMAKE_BINARY_DIR}/mycomp_packages + CACHE PATH "The directory this provider installs packages to" + ) + # Tell the built-in implementation to look in our area first, unless + # the find_package() call uses NO_..._PATH options to exclude it + list(APPEND CMAKE_MODULE_PATH ${MYCOMP_PROVIDER_INSTALL_DIR}/cmake) + list(APPEND CMAKE_PREFIX_PATH ${MYCOMP_PROVIDER_INSTALL_DIR}) + + macro(mycomp_provide_dependency method package_name) + execute_process( + COMMAND some_tool ${package_name} --installdir ${MYCOMP_PROVIDER_INSTALL_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + endmacro() + + cmake_language( + SET_DEPENDENCY_PROVIDER mycomp_provide_dependency + SUPPORTED_METHODS FIND_PACKAGE + ) + +The user would then typically use the above file like so:: + + cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/mycomp_provider.cmake ... + +The next example demonstrates a provider that accepts both methods, but +only handles one specific dependency. It enforces providing Google Test +using :module:`FetchContent`, but leaves all other dependencies to be +fulfilled by CMake's built-in implementation. It accepts a few different +names, which demonstrates one way of working around projects that hard-code +an unusual or undesirable way of adding this particular dependency to the +build. The example also demonstrates how to use the :command:`list` command +to preserve variables that may be overwritten by a call to +:command:`FetchContent_MakeAvailable`. + +.. code-block:: cmake + :caption: mycomp_provider.cmake + + cmake_minimum_required(VERSION 3.24) + + # Because we declare this very early, it will take precedence over any + # details the project might declare later for the same thing + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0 + ) + + # Both FIND_PACKAGE and FETCHCONTENT_MAKEAVAILABLE_SERIAL methods provide + # the package or dependency name as the first method-specific argument. + macro(mycomp_provide_dependency method dep_name) + if("${dep_name}" MATCHES "^(gtest|googletest)$") + # Save our current command arguments in case we are called recursively + list(APPEND mycomp_provider_args ${method} ${dep_name}) + + # This will forward to the built-in FetchContent implementation, + # which detects a recursive call for the same thing and avoids calling + # the provider again if dep_name is the same as the current call. + FetchContent_MakeAvailable(googletest) + + # Restore our command arguments + list(POP_BACK mycomp_provider_args dep_name method) + + # Tell the caller we fulfilled the request + if("${method}" STREQUAL "FIND_PACKAGE") + # We need to set this if we got here from a find_package() call + # since we used a different method to fulfill the request. + # This example assumes projects only use the gtest targets, + # not any of the variables the FindGTest module may define. + set(${dep_name}_FOUND TRUE) + elseif(NOT "${dep_name}" STREQUAL "googletest") + # We used the same method, but were given a different name to the + # one we populated with. Tell the caller about the name it used. + FetchContent_SetPopulated(${dep_name} + SOURCE_DIR "${googletest_SOURCE_DIR}" + BINARY_DIR "${googletest_BINARY_DIR}" + ) + endif() + endif() + endmacro() + + cmake_language( + SET_DEPENDENCY_PROVIDER mycomp_provide_dependency + SUPPORTED_METHODS + FIND_PACKAGE + FETCHCONTENT_MAKEAVAILABLE_SERIAL + ) + +The final example demonstrates how to modify arguments to a +:command:`find_package` call. It forces all such calls to have the +``QUIET`` keyword. It uses the ``BYPASS_PROVIDER`` keyword to prevent +calling the provider command recursively for the same dependency. + +.. code-block:: cmake + :caption: mycomp_provider.cmake + + cmake_minimum_required(VERSION 3.24) + + macro(mycomp_provide_dependency method) + find_package(${ARGN} BYPASS_PROVIDER QUIET) + endmacro() + + cmake_language( + SET_DEPENDENCY_PROVIDER mycomp_provide_dependency + SUPPORTED_METHODS FIND_PACKAGE + ) diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index a7d7d00..a4dad21 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst @@ -12,7 +12,8 @@ find_package .. contents:: Find a package (usually provided by something external to the project), -and load its package-specific details. +and load its package-specific details. Calls to this command can also +be intercepted by :ref:`dependency providers <dependency_providers>`. Search Modes ^^^^^^^^^^^^ diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 7935ca3..d88322c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -263,6 +263,7 @@ Properties on Targets /prop_tgt/INTERFACE_COMPILE_FEATURES /prop_tgt/INTERFACE_COMPILE_OPTIONS /prop_tgt/INTERFACE_HEADER_SETS + /prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_LINK_DEPENDS /prop_tgt/INTERFACE_LINK_DIRECTORIES diff --git a/Help/prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY.rst b/Help/prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY.rst new file mode 100644 index 0000000..b0d63f3 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY.rst @@ -0,0 +1,13 @@ +INTERFACE_HEADER_SETS_TO_VERIFY +------------------------------- + +.. versionadded:: 3.24 + +Used to specify which ``PUBLIC`` and ``INTERFACE`` header sets of a target +should be verified. + +This property contains a semicolon-separated list of header sets which +should be verified if :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to +``TRUE``. If the list is empty, all ``PUBLIC`` and ``INTERFACE`` header sets +are verified. (If the project does not want to verify any header sets on the +target, simply set :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` to ``FALSE``.) diff --git a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst index d8045c6..30c02f5 100644 --- a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst +++ b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst @@ -23,3 +23,6 @@ is used to determine the language with which to compile the header file. Otherwise, if the target has any C++ sources, the header is compiled as C++. Otherwise, if the target has any C sources, the header is compiled as C. Otherwise, the header file is not compiled. + +If the project wishes to control which header sets are verified by this +property, you can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`. diff --git a/Help/release/dev/LLVMFlang-compiler.rst b/Help/release/dev/LLVMFlang-compiler.rst new file mode 100644 index 0000000..1d29449 --- /dev/null +++ b/Help/release/dev/LLVMFlang-compiler.rst @@ -0,0 +1,6 @@ +LLVMFlang-compiler +------------------ + +* LLVM's `flang`_ Fortran compiler is now supported, with compiler id ``LLVMFlang``. + +.. _`flang`: https://github.com/llvm/llvm-project/tree/main/flang diff --git a/Help/release/dev/dependency-providers.rst b/Help/release/dev/dependency-providers.rst new file mode 100644 index 0000000..8b2cf06 --- /dev/null +++ b/Help/release/dev/dependency-providers.rst @@ -0,0 +1,9 @@ +dependency-providers +-------------------- + +* The :command:`cmake_language` command gained a new + ``SET_DEPENDENCY_PROVIDER`` sub-command. When a dependency provider is set, + calls to :command:`find_package` and :command:`FetchContent_MakeAvailable` + can be redirected through a custom command, which can choose to fulfill the + request directly, modify how the request is processed, or leave it to be + fulfilled by the built-in implementation. See :ref:`dependency_providers`. diff --git a/Help/release/dev/mingw-compiler-PATH.rst b/Help/release/dev/mingw-compiler-PATH.rst new file mode 100644 index 0000000..7452da0 --- /dev/null +++ b/Help/release/dev/mingw-compiler-PATH.rst @@ -0,0 +1,7 @@ +mingw-compiler-PATH +------------------- + +* The :generator:`MSYS Makefiles` and :generator:`MinGW Makefiles` + generators, when a compiler is not explicitly specified, now select + the first compiler (of any name) found in directories listed by the + ``PATH`` environment variable. diff --git a/Help/release/dev/verify-interface-header-sets.rst b/Help/release/dev/verify-interface-header-sets.rst index fcccb62..9e6856d 100644 --- a/Help/release/dev/verify-interface-header-sets.rst +++ b/Help/release/dev/verify-interface-header-sets.rst @@ -7,3 +7,6 @@ verify-interface-header-sets * A new :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` variable was added, which is used to initialize the :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` target property. +* A new :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY` target property was added, + which can be used to specify which header sets should be verified by + :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`. diff --git a/Help/variable/CMAKE_COMPILER_IS_GNUCC.rst b/Help/variable/CMAKE_COMPILER_IS_GNUCC.rst index 91cf848..4b799c0 100644 --- a/Help/variable/CMAKE_COMPILER_IS_GNUCC.rst +++ b/Help/variable/CMAKE_COMPILER_IS_GNUCC.rst @@ -1,7 +1,7 @@ CMAKE_COMPILER_IS_GNUCC ----------------------- -.. versionadded:: 3.7 - True if the ``C`` compiler is GNU. -Use :variable:`CMAKE_C_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. + +This variable is deprecated. Use +:variable:`CMAKE_C_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. diff --git a/Help/variable/CMAKE_COMPILER_IS_GNUCXX.rst b/Help/variable/CMAKE_COMPILER_IS_GNUCXX.rst index e67718a..29069d2 100644 --- a/Help/variable/CMAKE_COMPILER_IS_GNUCXX.rst +++ b/Help/variable/CMAKE_COMPILER_IS_GNUCXX.rst @@ -1,7 +1,7 @@ CMAKE_COMPILER_IS_GNUCXX ------------------------ -.. versionadded:: 3.7 - True if the C++ (``CXX``) compiler is GNU. -Use :variable:`CMAKE_CXX_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. + +This variable is deprecated. Use +:variable:`CMAKE_CXX_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. diff --git a/Help/variable/CMAKE_COMPILER_IS_GNUG77.rst b/Help/variable/CMAKE_COMPILER_IS_GNUG77.rst index f69c01a..05303dc 100644 --- a/Help/variable/CMAKE_COMPILER_IS_GNUG77.rst +++ b/Help/variable/CMAKE_COMPILER_IS_GNUG77.rst @@ -1,7 +1,7 @@ CMAKE_COMPILER_IS_GNUG77 ------------------------ -.. versionadded:: 3.7 - True if the ``Fortran`` compiler is GNU. -Use :variable:`CMAKE_Fortran_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. + +This variable is deprecated. Use +:variable:`CMAKE_Fortran_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` instead. diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst index cd7d5cd..6a0a1d9 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst @@ -18,7 +18,8 @@ include: Clang = LLVM Clang (clang.llvm.org) Cray = Cray Compiler (cray.com) Embarcadero, Borland = Embarcadero (embarcadero.com) - Flang = Flang LLVM Fortran Compiler + Flang = Classic Flang Fortran Compiler (https://github.com/flang-compiler/flang) + LLVMFlang = LLVM Flang Fortran Compiler (https://github.com/llvm/llvm-project/tree/main/flang) Fujitsu = Fujitsu HPC compiler (Trad mode) FujitsuClang = Fujitsu HPC compiler (Clang mode) G95 = G95 Fortran (g95.org) diff --git a/Modules/CMakeDetermineCompiler.cmake b/Modules/CMakeDetermineCompiler.cmake index ec2a865..3156ca9 100644 --- a/Modules/CMakeDetermineCompiler.cmake +++ b/Modules/CMakeDetermineCompiler.cmake @@ -53,10 +53,9 @@ macro(_cmake_find_compiler lang) NO_DEFAULT_PATH DOC "${lang} compiler") endif() - if(CMAKE_HOST_WIN32 AND CMAKE_GENERATOR MATCHES "Ninja") - # On Windows command-line builds, the Makefile generators each imply - # a preferred compiler tool. The Ninja generator does not imply a - # compiler tool, so use the compiler that occurs first in PATH. + if(CMAKE_HOST_WIN32 AND CMAKE_GENERATOR MATCHES "Ninja|MSYS Makefiles|MinGW Makefiles") + # On Windows command-line builds, some generators imply a preferred compiler tool. + # These generators do not, so use the compiler that occurs first in PATH. find_program(CMAKE_${lang}_COMPILER NAMES ${CMAKE_${lang}_COMPILER_LIST} NAMES_PER_DIR diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake index 1c4b6ea..650c87a 100644 --- a/Modules/CMakeDetermineFortranCompiler.cmake +++ b/Modules/CMakeDetermineFortranCompiler.cmake @@ -91,6 +91,7 @@ else() set(_Fortran_COMPILER_NAMES_Absoft af95 af90 af77) set(_Fortran_COMPILER_NAMES_PGI pgf95 pgfortran pgf90 pgf77) set(_Fortran_COMPILER_NAMES_Flang flang) + set(_Fortran_COMPILER_NAMES_LLVMFlang flang) set(_Fortran_COMPILER_NAMES_PathScale pathf2003 pathf95 pathf90) set(_Fortran_COMPILER_NAMES_XL xlf) set(_Fortran_COMPILER_NAMES_VisualAge xlf95 xlf90 xlf) diff --git a/Modules/CMakeFortranCompilerId.F.in b/Modules/CMakeFortranCompilerId.F.in index 969c841..f5c2ab5 100644 --- a/Modules/CMakeFortranCompilerId.F.in +++ b/Modules/CMakeFortranCompilerId.F.in @@ -154,6 +154,13 @@ # if defined(__FLANG_PATCHLEVEL__) # define COMPILER_VERSION_PATCH DEC(__FLANG_PATCHLEVEL__) # endif +#elif defined(__flang__) + PRINT *, 'INFO:compiler[LLVMFlang]' +# define COMPILER_VERSION_MAJOR DEC(__flang_major__) +# define COMPILER_VERSION_MINOR DEC(__flang_minor__) +# if defined(__flang_patchlevel__) +# define COMPILER_VERSION_PATCH DEC(__flang_patchlevel__) +# endif #elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__) PRINT *, 'INFO:compiler[VisualAge]' #elif defined(__hpux) || defined(__hpux__) diff --git a/Modules/CPackComponent.cmake b/Modules/CPackComponent.cmake index 1f8c38c..8ca9f28 100644 --- a/Modules/CPackComponent.cmake +++ b/Modules/CPackComponent.cmake @@ -327,32 +327,34 @@ OS X. if(NOT CPackComponent_CMake_INCLUDED) set(CPackComponent_CMake_INCLUDED 1) -# Macro that appends a SET command for the given variable name (var) -# to the macro named strvar, but only if the variable named "var" +# Function that appends a SET command for the given variable name (var) +# to the string named strvar, but only if the variable named "var" # has been defined. The string will eventually be appended to a CPack # configuration file. -macro(cpack_append_variable_set_command var strvar) +function(cpack_append_variable_set_command var strvar) if (DEFINED ${var}) string(APPEND ${strvar} "set(${var}") foreach(APPENDVAL ${${var}}) string(APPEND ${strvar} " ${APPENDVAL}") endforeach() string(APPEND ${strvar} ")\n") + set(${strvar} "${${strvar}}" PARENT_SCOPE) endif () -endmacro() +endfunction() -# Macro that appends a SET command for the given variable name (var) -# to the macro named strvar, but only if the variable named "var" +# Function that appends a SET command for the given variable name (var) +# to the string named strvar, but only if the variable named "var" # has been defined and is a string. The string will eventually be # appended to a CPack configuration file. -macro(cpack_append_string_variable_set_command var strvar) +function(cpack_append_string_variable_set_command var strvar) if (DEFINED ${var}) list(LENGTH ${var} CPACK_APP_VALUE_LEN) if(${CPACK_APP_VALUE_LEN} EQUAL 1) string(APPEND ${strvar} "set(${var} \"${${var}}\")\n") endif() + set(${strvar} "${${strvar}}" PARENT_SCOPE) endif () -endmacro() +endfunction() # Macro that appends a SET command for the given list variable name (var) # to the macro named strvar, but only if the variable named "var" diff --git a/Modules/Compiler/LLVMFlang-Fortran.cmake b/Modules/Compiler/LLVMFlang-Fortran.cmake new file mode 100644 index 0000000..7e9ba5e --- /dev/null +++ b/Modules/Compiler/LLVMFlang-Fortran.cmake @@ -0,0 +1,13 @@ +set(CMAKE_Fortran_SUBMODULE_SEP "-") +set(CMAKE_Fortran_SUBMODULE_EXT ".mod") + +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -cpp <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>") + +set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") +set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") + +set(CMAKE_Fortran_MODDIR_FLAG "-module-dir") + +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index a342aa7..5ca296e 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -193,6 +193,11 @@ Commands ``OVERRIDE_FIND_PACKAGE`` cannot be used when ``FIND_PACKAGE_ARGS`` is given. + :ref:`dependency_providers` discusses another way that + :command:`FetchContent_MakeAvailable` calls can be redirected. + ``FIND_PACKAGE_ARGS`` is intended for project control, whereas + dependency providers allow users to override project behavior. + ``OVERRIDE_FIND_PACKAGE`` When a ``FetchContent_Declare(<name> ...)`` call includes this option, subsequent calls to ``find_package(<name> ...)`` will ensure that @@ -204,6 +209,13 @@ Commands satisfy the package requirements of the latter. ``FIND_PACKAGE_ARGS`` cannot be used when ``OVERRIDE_FIND_PACKAGE`` is given. + If a :ref:`dependency provider <dependency_providers>` has been set + and the project calls :command:`find_package` for the ``<name>`` + dependency, ``OVERRIDE_FIND_PACKAGE`` will not prevent the provider + from seeing that call. Dependency providers always have the opportunity + to intercept any direct call to :command:`find_package`, except if that + call contains the ``BYPASS_PROVIDER`` option. + .. command:: FetchContent_MakeAvailable .. versionadded:: 3.14 @@ -217,17 +229,35 @@ Commands :command:`FetchContent_Declare` for each dependency, and the first such call will control how that dependency will be made available, as described below. - .. versionadded:: 3.24 - If permitted, :command:`find_package(<name> [<args>...]) <find_package>` - will be called, where ``<args>...`` may be provided by the - ``FIND_PACKAGE_ARGS`` option in :command:`FetchContent_Declare`. - The value of the :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` variable - at the time :command:`FetchContent_Declare` was called determines whether - ``FetchContent_MakeAvailable()`` can call :command:`find_package`. + If ``<lowercaseName>_SOURCE_DIR`` is not set: + + * .. versionadded:: 3.24 + + If a :ref:`dependency provider <dependency_providers>` is set, call the + provider's command with ``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` as the + first argument, followed by the arguments of the first call to + :command:`FetchContent_Declare` for ``<name>``. If ``SOURCE_DIR`` or + ``BINARY_DIR`` were not part of the original declared arguments, they + will be added with their default values. + If :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` was set to ``NEVER`` + when the details were declared, any ``FIND_PACKAGE_ARGS`` will be + omitted. The ``OVERRIDE_FIND_PACKAGE`` keyword is also always omitted. + If the provider fulfilled the request, ``FetchContent_MakeAvailable()`` + will consider that dependency handled, skip the remaining steps below + and move on to the next dependency in the list. + + * .. versionadded:: 3.24 - If :command:`find_package` was unsuccessful or was not allowed to be called, - ``FetchContent_MakeAvailable()`` then uses the following logic to make the - dependency available: + If permitted, :command:`find_package(<name> [<args>...]) <find_package>` + will be called, where ``<args>...`` may be provided by the + ``FIND_PACKAGE_ARGS`` option in :command:`FetchContent_Declare`. + The value of the :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` variable + at the time :command:`FetchContent_Declare` was called determines whether + ``FetchContent_MakeAvailable()`` can call :command:`find_package`. + + If the dependency was not satisfied by a provider or a + :command:`find_package` call, ``FetchContent_MakeAvailable()`` then uses + the following logic to make the dependency available: * If the dependency has already been populated earlier in this run, set the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR`` and @@ -468,7 +498,7 @@ Commands When using saved content details, a call to :command:`FetchContent_MakeAvailable` or :command:`FetchContent_Populate` records information in global properties which can be queried at any time. - This information includes the source and binary directories associated with + This information may include the source and binary directories associated with the content and also whether or not the content population has been processed during the current configure run. @@ -488,6 +518,8 @@ Commands set the same variables as a call to :command:`FetchContent_MakeAvailable(name) <FetchContent_MakeAvailable>` or :command:`FetchContent_Populate(name) <FetchContent_Populate>`. + Note that the ``SOURCE_DIR`` and ``BINARY_DIR`` values can be empty if the + call is fulfilled by a :ref:`dependency provider <dependency_providers>`. This command is rarely needed when using :command:`FetchContent_MakeAvailable`. It is more commonly used as part of @@ -511,6 +543,33 @@ Commands add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR}) endif() +.. command:: FetchContent_SetPopulated + + .. versionadded:: 3.24 + + .. note:: + This command should only be called by + :ref:`dependency providers <dependency_providers>`. Calling it in any + other context is unsupported and future CMake versions may halt with a + fatal error in such cases. + + .. code-block:: cmake + + FetchContent_SetPopulated( + <name> + [SOURCE_DIR <srcDir>] + [BINARY_DIR <binDir>] + ) + + If a provider command fulfills a ``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` + request, it must call this function before returning. The ``SOURCE_DIR`` + and ``BINARY_DIR`` arguments can be used to specify the values that + :command:`FetchContent_GetProperties` should return for its corresponding + arguments. Only provide ``SOURCE_DIR`` and ``BINARY_DIR`` if they have + the same meaning as if they had been populated by the built-in + :command:`FetchContent_MakeAvailable` implementation. + + Variables ^^^^^^^^^ @@ -588,7 +647,7 @@ A number of cache variables can influence the behavior where details from a behavior if ``FETCHCONTENT_TRY_FIND_PACKAGE_MODE`` is not set. ``ALWAYS`` - :command:`find_package` will be called by + :command:`find_package` can be called by :command:`FetchContent_MakeAvailable` regardless of whether the :command:`FetchContent_Declare` call included a ``FIND_PACKAGE_ARGS`` keyword or not. If no ``FIND_PACKAGE_ARGS`` keyword was given, the @@ -1099,14 +1158,26 @@ function(FetchContent_Declare contentName) endif() set(options "") - set(oneValueArgs SVN_REPOSITORY) + set(oneValueArgs + BINARY_DIR + SOURCE_DIR + SVN_REPOSITORY + ) set(multiValueArgs "") cmake_parse_arguments(PARSE_ARGV 1 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") - unset(srcDirSuffix) - unset(svnRepoArgs) + string(TOLOWER ${contentName} contentNameLower) + + if(NOT ARG_BINARY_DIR) + set(ARG_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") + endif() + + if(NOT ARG_SOURCE_DIR) + set(ARG_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") + endif() + if(ARG_SVN_REPOSITORY) # Add a hash of the svn repository URL to the source dir. This works # around the problem where if the URL changes, the download would @@ -1116,25 +1187,21 @@ function(FetchContent_Declare contentName) # problem on windows due to path length limits). string(SHA1 urlSHA ${ARG_SVN_REPOSITORY}) string(SUBSTRING ${urlSHA} 0 7 urlSHA) - set(srcDirSuffix "-${urlSHA}") - set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY}) + string(APPEND ARG_SOURCE_DIR "-${urlSHA}") + list(PREPEND ARG_UNPARSED_ARGUMENTS SVN_REPOSITORY "${ARG_SVN_REPOSITORY}") endif() - string(TOLOWER ${contentName} contentNameLower) + list(PREPEND ARG_UNPARSED_ARGUMENTS + SOURCE_DIR "${ARG_SOURCE_DIR}" + BINARY_DIR "${ARG_BINARY_DIR}" + ) set(__argsQuoted) foreach(__item IN LISTS ARG_UNPARSED_ARGUMENTS) string(APPEND __argsQuoted " [==[${__item}]==]") endforeach() - cmake_language(EVAL CODE " - __FetchContent_declareDetails( - ${contentNameLower} - SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}\" - BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build\" - \${svnRepoArgs} - # List these last so they can override things we set above - ${__argsQuoted} - )" + cmake_language(EVAL CODE + "__FetchContent_declareDetails(${contentNameLower} ${__argsQuoted})" ) endfunction() @@ -1145,11 +1212,11 @@ endfunction() # The setter also records the source and binary dirs used. #======================================================================= -# Internal use, projects must not call this directly. It is intended -# for use by things like the FetchContent_Populate() function to -# record when FetchContent_Populate() is called for a particular -# content name. -function(__FetchContent_setPopulated contentName) +# Semi-internal use. Projects must not call this directly. Dependency +# providers must call it if they satisfy a request made with the +# FETCHCONTENT_MAKEAVAILABLE_SERIAL method (that is the only permitted +# place to call it outside of the FetchContent module). +function(FetchContent_SetPopulated contentName) cmake_parse_arguments(PARSE_ARGV 1 arg "" @@ -1165,10 +1232,18 @@ function(__FetchContent_setPopulated contentName) set(propertyName "${prefix}_sourceDir") define_property(GLOBAL PROPERTY ${propertyName}) + if("${arg_SOURCE_DIR}" STREQUAL "") + # Don't discard a previously provided SOURCE_DIR + get_property(arg_SOURCE_DIR GLOBAL PROPERTY ${propertyName}) + endif() set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") set(propertyName "${prefix}_binaryDir") define_property(GLOBAL PROPERTY ${propertyName}) + if("${arg_BINARY_DIR}" STREQUAL "") + # Don't discard a previously provided BINARY_DIR + get_property(arg_BINARY_DIR GLOBAL PROPERTY ${propertyName}) + endif() set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") set(propertyName "${prefix}_populated") @@ -1480,7 +1555,8 @@ function(FetchContent_Populate contentName) if(${contentNameLower}_POPULATED) if("${${contentNameLower}_SOURCE_DIR}" STREQUAL "") message(FATAL_ERROR - "Content ${contentName} already populated by find_package()" + "Content ${contentName} already populated by find_package() or a " + "dependency provider" ) else() message(FATAL_ERROR @@ -1584,7 +1660,7 @@ function(FetchContent_Populate contentName) ) endif() - __FetchContent_setPopulated( + FetchContent_SetPopulated( ${contentName} SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" BINARY_DIR "${${contentNameLower}_BINARY_DIR}" @@ -1654,22 +1730,98 @@ endfunction() # calls will be available to the caller. macro(FetchContent_MakeAvailable) + get_property(__cmake_providerCommand GLOBAL PROPERTY + __FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER + ) foreach(__cmake_contentName IN ITEMS ${ARGV}) string(TOLOWER ${__cmake_contentName} __cmake_contentNameLower) # If user specified FETCHCONTENT_SOURCE_DIR_... for this dependency, that - # overrides everything else and we shouldn't try to use find_package(). + # overrides everything else and we shouldn't try to use find_package() or + # a dependency provider. string(TOUPPER ${__cmake_contentName} __cmake_contentNameUpper) if("${FETCHCONTENT_SOURCE_DIR_${__cmake_contentNameUpper}}" STREQUAL "") + # Dependency provider gets first opportunity, but prevent infinite + # recursion if we are called again for the same thing + if(NOT "${__cmake_providerCommand}" STREQUAL "" AND + NOT DEFINED __cmake_fcProvider_${__cmake_contentNameLower}) + message(VERBOSE + "Trying FETCHCONTENT_MAKEAVAILABLE_SERIAL dependency provider for " + "${__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. + __FetchContent_getSavedDetails(${__cmake_contentName} __cmake_contentDetails) + set(__cmake_providerArgs + "FETCHCONTENT_MAKEAVAILABLE_SERIAL" + "${__cmake_contentName}" + ) + # Empty arguments must be preserved because of things like + # GIT_SUBMODULES (see CMP0097) + foreach(__cmake_item IN LISTS __cmake_contentDetails) + string(APPEND __cmake_providerArgs " [==[${__cmake_item}]==]") + endforeach() + + # 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 + DEFINED + ) + if(__cmake_addfpargs) + get_property(__cmake_fpargs GLOBAL PROPERTY + _FetchContent_${contentNameLower}_find_package_args + ) + string(APPEND __cmake_providerArgs " FIND_PACKAGE_ARGS") + foreach(__cmake_item IN LISTS __cmake_fpargs) + string(APPEND __cmake_providerArgs " [==[${__cmake_item}]==]") + endforeach() + endif() + + # Calling the provider could lead to FetchContent_MakeAvailable() being + # called for a nested dependency. That nested call may occur in the + # current variable scope. We have to save and restore the variables we + # need preserved. + list(APPEND __cmake_fcCurrentVarsStack + ${__cmake_contentName} + ${__cmake_contentNameLower} + ) + + set(__cmake_fcProvider_${__cmake_contentNameLower} YES) + cmake_language(EVAL CODE "${__cmake_providerCommand}(${__cmake_providerArgs})") + unset(__cmake_fcProvider_${__cmake_contentNameLower}) + + list(POP_BACK __cmake_fcCurrentVarsStack + __cmake_contentNameLower + __cmake_contentName + ) + + unset(__cmake_providerArgs) + unset(__cmake_addfpargs) + unset(__cmake_fpargs) + unset(__cmake_item) + unset(__cmake_contentDetails) + + FetchContent_GetProperties(${__cmake_contentName}) + if(${__cmake_contentNameLower}_POPULATED) + continue() + endif() + endif() + # Check if we've been asked to try find_package() first, even if we # have already populated this dependency. If we previously tried to # use find_package() for this and it succeeded, those things might # no longer be in scope, so we have to do it again. - set(__cmake_fpArgsPropName "_FetchContent_${__cmake_contentNameLower}_find_package_args") - get_property(__cmake_haveFpArgs GLOBAL PROPERTY ${__cmake_fpArgsPropName} DEFINED) + get_property(__cmake_haveFpArgs GLOBAL PROPERTY + _FetchContent_${__cmake_contentNameLower}_find_package_args DEFINED + ) if(__cmake_haveFpArgs) + unset(__cmake_haveFpArgs) message(VERBOSE "Trying find_package(${__cmake_contentName} ...) before FetchContent") - get_property(__cmake_fpArgs GLOBAL PROPERTY ${__cmake_fpArgsPropName}) + get_property(__cmake_fpArgs GLOBAL PROPERTY + _FetchContent_${__cmake_contentNameLower}_find_package_args + ) # This call could lead to FetchContent_MakeAvailable() being called for # a nested dependency and it may occur in the current variable scope. @@ -1683,15 +1835,16 @@ macro(FetchContent_MakeAvailable) __cmake_contentNameLower __cmake_contentName ) + unset(__cmake_fpArgs) if(${__cmake_contentName}_FOUND) - set(${__cmake_contentNameLower}_SOURCE_DIR "") - set(${__cmake_contentNameLower}_BINARY_DIR "") - set(${__cmake_contentNameLower}_POPULATED TRUE) - __FetchContent_setPopulated(${__cmake_contentName}) + FetchContent_SetPopulated(${__cmake_contentName}) + FetchContent_GetProperties(${__cmake_contentName}) continue() endif() endif() + else() + unset(__cmake_haveFpArgs) endif() FetchContent_GetProperties(${__cmake_contentName}) @@ -1731,5 +1884,7 @@ macro(FetchContent_MakeAvailable) # clear local variables to prevent leaking into the caller's scope unset(__cmake_contentName) unset(__cmake_contentNameLower) + unset(__cmake_contentNameUpper) + unset(__cmake_providerCommand) endmacro() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 2deaaaa..95b07cb 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -199,6 +199,7 @@ set(SRCS cmCustomCommandTypes.h cmDefinitions.cxx cmDefinitions.h + cmDependencyProvider.h cmDepends.cxx cmDepends.h cmDependsC.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 3b258cd..c0ef6cc 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 23) -set(CMake_VERSION_PATCH 20220524) +set(CMake_VERSION_PATCH 20220527) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index 27d8cb8..7d05e88 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -13,11 +13,14 @@ #include <cm/string_view> #include <cmext/string_view> +#include "cmArgumentParser.h" +#include "cmDependencyProvider.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmRange.h" +#include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -215,6 +218,91 @@ bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args, return makefile.ReadListFileAsString( code, cmStrCat(context.FilePath, ":", context.Line, ":EVAL")); } + +bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( + std::vector<std::string> const& args, cmExecutionStatus& status) +{ + cmState* state = status.GetMakefile().GetState(); + if (!state->InTopLevelIncludes()) { + return FatalError( + status, + "Dependency providers can only be set as part of the first call to " + "project(). More specifically, cmake_language(SET_DEPENDENCY_PROVIDER) " + "can only be called while the first project() command processes files " + "listed in CMAKE_PROJECT_TOP_LEVEL_INCLUDES."); + } + + struct SetProviderArgs + { + std::string Command; + std::vector<std::string> Methods; + }; + + auto const ArgsParser = + cmArgumentParser<SetProviderArgs>() + .Bind("SET_DEPENDENCY_PROVIDER"_s, &SetProviderArgs::Command) + .Bind("SUPPORTED_METHODS"_s, &SetProviderArgs::Methods); + + std::vector<std::string> unparsed; + auto parsedArgs = ArgsParser.Parse(args, &unparsed); + + if (!unparsed.empty()) { + return FatalError( + status, cmStrCat("Unrecognized keyword: \"", unparsed.front(), "\"")); + } + + // We store the command that FetchContent_MakeAvailable() can call in a + // global (but considered internal) property. If the provider doesn't + // support this method, we set this property to an empty string instead. + // This simplifies the logic in FetchContent_MakeAvailable() and doesn't + // require us to define a new internal command or sub-command. + std::string fcmasProperty = "__FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER"; + + if (parsedArgs.Command.empty()) { + if (!parsedArgs.Methods.empty()) { + return FatalError(status, + "Must specify a non-empty command name when provider " + "methods are given"); + } + state->ClearDependencyProvider(); + state->SetGlobalProperty(fcmasProperty, ""); + return true; + } + + cmState::Command command = state->GetCommand(parsedArgs.Command); + if (!command) { + return FatalError(status, + cmStrCat("Command \"", parsedArgs.Command, + "\" is not a defined command")); + } + + if (parsedArgs.Methods.empty()) { + return FatalError(status, "Must specify at least one provider method"); + } + + bool supportsFetchContentMakeAvailableSerial = false; + std::vector<cmDependencyProvider::Method> methods; + for (auto const& method : parsedArgs.Methods) { + if (method == "FIND_PACKAGE") { + methods.emplace_back(cmDependencyProvider::Method::FindPackage); + } else if (method == "FETCHCONTENT_MAKEAVAILABLE_SERIAL") { + supportsFetchContentMakeAvailableSerial = true; + methods.emplace_back( + cmDependencyProvider::Method::FetchContentMakeAvailableSerial); + } else { + return FatalError( + status, + cmStrCat("Unknown dependency provider method \"", method, "\"")); + } + } + + state->SetDependencyProvider({ parsedArgs.Command, methods }); + state->SetGlobalProperty( + fcmasProperty, + supportsFetchContentMakeAvailableSerial ? parsedArgs.Command.c_str() : ""); + + return true; +} } bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, @@ -246,6 +334,11 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, return FatalError(status, "called with incorrect number of arguments"); } + if (expArgs[expArg] == "SET_DEPENDENCY_PROVIDER"_s) { + finishArgs(); + return cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(expArgs, status); + } + cm::optional<Defer> maybeDefer; if (expArgs[expArg] == "DEFER"_s) { ++expArg; // Consume "DEFER". diff --git a/Source/cmDependencyProvider.h b/Source/cmDependencyProvider.h new file mode 100644 index 0000000..a6670b4 --- /dev/null +++ b/Source/cmDependencyProvider.h @@ -0,0 +1,38 @@ +/* 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 <algorithm> +#include <string> +#include <utility> +#include <vector> + +class cmDependencyProvider +{ +public: + enum class Method + { + FindPackage, + FetchContentMakeAvailableSerial, + }; + + cmDependencyProvider(std::string command, std::vector<Method> methods) + : Command(std::move(command)) + , Methods(std::move(methods)) + { + } + + std::string const& GetCommand() const { return this->Command; } + std::vector<Method> const& GetMethods() const { return this->Methods; } + bool SupportsMethod(Method method) const + { + return std::find(this->Methods.begin(), this->Methods.end(), method) != + this->Methods.end(); + } + +private: + std::string Command; + std::vector<Method> Methods; +}; diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 6586c69..e41d5e4 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -23,6 +23,7 @@ #include "cmsys/String.h" #include "cmAlgorithms.h" +#include "cmDependencyProvider.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -238,6 +239,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) const char* components_sep = ""; std::set<std::string> requiredComponents; std::set<std::string> optionalComponents; + std::vector<std::pair<std::string, const char*>> componentVarDefs; + bool bypassProvider = false; // Always search directly in a generated path. this->SearchPathSuffixes.emplace_back(); @@ -268,6 +271,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (args[i] == "QUIET") { this->Quiet = true; doing = DoingNone; + } else if (args[i] == "BYPASS_PROVIDER") { + bypassProvider = true; + doing = DoingNone; } else if (args[i] == "EXACT") { this->VersionExact = true; doing = DoingNone; @@ -356,7 +362,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i]; - this->AddFindDefinition(req_var, isRequired); + componentVarDefs.emplace_back(req_var, isRequired); // Append to the list of required components. components += components_sep; @@ -408,7 +414,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } - // Maybe choose one mode exclusively. + // Check and eliminate search modes not allowed by the args provided this->UseFindModules = configArgs.empty(); this->UseConfigFiles = moduleArgs.empty(); if (!this->UseFindModules && !this->UseConfigFiles) { @@ -543,6 +549,48 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return true; } + // Now choose what method(s) we will use to satisfy the request. Note that + // we still want all the above checking of arguments, etc. regardless of the + // method used. This will ensure ill-formed arguments are caught earlier, + // before things like dependency providers need to deal with them. + + // A dependency provider (if set) gets first look before other methods. + // We do this before modifying the package root path stack because a + // provider might use methods that ignore that. + cmState* state = this->Makefile->GetState(); + cmState::Command providerCommand = state->GetDependencyProviderCommand( + cmDependencyProvider::Method::FindPackage); + if (bypassProvider) { + if (this->DebugMode && providerCommand) { + this->DebugMessage( + "BYPASS_PROVIDER given, skipping dependency provider"); + } + } else if (providerCommand) { + if (this->DebugMode) { + this->DebugMessage(cmStrCat("Trying dependency provider command: ", + state->GetDependencyProvider()->GetCommand(), + "()")); + } + std::vector<cmListFileArgument> listFileArgs(args.size() + 1); + listFileArgs[0] = + cmListFileArgument("FIND_PACKAGE", cmListFileArgument::Unquoted, 0); + std::transform(args.begin(), args.end(), listFileArgs.begin() + 1, + [](const std::string& arg) { + return cmListFileArgument(arg, + cmListFileArgument::Bracket, 0); + }); + if (!providerCommand(listFileArgs, this->Status)) { + return false; + } + if (this->Makefile->IsOn(cmStrCat(this->Name, "_FOUND"))) { + if (this->DebugMode) { + this->DebugMessage("Package was found by the dependency provider"); + } + this->AppendSuccessInformation(); + return true; + } + } + { // Allocate a PACKAGE_ROOT_PATH for the current find_package call. this->Makefile->FindPackageRootPathStack.emplace_back(); @@ -573,7 +621,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - this->SetModuleVariables(components); + this->SetModuleVariables(components, componentVarDefs); // See if we have been told to delegate to FetchContent or some other // redirected config package first. We have to check all names that @@ -697,6 +745,12 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->AppendSuccessInformation(); + // Restore original state of "_FIND_" variables set in SetModuleVariables() + this->RestoreFindDefinitions(); + + // Pop the package stack + this->Makefile->FindPackageRootPathStack.pop_back(); + if (!this->DebugBuffer.empty()) { this->DebugMessage(this->DebugBuffer); } @@ -778,13 +832,18 @@ void cmFindPackageCommand::SetVersionVariables( addDefinition(prefix + "_COUNT", buf); } -void cmFindPackageCommand::SetModuleVariables(const std::string& components) +void cmFindPackageCommand::SetModuleVariables( + const std::string& components, + const std::vector<std::pair<std::string, const char*>>& componentVarDefs) { this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name); - // Store the list of components. + // Store the list of components and associated variable definitions std::string components_var = this->Name + "_FIND_COMPONENTS"; this->AddFindDefinition(components_var, components); + for (const auto& varDef : componentVarDefs) { + this->AddFindDefinition(varDef.first, varDef.second); + } if (this->Quiet) { // Tell the module that is about to be read that it should find @@ -1388,12 +1447,6 @@ void cmFindPackageCommand::AppendSuccessInformation() this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName, "REQUIRED"); } - - // Restore original state of "_FIND_" variables we set. - this->RestoreFindDefinitions(); - - // Pop the package stack - this->Makefile->FindPackageRootPathStack.pop_back(); } inline std::size_t collectPathsForDebug(std::string& buffer, diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 902fa32..80fd8f8 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -9,6 +9,7 @@ #include <map> #include <set> #include <string> +#include <utility> #include <vector> #include <cm/string_view> @@ -97,7 +98,9 @@ private: const std::string& prefix, const std::string& version, unsigned int count, unsigned int major, unsigned int minor, unsigned int patch, unsigned int tweak); - void SetModuleVariables(const std::string& components); + void SetModuleVariables( + const std::string& components, + const std::vector<std::pair<std::string, const char*>>& componentVarDefs); bool FindModule(bool& found); void AddFindDefinition(const std::string& var, cm::string_view value); void RestoreFindDefinitions(); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 8ed7f10..ff148a2 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -8525,19 +8525,37 @@ bool cmGeneratorTarget::AddHeaderSetVerification() return true; } + auto verifyValue = this->GetProperty("INTERFACE_HEADER_SETS_TO_VERIFY"); + const bool all = verifyValue.IsEmpty(); + std::set<std::string> verifySet; + if (!all) { + auto verifyList = cmExpandedList(verifyValue); + verifySet.insert(verifyList.begin(), verifyList.end()); + } + cmTarget* verifyTarget = nullptr; auto interfaceFileSetEntries = this->Target->GetInterfaceHeaderSetsEntries(); std::set<cmFileSet*> fileSets; - auto const addFileSets = [&fileSets, this](const cmBTStringRange& entries) { - for (auto const& entry : entries) { - for (auto const& name : cmExpandedList(entry.Value)) { + for (auto const& entry : interfaceFileSetEntries) { + for (auto const& name : cmExpandedList(entry.Value)) { + if (all || verifySet.count(name)) { fileSets.insert(this->Target->GetFileSet(name)); + verifySet.erase(name); } } - }; - addFileSets(interfaceFileSetEntries); + } + if (!verifySet.empty()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property INTERFACE_HEADER_SETS_TO_VERIFY of target \"", + this->GetName(), + "\" contained the following header sets that are nonexistent " + "or not INTERFACE:\n ", + cmJoin(verifySet, "\n "))); + return false; + } cm::optional<std::set<std::string>> languages; for (auto* fileSet : fileSets) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4d636e4..9d61de9 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -690,6 +690,7 @@ void cmGlobalGenerator::EnableLanguage( } // One-time includes of user-provided project setup files + mf->GetState()->SetInTopLevelIncludes(true); std::string includes = mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES"); std::vector<std::string> includesList = cmExpandedList(includes); @@ -700,22 +701,26 @@ void cmGlobalGenerator::EnableLanguage( cmSystemTools::Error( "CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: " + setupFile); + mf->GetState()->SetInTopLevelIncludes(false); return; } if (cmSystemTools::FileIsDirectory(absSetupFile)) { cmSystemTools::Error( "CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: " + setupFile); + mf->GetState()->SetInTopLevelIncludes(false); return; } if (!mf->ReadListFile(absSetupFile)) { cmSystemTools::Error( "Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: " + setupFile); + mf->GetState()->SetInTopLevelIncludes(false); return; } } } + mf->GetState()->SetInTopLevelIncludes(false); // Check that the languages are supported by the generator and its // native build tool found above. diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx index c8520b8..d4ff1e0 100644 --- a/Source/cmGlobalMSYSMakefileGenerator.cxx +++ b/Source/cmGlobalMSYSMakefileGenerator.cxx @@ -42,34 +42,7 @@ std::string cmGlobalMSYSMakefileGenerator::FindMinGW( void cmGlobalMSYSMakefileGenerator::EnableLanguage( std::vector<std::string> const& l, cmMakefile* mf, bool optional) { - this->FindMakeProgram(mf); - const std::string& makeProgram = - mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - std::vector<std::string> locations; - std::string makeloc = cmSystemTools::GetProgramPath(makeProgram); - locations.push_back(this->FindMinGW(makeloc)); - locations.push_back(makeloc); - locations.push_back("/mingw/bin"); - locations.push_back("c:/mingw/bin"); - std::string tgcc = cmSystemTools::FindProgram("gcc", locations); - std::string gcc = "gcc.exe"; - if (!tgcc.empty()) { - gcc = tgcc; - } - std::string tgxx = cmSystemTools::FindProgram("g++", locations); - std::string gxx = "g++.exe"; - if (!tgxx.empty()) { - gxx = tgxx; - } - std::string trc = cmSystemTools::FindProgram("windres", locations); - std::string rc = "windres.exe"; - if (!trc.empty()) { - rc = trc; - } mf->AddDefinition("MSYS", "1"); - mf->AddDefinition("CMAKE_GENERATOR_CC", gcc); - mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx); - mf->AddDefinition("CMAKE_GENERATOR_RC", rc); this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional); if (!mf->IsSet("CMAKE_AR") && !this->CMakeInstance->GetIsInTryCompile() && diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx index 54d048d..5a7edae 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.cxx +++ b/Source/cmGlobalMinGWMakefileGenerator.cxx @@ -19,37 +19,6 @@ cmGlobalMinGWMakefileGenerator::cmGlobalMinGWMakefileGenerator(cmake* cm) cm->GetState()->SetMinGWMake(true); } -void cmGlobalMinGWMakefileGenerator::EnableLanguage( - std::vector<std::string> const& l, cmMakefile* mf, bool optional) -{ - this->FindMakeProgram(mf); - const std::string& makeProgram = - mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - std::vector<std::string> locations; - locations.push_back(cmSystemTools::GetProgramPath(makeProgram)); - locations.push_back("/mingw/bin"); - locations.push_back("c:/mingw/bin"); - std::string tgcc = cmSystemTools::FindProgram("gcc", locations); - std::string gcc = "gcc.exe"; - if (!tgcc.empty()) { - gcc = tgcc; - } - std::string tgxx = cmSystemTools::FindProgram("g++", locations); - std::string gxx = "g++.exe"; - if (!tgxx.empty()) { - gxx = tgxx; - } - std::string trc = cmSystemTools::FindProgram("windres", locations); - std::string rc = "windres.exe"; - if (!trc.empty()) { - rc = trc; - } - mf->AddDefinition("CMAKE_GENERATOR_CC", gcc); - mf->AddDefinition("CMAKE_GENERATOR_CXX", gxx); - mf->AddDefinition("CMAKE_GENERATOR_RC", rc); - this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional); -} - void cmGlobalMinGWMakefileGenerator::GetDocumentation( cmDocumentationEntry& entry) { diff --git a/Source/cmGlobalMinGWMakefileGenerator.h b/Source/cmGlobalMinGWMakefileGenerator.h index 1574faf..92d495c 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.h +++ b/Source/cmGlobalMinGWMakefileGenerator.h @@ -36,11 +36,4 @@ public: /** Get the documentation entry for this generator. */ static void GetDocumentation(cmDocumentationEntry& entry); - - /** - * Try to determine system information such as shared library - * extension, pthreads, byte order etc. - */ - virtual void EnableLanguage(std::vector<std::string> const& languages, - cmMakefile*, bool optional); }; diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index a47b3c0..40f3ab5 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -407,6 +407,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Common directories + std::string relativeBuildDir; { // Collapsed current binary directory std::string const cbd = cmSystemTools::CollapseFullPath( @@ -424,6 +425,8 @@ bool cmQtAutoGenInitializer::InitCustomTargets() cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen"); } cmSystemTools::ConvertToUnixSlashes(this->Dir.Build); + this->Dir.RelativeBuild = + cmSystemTools::RelativePath(cbd, this->Dir.Build); // Cleanup build directory this->AddCleanFile(this->Dir.Build); @@ -1357,7 +1360,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cmStrCat(this->Dir.Build, "/", timestampFileName); this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps"); this->AutogenTarget.DepFileRuleName = - cmStrCat(this->GenTarget->GetName(), "_autogen/", timestampFileName); + cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName); commandLines.push_back(cmMakeCommandLine( { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 603c537..33749ba 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -178,6 +178,7 @@ private: { std::string Info; std::string Build; + std::string RelativeBuild; std::string Work; ConfigString Include; std::string IncludeGenExp; diff --git a/Source/cmState.cxx b/Source/cmState.cxx index f1144e1..b753373 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -1072,3 +1072,12 @@ bool cmState::ParseCacheEntry(const std::string& entry, std::string& var, return flag; } + +cmState::Command cmState::GetDependencyProviderCommand( + cmDependencyProvider::Method method) const +{ + return (this->DependencyProvider && + this->DependencyProvider->SupportsMethod(method)) + ? this->GetCommand(this->DependencyProvider->GetCommand()) + : Command{}; +} diff --git a/Source/cmState.h b/Source/cmState.h index ee133fc..2d0c521 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -8,11 +8,16 @@ #include <memory> #include <set> #include <string> +#include <type_traits> #include <unordered_map> #include <unordered_set> +#include <utility> #include <vector> +#include <cm/optional> + #include "cmDefinitions.h" +#include "cmDependencyProvider.h" #include "cmLinkedTree.h" #include "cmPolicies.h" #include "cmProperty.h" @@ -227,6 +232,24 @@ public: ProjectKind GetProjectKind() const; + void ClearDependencyProvider() { this->DependencyProvider.reset(); } + void SetDependencyProvider(cmDependencyProvider provider) + { + this->DependencyProvider = std::move(provider); + } + cm::optional<cmDependencyProvider> const& GetDependencyProvider() const + { + return this->DependencyProvider; + } + Command GetDependencyProviderCommand( + cmDependencyProvider::Method method) const; + + void SetInTopLevelIncludes(bool inTopLevelIncludes) + { + this->ProcessingTopLevelIncludes = inTopLevelIncludes; + } + bool InTopLevelIncludes() const { return this->ProcessingTopLevelIncludes; } + private: friend class cmake; void AddCacheEntry(const std::string& key, const char* value, @@ -288,4 +311,6 @@ private: bool NinjaMulti = false; Mode StateMode = Unknown; ProjectKind StateProjectKind = ProjectKind::Normal; + cm::optional<cmDependencyProvider> DependencyProvider; + bool ProcessingTopLevelIncludes = false; }; diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index f0a087e..6f90bf7 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -464,6 +464,7 @@ add_RunCMake_test(message) add_RunCMake_test(option) add_RunCMake_test(project -DCMake_TEST_RESOURCES=${CMake_TEST_RESOURCES}) add_RunCMake_test(project_injected) +add_RunCMake_test(DependencyProviders) add_RunCMake_test(return) add_RunCMake_test(separate_arguments) add_RunCMake_test(set_property) diff --git a/Tests/RunCMake/DependencyProviders/AfterProject-result.txt b/Tests/RunCMake/DependencyProviders/AfterProject-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/AfterProject-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/AfterProject-stderr.txt b/Tests/RunCMake/DependencyProviders/AfterProject-stderr.txt new file mode 100644 index 0000000..7bee23c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/AfterProject-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Dependency providers can only be set as part of the first + call to project\(\)\. More specifically, + cmake_language\(SET_DEPENDENCY_PROVIDER\) can only be called while the first + project\(\) command processes files listed in + CMAKE_PROJECT_TOP_LEVEL_INCLUDES\. diff --git a/Tests/RunCMake/DependencyProviders/BeforeProject-result.txt b/Tests/RunCMake/DependencyProviders/BeforeProject-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/BeforeProject-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/BeforeProject-stderr.txt b/Tests/RunCMake/DependencyProviders/BeforeProject-stderr.txt new file mode 100644 index 0000000..7bee23c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/BeforeProject-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Dependency providers can only be set as part of the first + call to project\(\)\. More specifically, + cmake_language\(SET_DEPENDENCY_PROVIDER\) can only be called while the first + project\(\) command processes files listed in + CMAKE_PROJECT_TOP_LEVEL_INCLUDES\. diff --git a/Tests/RunCMake/DependencyProviders/Bypass-stdout.txt b/Tests/RunCMake/DependencyProviders/Bypass-stdout.txt new file mode 100644 index 0000000..b0c7e6e --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/Bypass-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- Forwarding find_package\(SomeDep\) +-- Provider invoked for method FIND_PACKAGE with args: QUIET;REQUIRED +-- SomeDepConfig\.cmake was used +-- Leaving provider +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/Bypass.cmake b/Tests/RunCMake/DependencyProviders/Bypass.cmake new file mode 100644 index 0000000..883087e --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/Bypass.cmake @@ -0,0 +1 @@ +find_package(SomeDep QUIET REQUIRED) diff --git a/Tests/RunCMake/DependencyProviders/CMakeLists.txt b/Tests/RunCMake/DependencyProviders/CMakeLists.txt new file mode 100644 index 0000000..3552604 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.23...3.24) + +if(DEFINED include_before_project) + include("${include_before_project}") +endif() + +project(${RunCMake_TEST} NONE) + +if(DEFINED include_after_project) + include("${include_after_project}") +endif() + +include(${RunCMake_TEST}.cmake OPTIONAL) diff --git a/Tests/RunCMake/DependencyProviders/ConfigFiles/SomeDepConfig.cmake b/Tests/RunCMake/DependencyProviders/ConfigFiles/SomeDepConfig.cmake new file mode 100644 index 0000000..e04eefe --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ConfigFiles/SomeDepConfig.cmake @@ -0,0 +1,2 @@ +message(STATUS "SomeDepConfig.cmake was used") +set(SomeDep_FOUND TRUE) diff --git a/Tests/RunCMake/DependencyProviders/FetchContentSerial-stdout.txt b/Tests/RunCMake/DependencyProviders/FetchContentSerial-stdout.txt new file mode 100644 index 0000000..fa4a794 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/FetchContentSerial-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- AThing_FOUND = 0 +-- Intercepted FetchContent_MakeAvailable\(SomeDep\) +-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/FetchContentSerial-build/_deps/somedep-build;SOURCE_SUBDIR;DoesNotExist +-- FetchContent_MakeAvailable\(\) succeeded +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/FetchContentSerial.cmake b/Tests/RunCMake/DependencyProviders/FetchContentSerial.cmake new file mode 100644 index 0000000..cbd3010 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/FetchContentSerial.cmake @@ -0,0 +1 @@ +include(try_methods.cmake) diff --git a/Tests/RunCMake/DependencyProviders/FindPackage-stdout.txt b/Tests/RunCMake/DependencyProviders/FindPackage-stdout.txt new file mode 100644 index 0000000..19c88b9 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/FindPackage-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- Intercepted find_package\(AThing\) +-- Provider invoked for method FIND_PACKAGE with args: QUIET +-- AThing_FOUND = TRUE +-- FetchContent_MakeAvailable\(\) succeeded +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/FindPackage.cmake b/Tests/RunCMake/DependencyProviders/FindPackage.cmake new file mode 100644 index 0000000..cbd3010 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/FindPackage.cmake @@ -0,0 +1 @@ +include(try_methods.cmake) diff --git a/Tests/RunCMake/DependencyProviders/NoCommand-result.txt b/Tests/RunCMake/DependencyProviders/NoCommand-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoCommand-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/NoCommand-stderr.txt b/Tests/RunCMake/DependencyProviders/NoCommand-stderr.txt new file mode 100644 index 0000000..a43222f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoCommand-stderr.txt @@ -0,0 +1,3 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Must specify a non-empty command name when provider methods + are given diff --git a/Tests/RunCMake/DependencyProviders/NoCommandOrMethods-stdout.txt b/Tests/RunCMake/DependencyProviders/NoCommandOrMethods-stdout.txt new file mode 100644 index 0000000..c53435b --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoCommandOrMethods-stdout.txt @@ -0,0 +1,3 @@ +-- Before cmake_language +-- After cmake_language +-- AThing_FOUND = 0 diff --git a/Tests/RunCMake/DependencyProviders/NoCommandOrMethods.cmake b/Tests/RunCMake/DependencyProviders/NoCommandOrMethods.cmake new file mode 100644 index 0000000..bde0cf8 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoCommandOrMethods.cmake @@ -0,0 +1,3 @@ +# Force the provider to be invoked +find_package(AThing QUIET) +message(STATUS "AThing_FOUND = ${AThing_FOUND}") diff --git a/Tests/RunCMake/DependencyProviders/NoMethods-result.txt b/Tests/RunCMake/DependencyProviders/NoMethods-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoMethods-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/NoMethods-stderr.txt b/Tests/RunCMake/DependencyProviders/NoMethods-stderr.txt new file mode 100644 index 0000000..6968851 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/NoMethods-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Must specify at least one provider method diff --git a/Tests/RunCMake/DependencyProviders/PassThroughProvider-stdout.txt b/Tests/RunCMake/DependencyProviders/PassThroughProvider-stdout.txt new file mode 100644 index 0000000..0c9303a --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/PassThroughProvider-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- Null provider called +-- Provider invoked for method FIND_PACKAGE with args: AThing;QUIET +-- AThing_FOUND = 0 +-- Null provider called +-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SomeDep;SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/PassThroughProvider-build/_deps/somedep-build;SOURCE_SUBDIR;DoesNotExist diff --git a/Tests/RunCMake/DependencyProviders/PassThroughProvider.cmake b/Tests/RunCMake/DependencyProviders/PassThroughProvider.cmake new file mode 100644 index 0000000..cbd3010 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/PassThroughProvider.cmake @@ -0,0 +1 @@ +include(try_methods.cmake) diff --git a/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-result.txt b/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-stderr.txt b/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-stderr.txt new file mode 100644 index 0000000..7bee23c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProjectIncludeAfter-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Dependency providers can only be set as part of the first + call to project\(\)\. More specifically, + cmake_language\(SET_DEPENDENCY_PROVIDER\) can only be called while the first + project\(\) command processes files listed in + CMAKE_PROJECT_TOP_LEVEL_INCLUDES\. diff --git a/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-result.txt b/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-stderr.txt b/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-stderr.txt new file mode 100644 index 0000000..7bee23c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ProjectIncludeBefore-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Dependency providers can only be set as part of the first + call to project\(\)\. More specifically, + cmake_language\(SET_DEPENDENCY_PROVIDER\) can only be called while the first + project\(\) command processes files listed in + CMAKE_PROJECT_TOP_LEVEL_INCLUDES\. diff --git a/Tests/RunCMake/DependencyProviders/Recurse-stdout.txt b/Tests/RunCMake/DependencyProviders/Recurse-stdout.txt new file mode 100644 index 0000000..2c2035a --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/Recurse-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- Intercepted FetchContent_MakeAvailable\(SomeDep\) +-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders/Recurse-build/_deps/somedep-src;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/Recurse-build/_deps/somedep-build;DOWNLOAD_COMMAND;.*/cmake(\.exe)?;-E;echo;Download command called +.*Download command called +.*-- Should now be handled +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/Recurse.cmake b/Tests/RunCMake/DependencyProviders/Recurse.cmake new file mode 100644 index 0000000..3a79d9c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/Recurse.cmake @@ -0,0 +1,8 @@ +include(FetchContent) + +set(FETCHCONTENT_QUIET NO) + +FetchContent_Declare(SomeDep + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command called" +) +FetchContent_MakeAvailable(SomeDep) diff --git a/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-result.txt b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stderr.txt b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stderr.txt new file mode 100644 index 0000000..047a64b --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stderr.txt @@ -0,0 +1,11 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(find_package\): + Could not find a package configuration file provided by "SomeDep" with any + of the following names: + + SomeDepConfig\.cmake + somedep-config\.cmake + + Add the installation prefix of "SomeDep" to CMAKE_PREFIX_PATH or set + "SomeDep_DIR" to a directory containing one of the above files\. If + "SomeDep" provides a separate development package or SDK, be sure it has + been installed\. diff --git a/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stdout.txt b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stdout.txt new file mode 100644 index 0000000..a293324 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-stdout.txt @@ -0,0 +1,5 @@ +-- Before cmake_language +-- After cmake_language +-- AThing_FOUND = 0 +-- Redirecting FetchContent_MakeAvailable\(SomeDep\) to find_package\(\) +-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial-build/_deps/somedep-build;SOURCE_SUBDIR;DoesNotExist diff --git a/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial.cmake b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial.cmake new file mode 100644 index 0000000..cbd3010 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFetchContentSerial.cmake @@ -0,0 +1 @@ +include(try_methods.cmake) diff --git a/Tests/RunCMake/DependencyProviders/RedirectFindPackage-stdout.txt b/Tests/RunCMake/DependencyProviders/RedirectFindPackage-stdout.txt new file mode 100644 index 0000000..23e751d --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFindPackage-stdout.txt @@ -0,0 +1,7 @@ +-- Before cmake_language +-- After cmake_language +-- Redirecting find_package\(AThing\) to FetchContent_MakeAvailable\(\) +-- Provider invoked for method FIND_PACKAGE with args: QUIET +-- AThing_FOUND = TRUE +-- FetchContent_MakeAvailable\(\) succeeded +-- Configuring done diff --git a/Tests/RunCMake/DependencyProviders/RedirectFindPackage.cmake b/Tests/RunCMake/DependencyProviders/RedirectFindPackage.cmake new file mode 100644 index 0000000..cbd3010 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RedirectFindPackage.cmake @@ -0,0 +1 @@ +include(try_methods.cmake) diff --git a/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake new file mode 100644 index 0000000..42893d2 --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake @@ -0,0 +1,73 @@ +include(RunCMake) + +run_cmake_with_options(BeforeProject + -D "include_before_project=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=find_package" +) +run_cmake_with_options(AfterProject + -D "include_after_project=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=find_package" +) +run_cmake_with_options(ProjectIncludeBefore + -D "CMAKE_PROJECT_INCLUDE_BEFORE=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=find_package" +) +run_cmake_with_options(ProjectIncludeAfter + -D "CMAKE_PROJECT_INCLUDE=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=find_package" +) +run_cmake_with_options(ToolchainFile + -D "CMAKE_TOOLCHAIN_FILE=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=find_package" +) +run_cmake_with_options(NoCommand + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_methods=find_package" +) +run_cmake_with_options(NoMethods + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=null_provider" +) +run_cmake_with_options(NoCommandOrMethods + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" +) +run_cmake_with_options(PassThroughProvider + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=null_provider" + -D "provider_methods=FIND_PACKAGE\\;FETCHCONTENT_MAKEAVAILABLE_SERIAL" +) +run_cmake_with_options(FindPackage + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=find_package_provider" + -D "provider_methods=FIND_PACKAGE" +) +run_cmake_with_options(RedirectFindPackage + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=redirect_find_package_provider" + -D "provider_methods=FIND_PACKAGE" +) +run_cmake_with_options(FetchContentSerial + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=FetchContentSerial_provider" + -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL" +) +run_cmake_with_options(RedirectFetchContentSerial + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=redirect_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" + -D "provider_methods=FIND_PACKAGE" +) +run_cmake_with_options(Recurse + -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake" + -D "provider_command=recurse_FetchContent" + -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL" +) diff --git a/Tests/RunCMake/DependencyProviders/ToolchainFile-result.txt b/Tests/RunCMake/DependencyProviders/ToolchainFile-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ToolchainFile-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DependencyProviders/ToolchainFile-stderr.txt b/Tests/RunCMake/DependencyProviders/ToolchainFile-stderr.txt new file mode 100644 index 0000000..7bee23c --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/ToolchainFile-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at set_provider\.cmake:[0-9]+ \(cmake_language\): + cmake_language Dependency providers can only be set as part of the first + call to project\(\)\. More specifically, + cmake_language\(SET_DEPENDENCY_PROVIDER\) can only be called while the first + project\(\) command processes files listed in + CMAKE_PROJECT_TOP_LEVEL_INCLUDES\. diff --git a/Tests/RunCMake/DependencyProviders/set_provider.cmake b/Tests/RunCMake/DependencyProviders/set_provider.cmake new file mode 100644 index 0000000..6e82b8f --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/set_provider.cmake @@ -0,0 +1,64 @@ +include(FetchContent) + +macro(null_provider method) + message(STATUS "Null provider called") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") +endmacro() + +macro(find_package_provider method package_name) + message(STATUS "Intercepted find_package(${package_name})") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + set(${package_name}_FOUND TRUE) +endmacro() + +macro(FetchContentSerial_provider method dep_name) + message(STATUS "Intercepted FetchContent_MakeAvailable(${dep_name})") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + FetchContent_SetPopulated(${dep_name}) +endmacro() + +macro(redirect_find_package_provider method package_name) + message(STATUS "Redirecting find_package(${package_name}) to FetchContent_MakeAvailable()") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + FetchContent_Declare(${package_name} + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} + SOURCE_SUBDIR DoesNotExist + ) + FetchContent_MakeAvailable(${package_name}) + set(${package_name}_FOUND TRUE) +endmacro() + +macro(redirect_FetchContentSerial_provider method dep_name) + message(STATUS "Redirecting FetchContent_MakeAvailable(${dep_name}) to find_package()") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + find_package(${dep_name} NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR}/Finders + REQUIRED + ) + FetchContent_SetPopulated(${dep_name}) +endmacro() + +macro(forward_find_package method package_name) + message(STATUS "Forwarding find_package(${package_name})") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + find_package(${package_name} + BYPASS_PROVIDER + PATHS ${CMAKE_CURRENT_LIST_DIR}/ConfigFiles + ${ARGN} + ) + message(STATUS "Leaving provider") +endmacro() + +macro(recurse_FetchContent method dep_name) + message(STATUS "Intercepted FetchContent_MakeAvailable(${dep_name})") + message(STATUS "Provider invoked for method ${method} with args: ${ARGN}") + FetchContent_MakeAvailable(${dep_name}) + message(STATUS "Should now be handled") +endmacro() + +message(STATUS "Before cmake_language") +cmake_language( + SET_DEPENDENCY_PROVIDER ${provider_command} + SUPPORTED_METHODS ${provider_methods} +) +message(STATUS "After cmake_language") diff --git a/Tests/RunCMake/DependencyProviders/try_methods.cmake b/Tests/RunCMake/DependencyProviders/try_methods.cmake new file mode 100644 index 0000000..652c32d --- /dev/null +++ b/Tests/RunCMake/DependencyProviders/try_methods.cmake @@ -0,0 +1,12 @@ +# Force the provider to be invoked for each method +find_package(AThing QUIET) +message(STATUS "AThing_FOUND = ${AThing_FOUND}") + +# These declared details should always succeed when used +include(FetchContent) +FetchContent_Declare(SomeDep + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} + SOURCE_SUBDIR DoesNotExist +) +FetchContent_MakeAvailable(SomeDep) +message(STATUS "FetchContent_MakeAvailable() succeeded") diff --git a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake index edc655b..f022a43 100644 --- a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake @@ -40,3 +40,8 @@ endif() run_cmake_build(VerifyHeaderSets lang_test_c_verify_interface_header_sets) run_cmake_build(VerifyHeaderSets lang_test_cxx_verify_interface_header_sets) +run_cmake_build(VerifyHeaderSets list_verify_interface_header_sets) + +set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON) +run_cmake(VerifyHeaderSetsNonexistent) +unset(RunCMake_TEST_OPTIONS) diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake index 24298df..82ed935 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake @@ -58,3 +58,13 @@ target_sources(lang_test_c INTERFACE FILE_SET HEADERS FILES lang_test.h) add_library(lang_test_cxx STATIC lib.c lib.cxx) target_compile_definitions(lang_test_cxx INTERFACE EXPECT_CXX) target_sources(lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h) + +set_property(SOURCE error.h PROPERTY LANGUAGE C) + +add_library(list STATIC lib.c) +target_sources(list INTERFACE + FILE_SET a TYPE HEADERS FILES a.h + FILE_SET c TYPE HEADERS FILES dir/c.h + FILE_SET error TYPE HEADERS FILES error.h + ) +set_property(TARGET list PROPERTY INTERFACE_HEADER_SETS_TO_VERIFY "a;c") diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-result.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-stderr.txt b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-stderr.txt new file mode 100644 index 0000000..76c2f94 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error in CMakeLists\.txt: + Property INTERFACE_HEADER_SETS_TO_VERIFY of target "nonexistent" contained + the following header sets that are nonexistent or not INTERFACE: + + b + c + + +CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent.cmake new file mode 100644 index 0000000..b269b73 --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +add_library(nonexistent STATIC lib.c) +target_sources(nonexistent INTERFACE FILE_SET a TYPE HEADERS FILES a.h) +set_property(TARGET nonexistent PROPERTY INTERFACE_HEADER_SETS_TO_VERIFY "a;c;b") diff --git a/Tests/RunCMake/VerifyHeaderSets/error.h b/Tests/RunCMake/VerifyHeaderSets/error.h new file mode 100644 index 0000000..cbba5ae --- /dev/null +++ b/Tests/RunCMake/VerifyHeaderSets/error.h @@ -0,0 +1,3 @@ +#error "This file should not be included" + +extern void error_h(void); |