mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-04 14:50:23 +00:00
FILE_SET: relax file uniqueness for HEADERS type
Enable to declare in multiple sets headers files.
This complements commit 167e903c15 (FILE_SET: enforce uniqueness of files, 2026-03-06)
Issue: #27035
This commit is contained in:
@@ -3,16 +3,19 @@ CMP0211
|
||||
|
||||
.. versionadded:: 4.4
|
||||
|
||||
A file may belong to at most one :ref:`file set <file sets>` in a target.
|
||||
A file may belong to at most one :ref:`file set <file sets>` in a target except
|
||||
for the ``HEADERS`` file set type.
|
||||
|
||||
In CMake 4.3 and below, it was possible to specify the same file
|
||||
in multiple file sets in a target. In CMake 4.4 and above, a file
|
||||
may belong to at most one file set in a target. This policy provides
|
||||
compatibility for projects that have not been updated accordingly.
|
||||
may belong to at most one file set in a target except for the ``HEADERS`` file
|
||||
set type. This policy provides compatibility for projects that have not been
|
||||
updated accordingly.
|
||||
|
||||
The ``OLD`` behavior for this policy is to accept the same file in
|
||||
multiple file sets in a target. The ``NEW`` behavior for this policy
|
||||
is to accept a given file in at most one file set in a target.
|
||||
is to accept a given file in at most one file set in a target except for the
|
||||
``HEADERS`` file set type.
|
||||
|
||||
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4
|
||||
.. |WARNS_OR_DOES_NOT_WARN| replace::
|
||||
|
||||
@@ -86,19 +86,19 @@ std::map<cm::string_view, FileSetDescriptor> const FileSetDescriptors{
|
||||
cm::FileSetMetadata::FileSetLookup::Target,
|
||||
{ DependencyMode ::Includables },
|
||||
DependencyMode ::Includables,
|
||||
FrameworkCompatible::No } },
|
||||
{ cm::FileSetMetadata::FileSetAttributes::FilesInMultipleFileSets } } },
|
||||
{ cm::FileSetMetadata::SOURCES,
|
||||
{ cm::FileSetMetadata::SOURCES,
|
||||
cm::FileSetMetadata::FileSetLookup::Dependencies,
|
||||
{ DependencyMode ::IndependentFiles, DependencyMode ::Includables },
|
||||
DependencyMode ::Includables,
|
||||
FrameworkCompatible::Yes } },
|
||||
{ cm::FileSetMetadata::FileSetAttributes::FrameworkCompatible } } },
|
||||
{ cm::FileSetMetadata::CXX_MODULES,
|
||||
{ cm::FileSetMetadata::CXX_MODULES,
|
||||
cm::FileSetMetadata::FileSetLookup::Target,
|
||||
{ DependencyMode ::IndependentFiles },
|
||||
DependencyMode ::IndependentFiles,
|
||||
FrameworkCompatible::No } },
|
||||
{} } },
|
||||
};
|
||||
|
||||
std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES };
|
||||
@@ -138,13 +138,17 @@ DependencyMode GetDependencyMode(cm::string_view type,
|
||||
return DependencyMode::Includables;
|
||||
}
|
||||
|
||||
bool IsFrameworkSupported(cm::string_view type)
|
||||
AttributeSet GetAttributes(cm::string_view type)
|
||||
{
|
||||
auto descriptor = GetFileSetDescriptor(type);
|
||||
if (descriptor) {
|
||||
return descriptor->FrameworkSupported == FrameworkCompatible::Yes;
|
||||
return descriptor->Attributes;
|
||||
}
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
bool IsFrameworkSupported(cm::string_view type)
|
||||
{
|
||||
return GetAttributes(type).contains(FileSetAttributes::FrameworkCompatible);
|
||||
}
|
||||
|
||||
std::vector<cm::string_view> const& GetKnownTypes()
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
#include <cmext/enum_set>
|
||||
|
||||
class cmMakefile;
|
||||
|
||||
@@ -48,27 +50,28 @@ enum class FileSetLookup
|
||||
enum class DependencyMode
|
||||
{
|
||||
IndependentFiles, // files in the file set are independent from each other
|
||||
Includables, // files can be used by another source during compilation
|
||||
Includables // files can be used by another source during compilation
|
||||
};
|
||||
using DependencySet = std::set<DependencyMode>;
|
||||
|
||||
enum class FrameworkCompatible
|
||||
enum class FileSetAttributes : std::uint16_t
|
||||
{
|
||||
No,
|
||||
Yes
|
||||
FrameworkCompatible, // Can be part of an Apple framework
|
||||
FilesInMultipleFileSets // Files of this file set type can be part of other
|
||||
// file sets
|
||||
};
|
||||
using AttributeSet = cm::enum_set<FileSetAttributes, 2>;
|
||||
|
||||
struct FileSetDescriptor
|
||||
{
|
||||
FileSetDescriptor(cm::string_view type, FileSetLookup lookup,
|
||||
DependencySet dependencies,
|
||||
DependencyMode defaultDependency,
|
||||
FrameworkCompatible frameworkSupported)
|
||||
DependencyMode defaultDependency, AttributeSet attributes)
|
||||
: Type(type)
|
||||
, Lookup(lookup)
|
||||
, SupportedDependencies(std::move(dependencies))
|
||||
, DefaultDependency(defaultDependency)
|
||||
, FrameworkSupported(frameworkSupported)
|
||||
, Attributes(attributes)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -77,7 +80,6 @@ struct FileSetDescriptor
|
||||
, Lookup(lookup)
|
||||
, SupportedDependencies({ DependencyMode::Includables })
|
||||
, DefaultDependency(DependencyMode::Includables)
|
||||
, FrameworkSupported(FrameworkCompatible::No)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -85,13 +87,15 @@ struct FileSetDescriptor
|
||||
FileSetLookup const Lookup;
|
||||
DependencySet const SupportedDependencies;
|
||||
DependencyMode const DefaultDependency;
|
||||
FrameworkCompatible const FrameworkSupported;
|
||||
AttributeSet const Attributes;
|
||||
};
|
||||
|
||||
cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type);
|
||||
DependencyMode GetDependencyMode(cm::string_view type);
|
||||
DependencyMode GetDependencyMode(cm::string_view type,
|
||||
DependencyMode requestedMode);
|
||||
|
||||
AttributeSet GetAttributes(cm::string_view type);
|
||||
bool IsFrameworkSupported(cm::string_view type);
|
||||
|
||||
std::vector<cm::string_view> const& GetKnownTypes();
|
||||
@@ -101,3 +105,5 @@ bool IsKnownType(cm::string_view type);
|
||||
bool IsValidName(cm::string_view type);
|
||||
}
|
||||
}
|
||||
|
||||
CM_ENUM_SET_TRAITS(cm::FileSetMetadata::AttributeSet)
|
||||
|
||||
@@ -133,11 +133,10 @@ cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSet(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
|
||||
std::string const& config, std::string const& path) const
|
||||
std::unordered_set<cmGeneratorFileSet const*> const&
|
||||
cmGeneratorFileSets::GetAllFileSetsForSource(std::string const& config,
|
||||
std::string const& path) const
|
||||
{
|
||||
using Lookup = cm::FileSetMetadata::FileSetLookup;
|
||||
|
||||
this->BuildInfoCache(config);
|
||||
|
||||
auto const& info = this->Configs[config];
|
||||
@@ -149,14 +148,28 @@ cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
|
||||
|
||||
// search in all the dependents
|
||||
auto const it2 = info.InterfaceFileSetCache.find(path);
|
||||
if (it2 != info.InterfaceFileSetCache.end() &&
|
||||
cm::FileSetMetadata::GetFileSetDescriptor(it2->second->GetType())
|
||||
.value_or(cm::FileSetMetadata::FileSetDescriptor{ Lookup::Target })
|
||||
.Lookup == Lookup::Dependencies) {
|
||||
if (it2 != info.InterfaceFileSetCache.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
static std::unordered_set<cmGeneratorFileSet const*> emptySet;
|
||||
return emptySet;
|
||||
}
|
||||
std::unordered_set<cmGeneratorFileSet const*> const&
|
||||
cmGeneratorFileSets::GetAllFileSetsForSource(std::string const& config,
|
||||
cmSourceFile const* sf) const
|
||||
{
|
||||
return this->GetAllFileSetsForSource(config, sf->GetFullPath());
|
||||
}
|
||||
|
||||
cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
|
||||
std::string const& config, std::string const& file) const
|
||||
{
|
||||
auto const& fileSets = this->GetAllFileSetsForSource(config, file);
|
||||
if (fileSets.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return *fileSets.begin();
|
||||
}
|
||||
cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
|
||||
std::string const& config, cmSourceFile const* sf) const
|
||||
@@ -358,10 +371,10 @@ std::string cmGeneratorFileSets::EvaluateInterfaceProperty(
|
||||
}
|
||||
|
||||
namespace {
|
||||
void GetInterfaceFiles(cmGeneratorTarget const* target,
|
||||
cm::GenEx::Context const& context,
|
||||
std::unordered_set<cmGeneratorTarget const*>& targets,
|
||||
std::map<std::string, cmGeneratorFileSet const*>& cache)
|
||||
void GetInterfaceFiles(
|
||||
cmGeneratorTarget const* target, cm::GenEx::Context const& context,
|
||||
std::unordered_set<cmGeneratorTarget const*>& targets,
|
||||
std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>>& cache)
|
||||
{
|
||||
namespace Metadata = cm::FileSetMetadata;
|
||||
|
||||
@@ -375,7 +388,7 @@ void GetInterfaceFiles(cmGeneratorTarget const* target,
|
||||
for (auto const& it : files.first) {
|
||||
for (auto const& filename : it.second) {
|
||||
auto collapsedFile = cmSystemTools::CollapseFullPath(filename);
|
||||
cache[collapsedFile] = fileSet;
|
||||
cache[collapsedFile].insert(fileSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,7 +426,7 @@ void cmGeneratorFileSets::BuildInfoCache(std::string const& config) const
|
||||
for (auto const& it : files.first) {
|
||||
for (auto const& filename : it.second) {
|
||||
auto collapsedFile = cmSystemTools::CollapseFullPath(filename);
|
||||
info.FileSetCache[collapsedFile] = fileSet;
|
||||
info.FileSetCache[collapsedFile].insert(fileSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/string_view>
|
||||
@@ -53,6 +54,12 @@ public:
|
||||
|
||||
cmGeneratorFileSet const* GetFileSet(std::string const& name) const;
|
||||
|
||||
std::unordered_set<cmGeneratorFileSet const*> const& GetAllFileSetsForSource(
|
||||
std::string const& config, std::string const& file) const;
|
||||
std::unordered_set<cmGeneratorFileSet const*> const& GetAllFileSetsForSource(
|
||||
std::string const& config, cmSourceFile const* sf) const;
|
||||
|
||||
// returns the first FileSet of the set
|
||||
cmGeneratorFileSet const* GetFileSetForSource(std::string const& config,
|
||||
std::string const& file) const;
|
||||
cmGeneratorFileSet const* GetFileSetForSource(std::string const& config,
|
||||
@@ -102,8 +109,10 @@ private:
|
||||
struct InfoByConfig
|
||||
{
|
||||
bool BuiltCache = false;
|
||||
std::map<std::string, cmGeneratorFileSet const*> FileSetCache;
|
||||
std::map<std::string, cmGeneratorFileSet const*> InterfaceFileSetCache;
|
||||
std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>>
|
||||
FileSetCache;
|
||||
std::map<std::string, std::unordered_set<cmGeneratorFileSet const*>>
|
||||
InterfaceFileSetCache;
|
||||
};
|
||||
mutable std::map<std::string, InfoByConfig> Configs;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "cmConfigure.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -160,8 +161,22 @@ bool processSources(cmGeneratorTarget const* tgt, std::string const& config,
|
||||
usedSources += cmStrCat(" * ", src, '\n');
|
||||
}
|
||||
} else {
|
||||
if (auto const* fileSet =
|
||||
tgt->GetGeneratorFileSets()->GetFileSetForSource(config, src)) {
|
||||
auto const& fileSets =
|
||||
tgt->GetGeneratorFileSets()->GetAllFileSetsForSource(config, src);
|
||||
if (fileSets.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto fileMustBeUnique = [&fileSets]() -> bool {
|
||||
return std::none_of(
|
||||
fileSets.begin(), fileSets.end(),
|
||||
[](cmGeneratorFileSet const* fileSet) {
|
||||
return cm::FileSetMetadata::GetAttributes(fileSet->GetType())
|
||||
.contains(cm::FileSetMetadata::FileSetAttributes::
|
||||
FilesInMultipleFileSets);
|
||||
});
|
||||
};
|
||||
if (fileMustBeUnique()) {
|
||||
auto const* fileSet = *fileSets.begin();
|
||||
switch (tgt->GetPolicyStatusCMP0211()) {
|
||||
case cmPolicies::WARN:
|
||||
tgt->GetLocalGenerator()->IssueDiagnostic(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(foo1 STATIC lib1.c)
|
||||
target_sources(foo1 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h)
|
||||
target_sources(foo1 PRIVATE FILE_SET h2 TYPE HEADERS FILES h1.h)
|
||||
target_sources(foo1 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c)
|
||||
target_sources(foo1 PRIVATE FILE_SET s2 TYPE SOURCES FILES lib2.c)
|
||||
|
||||
add_library(foo2 STATIC lib1.c h1.h)
|
||||
target_sources(foo2 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h)
|
||||
add_library(foo2 STATIC lib1.c lib2.c)
|
||||
target_sources(foo2 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c)
|
||||
|
||||
13
Tests/RunCMake/target_sources/CMP0211-NEW-HEADERS.cmake
Normal file
13
Tests/RunCMake/target_sources/CMP0211-NEW-HEADERS.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
cmake_policy(SET CMP0211 NEW)
|
||||
|
||||
enable_language(C)
|
||||
|
||||
# files in HEADERS file sets can also be specified in other file sets
|
||||
|
||||
add_library(foo1 STATIC lib1.c)
|
||||
target_sources(foo1 PRIVATE FILE_SET s1 TYPE SOURCES FILES lib2.c h1.h)
|
||||
target_sources(foo1 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h)
|
||||
|
||||
add_library(foo2 STATIC lib1.c h1.h)
|
||||
target_sources(foo2 PRIVATE FILE_SET h1 TYPE HEADERS FILES h1.h)
|
||||
@@ -1,14 +1,14 @@
|
||||
CMake Error in CMakeLists\.txt:
|
||||
In target "foo1" the file
|
||||
|
||||
.+/Tests/RunCMake/target_sources/h1\.h
|
||||
.+/Tests/RunCMake/target_sources/lib2\.c
|
||||
|
||||
already belongs to file set "h2"\.
|
||||
already belongs to file set "s[12]"\.
|
||||
|
||||
|
||||
CMake Error in CMakeLists\.txt:
|
||||
In target "foo2" the file
|
||||
|
||||
.+/Tests/RunCMake/target_sources/h1\.h
|
||||
.+/Tests/RunCMake/target_sources/lib2\.c
|
||||
|
||||
already belongs to file set "h1"\.
|
||||
already belongs to file set "s1"\.
|
||||
|
||||
@@ -5,9 +5,9 @@ CMake Warning \(author\) in CMakeLists\.txt:
|
||||
|
||||
In target "foo1" the file
|
||||
|
||||
.+/Tests/RunCMake/target_sources/h1\.h
|
||||
.+/Tests/RunCMake/target_sources/lib2\.c
|
||||
|
||||
already belongs to file set "h2"\.
|
||||
already belongs to file set "s[12]"\.
|
||||
This warning is for project developers\. Use -Wno-author to suppress it\.
|
||||
|
||||
CMake Warning \(author\) in CMakeLists\.txt:
|
||||
@@ -17,7 +17,7 @@ CMake Warning \(author\) in CMakeLists\.txt:
|
||||
|
||||
In target "foo2" the file
|
||||
|
||||
.+/Tests/RunCMake/target_sources/h1\.h
|
||||
.+/Tests/RunCMake/target_sources/lib2\.c
|
||||
|
||||
already belongs to file set "h1"\.
|
||||
already belongs to file set "s1"\.
|
||||
This warning is for project developers\. Use -Wno-author to suppress it\.
|
||||
|
||||
@@ -52,6 +52,7 @@ endif()
|
||||
run_cmake(CMP0211-OLD)
|
||||
run_cmake(CMP0211-NEW)
|
||||
run_cmake(CMP0211-WARN)
|
||||
run_cmake(CMP0211-NEW-HEADERS)
|
||||
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/FileSetGeneratedDependency-build")
|
||||
|
||||
Reference in New Issue
Block a user