mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-09 17:18:18 +00:00
FILE_SET: Add INDEPENDENT_FILES property for Ninja order dependency control
Fixes: #25370
This commit is contained in:
@@ -545,6 +545,7 @@ Properties on File Sets
|
||||
/prop_fs/COMPILE_OPTIONS
|
||||
/prop_fs/CXX_SCAN_FOR_MODULES
|
||||
/prop_fs/INCLUDE_DIRECTORIES
|
||||
/prop_fs/INDEPENDENT_FILES
|
||||
/prop_fs/INTERFACE_COMPILE_DEFINITIONS
|
||||
/prop_fs/INTERFACE_COMPILE_OPTIONS
|
||||
/prop_fs/INTERFACE_INCLUDE_DIRECTORIES
|
||||
|
||||
33
Help/prop_fs/INDEPENDENT_FILES.rst
Normal file
33
Help/prop_fs/INDEPENDENT_FILES.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
INDEPENDENT_FILES
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 4.4
|
||||
|
||||
``INDEPENDENT_FILES`` is a boolean specifying that any :prop_sf:`GENERATED`
|
||||
sources in the file set are not necessary for the compilation of other sources
|
||||
in the same target. Stated another way, these files are "independent" and their
|
||||
presence is never necessary for compilation of other sources (e.g., via
|
||||
``#include``).
|
||||
|
||||
When this property is ``ON``, :ref:`Ninja Generators` will omit conservative
|
||||
order-only dependencies that prevent a target's source files from compiling
|
||||
before custom commands from the target's dependencies are finished, even if
|
||||
those custom commands only produce sources independent from other sources in
|
||||
the same target.
|
||||
When this property is ``OFF``, :ref:`Ninja Generators` will apply conservative
|
||||
order-only dependencies that prevent a target's source files from compiling
|
||||
before custom commands from the target's dependencies are finished, even if
|
||||
those custom commands only produce sources independent from other sources in
|
||||
the same target.
|
||||
|
||||
If this property is not defined, the following default will be applied:
|
||||
* ``ON`` for ``CXX_MODULES`` file set type.
|
||||
* ``OFF`` for all other file set types.
|
||||
|
||||
In the case of the ``CXX_MODULES`` file set type, the only supported mode is
|
||||
``ON``. So if the property is set to ``OFF``, it will be ignored.
|
||||
|
||||
In the case of the ``HEADERS`` file set type, if the property is ``ON``, an
|
||||
author warning will be emit because this mode is not supported.
|
||||
|
||||
This property is effective only when the policy :policy:`CMP0154` is ``NEW``.
|
||||
5
Help/release/dev/FILE_SET-INDEPENDENT_FILES.rst
Normal file
5
Help/release/dev/FILE_SET-INDEPENDENT_FILES.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
FILE_SET-INDEPENDENT_FILES
|
||||
--------------------------
|
||||
|
||||
* :ref:`File sets <file sets>` learned to support the
|
||||
:prop_fs:`INDEPENDENT_FILES` property.
|
||||
@@ -83,13 +83,19 @@ namespace {
|
||||
std::map<cm::string_view, FileSetDescriptor> const FileSetDescriptors{
|
||||
{ cm::FileSetMetadata::HEADERS,
|
||||
{ cm::FileSetMetadata::HEADERS,
|
||||
cm::FileSetMetadata::FileSetLookup::Target } },
|
||||
cm::FileSetMetadata::FileSetLookup::Target,
|
||||
{ DependencyMode ::Includables },
|
||||
DependencyMode ::Includables } },
|
||||
{ cm::FileSetMetadata::SOURCES,
|
||||
{ cm::FileSetMetadata::SOURCES,
|
||||
cm::FileSetMetadata::FileSetLookup::Dependencies } },
|
||||
cm::FileSetMetadata::FileSetLookup::Dependencies,
|
||||
{ DependencyMode ::IndependentFiles, DependencyMode ::Includables },
|
||||
DependencyMode ::Includables } },
|
||||
{ cm::FileSetMetadata::CXX_MODULES,
|
||||
{ cm::FileSetMetadata::CXX_MODULES,
|
||||
cm::FileSetMetadata::FileSetLookup::Target } },
|
||||
cm::FileSetMetadata::FileSetLookup::Target,
|
||||
{ DependencyMode ::IndependentFiles },
|
||||
DependencyMode ::IndependentFiles } },
|
||||
};
|
||||
|
||||
std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES };
|
||||
@@ -106,6 +112,29 @@ cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type)
|
||||
return cm::nullopt;
|
||||
}
|
||||
|
||||
DependencyMode GetDependencyMode(cm::string_view type)
|
||||
{
|
||||
auto descriptor = GetFileSetDescriptor(type);
|
||||
if (descriptor) {
|
||||
return descriptor->DefaultDependency;
|
||||
}
|
||||
return DependencyMode::Includables;
|
||||
}
|
||||
DependencyMode GetDependencyMode(cm::string_view type,
|
||||
DependencyMode requestedMode)
|
||||
{
|
||||
auto descriptor = GetFileSetDescriptor(type);
|
||||
if (descriptor) {
|
||||
// Select the requested mode or the next-weakest mode that is supported by
|
||||
// the file set type
|
||||
auto mode = descriptor->SupportedDependencies.lower_bound(requestedMode);
|
||||
return mode == descriptor->SupportedDependencies.end()
|
||||
? descriptor->DefaultDependency
|
||||
: *mode;
|
||||
}
|
||||
return DependencyMode::Includables;
|
||||
}
|
||||
|
||||
std::vector<cm::string_view> const& GetKnownTypes()
|
||||
{
|
||||
return KnownTypes;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/optional>
|
||||
@@ -37,19 +39,49 @@ enum class FileSetLookup
|
||||
Dependencies
|
||||
};
|
||||
|
||||
// Define the various modes regarding graph dependency for
|
||||
// the generated files (Ninja specific)
|
||||
// items must be kept in this order: "Lower" modes are "stronger" in that they
|
||||
// have more restrictions (and therefore allow for more build graph
|
||||
// optimization).
|
||||
// std::set rely on it.
|
||||
enum class DependencyMode
|
||||
{
|
||||
IndependentFiles, // files in the file set are independent from each other
|
||||
Includables, // files can be used by another source during compilation
|
||||
};
|
||||
using DependencySet = std::set<DependencyMode>;
|
||||
|
||||
struct FileSetDescriptor
|
||||
{
|
||||
FileSetDescriptor(cm::string_view type, FileSetLookup lookup)
|
||||
FileSetDescriptor(cm::string_view type, FileSetLookup lookup,
|
||||
DependencySet dependencies,
|
||||
DependencyMode defaultDependency)
|
||||
: Type(type)
|
||||
, Lookup(lookup)
|
||||
, SupportedDependencies(std::move(dependencies))
|
||||
, DefaultDependency(defaultDependency)
|
||||
{
|
||||
}
|
||||
|
||||
FileSetDescriptor(FileSetLookup lookup)
|
||||
: Type()
|
||||
, Lookup(lookup)
|
||||
, SupportedDependencies({ DependencyMode::Includables })
|
||||
, DefaultDependency(DependencyMode::Includables)
|
||||
{
|
||||
}
|
||||
|
||||
cm::string_view const Type;
|
||||
FileSetLookup const Lookup;
|
||||
DependencySet const SupportedDependencies;
|
||||
DependencyMode const DefaultDependency;
|
||||
};
|
||||
|
||||
cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type);
|
||||
DependencyMode GetDependencyMode(cm::string_view type);
|
||||
DependencyMode GetDependencyMode(cm::string_view type,
|
||||
DependencyMode requestedMode);
|
||||
|
||||
std::vector<cm::string_view> const& GetKnownTypes();
|
||||
bool IsKnownType(cm::string_view type);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <cm/memory>
|
||||
#include <cm/optional>
|
||||
#include <cmext/algorithm>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmFileSetMetadata.h"
|
||||
#include "cmGenExContext.h"
|
||||
@@ -132,8 +131,7 @@ cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
|
||||
auto const it2 = info.InterfaceFileSetCache.find(path);
|
||||
if (it2 != info.InterfaceFileSetCache.end() &&
|
||||
cm::FileSetMetadata::GetFileSetDescriptor(it2->second->GetType())
|
||||
.value_or(
|
||||
cm::FileSetMetadata::FileSetDescriptor{ ""_s, Lookup::Target })
|
||||
.value_or(cm::FileSetMetadata::FileSetDescriptor{ Lookup::Target })
|
||||
.Lookup == Lookup::Dependencies) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
@@ -1089,17 +1089,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
|
||||
// Gather order-only dependencies on custom command outputs.
|
||||
std::vector<std::string> ccouts;
|
||||
std::vector<std::string> ccouts_private;
|
||||
bool usePrivateGeneratedSources = false;
|
||||
if (this->GeneratorTarget->HasFileSets()) {
|
||||
switch (this->GetGeneratorTarget()->GetPolicyStatusCMP0154()) {
|
||||
case cmPolicies::WARN:
|
||||
case cmPolicies::OLD:
|
||||
break;
|
||||
case cmPolicies::NEW:
|
||||
usePrivateGeneratedSources = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool usePrivateGeneratedSources = this->GeneratorTarget->HasFileSets() &&
|
||||
this->GetGeneratorTarget()->GetPolicyStatusCMP0154() == cmPolicies::NEW;
|
||||
|
||||
for (cmCustomCommand const* cc : customCommands) {
|
||||
cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator());
|
||||
std::vector<std::string> const& ccoutputs = ccg.GetOutputs();
|
||||
@@ -1115,15 +1107,51 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
|
||||
cmGeneratorFileSet const* fileset =
|
||||
this->GeneratorTarget->GetFileSetForSource(
|
||||
config, this->Makefile->GetOrCreateGeneratedSource(*it));
|
||||
bool isVisible = fileset && fileset->IsForInterface();
|
||||
bool isIncludeable = !fileset || fileset->CanBeIncluded();
|
||||
if (fileset && isVisible && isIncludeable) {
|
||||
++it;
|
||||
|
||||
if (!fileset) {
|
||||
// use private order dependency
|
||||
ccouts_private.push_back(*it);
|
||||
it = ccouts.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (!fileset || isIncludeable) {
|
||||
ccouts_private.push_back(*it);
|
||||
|
||||
using DependencyMode = cm::FileSetMetadata::DependencyMode;
|
||||
|
||||
cmValue independentFiles = fileset->GetProperty("INDEPENDENT_FILES");
|
||||
// retrieve default mode
|
||||
DependencyMode dependencyMode =
|
||||
cm::FileSetMetadata::GetDependencyMode(fileset->GetType());
|
||||
// if property is defined, try to enforce mode requested
|
||||
if (independentFiles) {
|
||||
dependencyMode = cm::FileSetMetadata::GetDependencyMode(
|
||||
fileset->GetType(),
|
||||
independentFiles.IsOn() ? DependencyMode::IndependentFiles
|
||||
: DependencyMode::Includables);
|
||||
}
|
||||
if (independentFiles.IsOn() &&
|
||||
dependencyMode != DependencyMode::IndependentFiles) {
|
||||
// requested dependency mode not supported
|
||||
this->GetMakefile()->IssueMessage(
|
||||
MessageType::AUTHOR_WARNING,
|
||||
cmStrCat(R"(the "INDEPENDENT_FILES" property of the file set ")",
|
||||
fileset->GetName(), R"(" of the target ")",
|
||||
this->GeneratorTarget->GetName(),
|
||||
R"(" will be ignored because it is incompatible with )"
|
||||
R"(the file set type ")",
|
||||
fileset->GetType(), R"(".)"));
|
||||
}
|
||||
if (dependencyMode == DependencyMode::Includables) {
|
||||
if (fileset->IsForInterface()) {
|
||||
// use public order dependency
|
||||
++it;
|
||||
} else {
|
||||
// use private order dependency
|
||||
ccouts_private.push_back(*it);
|
||||
it = ccouts.erase(it);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// no order dependency is required
|
||||
it = ccouts.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,6 +624,7 @@ add_RunCMake_test(TargetObjects)
|
||||
add_RunCMake_test(TargetProperties)
|
||||
add_RunCMake_test(FileSetProperties)
|
||||
add_RunCMake_test(FileSet-SOURCES -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
|
||||
-DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
|
||||
-DMSVC_VERSION=${MSVC_VERSION})
|
||||
add_RunCMake_test(ToolchainFile)
|
||||
|
||||
|
||||
@@ -27,5 +27,7 @@ target_compile_features(ninja_cmp0154 PUBLIC cxx_std_20)
|
||||
set_property(SOURCE unrelated.cxx
|
||||
PROPERTY
|
||||
CXX_SCAN_FOR_MODULES 0)
|
||||
# ensure property INDEPENDENT_FILES=OFF is ignored
|
||||
set_property(FILE_SET CXX_MODULES TARGET ninja_cmp0154 PROPERTY INDEPENDENT_FILES OFF)
|
||||
|
||||
add_test(NAME ninja_cmp0154 COMMAND ninja_cmp0154)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
if (EXISTS "${RunCMake_TEST_BINARY_DIR}/gen.c")
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"The `gen.c` file should not be generated to compile `unrelated`'s object")
|
||||
endif ()
|
||||
16
Tests/RunCMake/FileSet-SOURCES/IndependentFiles.cmake
Normal file
16
Tests/RunCMake/FileSet-SOURCES/IndependentFiles.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
enable_language(C)
|
||||
|
||||
set(CACHE{CMAKE_INTERMEDIATE_DIR_STRATEGY} TYPE STRING FORCE VALUE FULL)
|
||||
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen.c"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/gen.c"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in")
|
||||
|
||||
add_library(lib1 STATIC)
|
||||
target_sources(lib1 PRIVATE independent.c
|
||||
PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/gen.c")
|
||||
|
||||
set_property(FILE_SET SOURCES TARGET lib1 PROPERTY INDEPENDENT_FILES ON)
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Warning \(dev\) in CMakeLists\.txt:
|
||||
the "INDEPENDENT_FILES" property of the file set "HEADERS" of the target
|
||||
"lib1" will be ignored because it is incompatible with the file set type
|
||||
"HEADERS"\.
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
13
Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning.cmake
Normal file
13
Tests/RunCMake/FileSet-SOURCES/IndependentFilesWarning.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
enable_language(C)
|
||||
|
||||
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen.h"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/gen.h"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/gen.c.in")
|
||||
|
||||
add_library(lib1 STATIC lib1.c)
|
||||
target_sources(lib1 PRIVATE FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/gen.h")
|
||||
|
||||
set_property(FILE_SET HEADERS TARGET lib1 PROPERTY INDEPENDENT_FILES ON)
|
||||
@@ -34,6 +34,16 @@ run_and_build(CustomCommandInput)
|
||||
run_and_build(IncludeDirectoriesOrder)
|
||||
run_and_build(FileSetTransitivity)
|
||||
run_and_check(CompileOptionsOrder)
|
||||
if (RunCMake_GENERATOR MATCHES "Ninja")
|
||||
run_cmake(IndependentFilesWarning)
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(IndependentFiles_target "CMakeFiles/lib1.dir/Debug/independent.c${CMAKE_C_OUTPUT_EXTENSION}")
|
||||
else ()
|
||||
set(IndependentFiles_target "CMakeFiles/lib1.dir/independent.c${CMAKE_C_OUTPUT_EXTENSION}")
|
||||
endif ()
|
||||
set(RunCMake-check-file IndependentFiles-check.cmake)
|
||||
run_and_build(IndependentFiles ${IndependentFiles_target})
|
||||
endif()
|
||||
|
||||
# Some environments are excluded because they are not able to honor verbose mode
|
||||
if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode"
|
||||
|
||||
4
Tests/RunCMake/FileSet-SOURCES/gen.c.in
Normal file
4
Tests/RunCMake/FileSet-SOURCES/gen.c.in
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
void gen(void)
|
||||
{
|
||||
}
|
||||
4
Tests/RunCMake/FileSet-SOURCES/independent.c
Normal file
4
Tests/RunCMake/FileSet-SOURCES/independent.c
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
void independent(void)
|
||||
{
|
||||
}
|
||||
0
Tests/RunCMake/FileSet-SOURCES/lib1.h
Normal file
0
Tests/RunCMake/FileSet-SOURCES/lib1.h
Normal file
Reference in New Issue
Block a user