1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-21 06:10:16 +08:00

project(): Add new CMAKE_PROJECT_TOP_LEVEL_INCLUDES file injection point

Fixes: #22685
This commit is contained in:
Craig Scott
2022-05-10 22:52:18 +10:00
parent 8aa29a1793
commit a6c34b0353
22 changed files with 167 additions and 28 deletions

View File

@@ -123,28 +123,56 @@ The options are:
The variables set through the ``VERSION``, ``DESCRIPTION`` and ``HOMEPAGE_URL``
options are intended for use as default values in package metadata and documentation.
.. _`Code Injection`:
Code Injection
^^^^^^^^^^^^^^
If the :variable:`CMAKE_PROJECT_INCLUDE_BEFORE` or
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` variables are set,
the files they point to will be included as the first step of the
``project()`` command.
If both are set, then :variable:`CMAKE_PROJECT_INCLUDE_BEFORE` will be
included before :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`.
A number of variables can be defined by the user to specify files to include
at different points during the execution of the ``project()`` command.
The following outlines the steps performed during a ``project()`` call:
If the :variable:`CMAKE_PROJECT_INCLUDE` or
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE` variables are set, the files
they point to will be included as the last step of the ``project()`` command.
If both are set, then :variable:`CMAKE_PROJECT_INCLUDE` will be included before
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`.
* .. versionadded:: 3.15
For every ``project()`` call regardless of the project
name, include the file named by :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
if set.
.. versionadded:: 3.15
Added the ``CMAKE_PROJECT_INCLUDE`` and ``CMAKE_PROJECT_INCLUDE_BEFORE``
variables.
* .. versionadded:: 3.17
If the ``project()`` command specifies ``<PROJECT-NAME>`` as its project
name, include the file named by
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`, if set.
.. versionadded:: 3.17
Added the ``CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`` variable.
* Set the various project-specific variables detailed in the `Synopsis`_
and `Options`_ sections above.
* For the very first ``project()`` call only:
* If :variable:`CMAKE_TOOLCHAIN_FILE` is set, read it at least once.
It may be read multiple times and it may also be read again when
enabling languages later (see below).
* Set the variables describing the host and target platforms.
Language-specific variables might or might not be set at this point.
On the first run, the only language-specific variables that might be
defined are those a toolchain file may have set. On subsequent runs,
language-specific variables cached from a previous run may be set.
* .. versionadded:: 3.24
Include each file listed in :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES`,
if set. The variable is ignored by CMake thereafter.
* Enable any languages specified in the call, or the default languages if
none were provided. The toolchain file may be re-read when enabling a
language for the first time.
* .. versionadded:: 3.15
For every ``project()`` call regardless of the project
name, include the file named by :variable:`CMAKE_PROJECT_INCLUDE`,
if set.
* If the ``project()`` command specifies ``<PROJECT-NAME>`` as its project
name, include the file named by
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, if set.
Usage
^^^^^

View File

@@ -243,6 +243,7 @@ Variables that Change Behavior
/variable/CMAKE_PROJECT_INCLUDE_BEFORE
/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE
/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE
/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES
/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName
/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
/variable/CMAKE_STAGING_PREFIX

View File

@@ -0,0 +1,6 @@
CMAKE_PROJECT_TOP_LEVEL_INCLUDES
--------------------------------
* The :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable was added to allow
injecting custom code at the site of the first :command:`project` call,
after the host and target platform details have been determined.

View File

@@ -5,8 +5,11 @@ CMAKE_PROJECT_INCLUDE
A CMake language file or module 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.
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.
See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` and
:variable:`CMAKE_PROJECT_INCLUDE_BEFORE` variables.
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
:variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, and
:variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.

View File

@@ -5,8 +5,11 @@ CMAKE_PROJECT_INCLUDE_BEFORE
A CMake language file or module 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.
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.
See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` and
:variable:`CMAKE_PROJECT_INCLUDE` variables.
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
:variable:`CMAKE_PROJECT_INCLUDE`, and
:variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.

View File

@@ -4,8 +4,9 @@ CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE
A CMake language file or module 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.
modifying their source. See :ref:`Code Injection` for a more detailed
discussion of files potentially included during a :command:`project` call.
See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
:variable:`CMAKE_PROJECT_INCLUDE` and
:variable:`CMAKE_PROJECT_INCLUDE_BEFORE` variables.
:variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.

View File

@@ -6,8 +6,9 @@ CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE
A CMake language file or module 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.
modifying their source. See :ref:`Code Injection` for a more detailed
discussion of files potentially included during a :command:`project` call.
See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
:variable:`CMAKE_PROJECT_INCLUDE` and
:variable:`CMAKE_PROJECT_INCLUDE_BEFORE` variables.
:variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.

View File

@@ -0,0 +1,27 @@
CMAKE_PROJECT_TOP_LEVEL_INCLUDES
--------------------------------
.. versionadded:: 3.24
:ref:`Semicolon-separated list <CMake Language Lists>` of CMake language
files to include as part of the very first :command:`project` call.
The files will be included immediately after the toolchain file has been read
(if one is specified) and platform variables have been set, but before any
languages have been enabled. Therefore, language-specific variables,
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.
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
their own custom build types), and so on. It is primarily for users to add
things specific to their environment, but not for specifying the toolchain
details (use :variable:`CMAKE_TOOLCHAIN_FILE` for that).
By default, this variable is empty. It is intended to be set by the user.
See also the :variable:`CMAKE_PROJECT_INCLUDE`,
:variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, and
:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` variables.

View File

@@ -13,3 +13,6 @@ build directory, and if not found, relative to the source directory.
This is initialized by the :envvar:`CMAKE_TOOLCHAIN_FILE` environment
variable if it is set when a new build tree is first created.
See the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable for setting
other things not directly related to the toolchain.

View File

@@ -688,6 +688,33 @@ void cmGlobalGenerator::EnableLanguage(
if (!this->FindMakeProgram(mf)) {
return;
}
// One-time includes of user-provided project setup files
std::string includes =
mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
std::vector<std::string> includesList = cmExpandedList(includes);
for (std::string const& setupFile : includesList) {
std::string absSetupFile = cmSystemTools::CollapseFullPath(
setupFile, mf->GetCurrentSourceDirectory());
if (!cmSystemTools::FileExists(absSetupFile)) {
cmSystemTools::Error(
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: " +
setupFile);
return;
}
if (cmSystemTools::FileIsDirectory(absSetupFile)) {
cmSystemTools::Error(
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: " +
setupFile);
return;
}
if (!mf->ReadListFile(absSetupFile)) {
cmSystemTools::Error(
"Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: " +
setupFile);
return;
}
}
}
// Check that the languages are supported by the generator and its

View File

@@ -0,0 +1,10 @@
(-- )?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

View File

@@ -0,0 +1 @@
add_subdirectory(CodeInjection)

View File

@@ -0,0 +1,2 @@
message(STATUS "Calling sub-project")
project(SubProj LANGUAGES NONE)

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_INCLUDE")

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE")

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE")

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE")

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file")

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file")

View File

@@ -0,0 +1,10 @@
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_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 ""
)

View File

@@ -0,0 +1 @@
message(STATUS "Included CMAKE_TOOLCHAIN_FILE")

View File

@@ -1,5 +1,14 @@
include(RunCMake)
# Use an initial cache file to define the project() variables
# to avoid long command lines. Also see the CMakeOnly test case
# 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"
)
if(CMake_TEST_RESOURCES)
run_cmake(ExplicitRC)
endif()