diff options
author | Craig Scott <craig.scott@crascit.com> | 2022-05-10 12:52:18 (GMT) |
---|---|---|
committer | Craig Scott <craig.scott@crascit.com> | 2022-05-13 08:03:36 (GMT) |
commit | a6c34b0353e62457465248334703cbd641b2545c (patch) | |
tree | 7d51fc255e6b3fc36c474a80e08704be0c9f3208 | |
parent | 8aa29a1793f8f5c34b92864e53d6806725e5aa57 (diff) | |
download | CMake-a6c34b0353e62457465248334703cbd641b2545c.zip CMake-a6c34b0353e62457465248334703cbd641b2545c.tar.gz CMake-a6c34b0353e62457465248334703cbd641b2545c.tar.bz2 |
project(): Add new CMAKE_PROJECT_TOP_LEVEL_INCLUDES file injection point
Fixes: #22685
22 files changed, 170 insertions, 31 deletions
diff --git a/Help/command/project.rst b/Help/command/project.rst index 2a9dcfe..8f32fa3 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst @@ -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`. - -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 - Added the ``CMAKE_PROJECT_INCLUDE`` and ``CMAKE_PROJECT_INCLUDE_BEFORE`` - variables. - -.. versionadded:: 3.17 - Added the ``CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`` variable. +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: + +* .. 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.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. + +* 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 ^^^^^ diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 98fc228..b82ff2f 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -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 diff --git a/Help/release/dev/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst b/Help/release/dev/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst new file mode 100644 index 0000000..ca2d223 --- /dev/null +++ b/Help/release/dev/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst @@ -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. diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE.rst index 41d9e5d..76b9d92 100644 --- a/Help/variable/CMAKE_PROJECT_INCLUDE.rst +++ b/Help/variable/CMAKE_PROJECT_INCLUDE.rst @@ -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. diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst index c2fd0f8..9a8c4b5 100644 --- a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst +++ b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst @@ -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. diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst index 74247f1..3bb5cd8 100644 --- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst +++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst @@ -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. diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst index 39abb12..ca584c1 100644 --- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst +++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst @@ -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. diff --git a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst new file mode 100644 index 0000000..2010b08 --- /dev/null +++ b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst @@ -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. diff --git a/Help/variable/CMAKE_TOOLCHAIN_FILE.rst b/Help/variable/CMAKE_TOOLCHAIN_FILE.rst index ff8d59a..1117c1f 100644 --- a/Help/variable/CMAKE_TOOLCHAIN_FILE.rst +++ b/Help/variable/CMAKE_TOOLCHAIN_FILE.rst @@ -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. diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 09e2abe..3831546 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -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 diff --git a/Tests/RunCMake/project/CodeInjection-stdout.txt b/Tests/RunCMake/project/CodeInjection-stdout.txt new file mode 100644 index 0000000..88ac966 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection-stdout.txt @@ -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 diff --git a/Tests/RunCMake/project/CodeInjection.cmake b/Tests/RunCMake/project/CodeInjection.cmake new file mode 100644 index 0000000..dcf56a1 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection.cmake @@ -0,0 +1 @@ +add_subdirectory(CodeInjection) diff --git a/Tests/RunCMake/project/CodeInjection/CMakeLists.txt b/Tests/RunCMake/project/CodeInjection/CMakeLists.txt new file mode 100644 index 0000000..8ee99d0 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/CMakeLists.txt @@ -0,0 +1,2 @@ +message(STATUS "Calling sub-project") +project(SubProj LANGUAGES NONE) diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake new file mode 100644 index 0000000..f3f0a7e --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake new file mode 100644 index 0000000..01d53c9 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake new file mode 100644 index 0000000..d68de6a --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake new file mode 100644 index 0000000..ef3bfc0 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_1.cmake new file mode 100644 index 0000000..73ad037 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_1.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file") diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_2.cmake new file mode 100644 index 0000000..80f9705 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/cmake_project_top_level_includes_2.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file") diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache.cmake new file mode 100644 index 0000000..6c8995b --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/initial_cache.cmake @@ -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 "" +) diff --git a/Tests/RunCMake/project/CodeInjection/passthrough_toolchain_file.cmake b/Tests/RunCMake/project/CodeInjection/passthrough_toolchain_file.cmake new file mode 100644 index 0000000..d045712 --- /dev/null +++ b/Tests/RunCMake/project/CodeInjection/passthrough_toolchain_file.cmake @@ -0,0 +1 @@ +message(STATUS "Included CMAKE_TOOLCHAIN_FILE") diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index d7dab12..945d9ed 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -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() |