FILE_SET: enhance FRAMEWORK compatibility checks

The SOURCES file set type is compatible with the FRAMEWORK target.
Add check at generation time.

Fixes: #27705
This commit is contained in:
Marc Chevrier
2026-04-10 15:37:44 +02:00
parent e012f62599
commit 2dda1f21ab
18 changed files with 114 additions and 31 deletions

View File

@@ -109,7 +109,8 @@ files within those directories.
``INTERFACE`` scope except on ``IMPORTED`` targets. ``INTERFACE`` scope except on ``IMPORTED`` targets.
The optional default file sets are named after their type. The target may not The optional default file sets are named after their type. The target may not
be a custom target or :prop_tgt:`FRAMEWORK` target. be a custom target or, for ``HEADERS`` and ``CXX_MODULES`` types, a
:prop_tgt:`FRAMEWORK` target.
Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for
the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets

View File

@@ -85,17 +85,20 @@ 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 },
DependencyMode ::Includables } }, DependencyMode ::Includables,
FrameworkCompatible::No } },
{ cm::FileSetMetadata::SOURCES, { cm::FileSetMetadata::SOURCES,
{ cm::FileSetMetadata::SOURCES, { cm::FileSetMetadata::SOURCES,
cm::FileSetMetadata::FileSetLookup::Dependencies, cm::FileSetMetadata::FileSetLookup::Dependencies,
{ DependencyMode ::IndependentFiles, DependencyMode ::Includables }, { DependencyMode ::IndependentFiles, DependencyMode ::Includables },
DependencyMode ::Includables } }, DependencyMode ::Includables,
FrameworkCompatible::Yes } },
{ cm::FileSetMetadata::CXX_MODULES, { cm::FileSetMetadata::CXX_MODULES,
{ cm::FileSetMetadata::CXX_MODULES, { cm::FileSetMetadata::CXX_MODULES,
cm::FileSetMetadata::FileSetLookup::Target, cm::FileSetMetadata::FileSetLookup::Target,
{ DependencyMode ::IndependentFiles }, { DependencyMode ::IndependentFiles },
DependencyMode ::IndependentFiles } }, DependencyMode ::IndependentFiles,
FrameworkCompatible::No } },
}; };
std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES }; std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES };
@@ -135,6 +138,15 @@ DependencyMode GetDependencyMode(cm::string_view type,
return DependencyMode::Includables; return DependencyMode::Includables;
} }
bool IsFrameworkSupported(cm::string_view type)
{
auto descriptor = GetFileSetDescriptor(type);
if (descriptor) {
return descriptor->FrameworkSupported == FrameworkCompatible::Yes;
}
return false;
}
std::vector<cm::string_view> const& GetKnownTypes() std::vector<cm::string_view> const& GetKnownTypes()
{ {
return KnownTypes; return KnownTypes;

View File

@@ -52,15 +52,23 @@ enum class DependencyMode
}; };
using DependencySet = std::set<DependencyMode>; using DependencySet = std::set<DependencyMode>;
enum class FrameworkCompatible
{
No,
Yes
};
struct FileSetDescriptor struct FileSetDescriptor
{ {
FileSetDescriptor(cm::string_view type, FileSetLookup lookup, FileSetDescriptor(cm::string_view type, FileSetLookup lookup,
DependencySet dependencies, DependencySet dependencies,
DependencyMode defaultDependency) DependencyMode defaultDependency,
FrameworkCompatible frameworkSupported)
: Type(type) : Type(type)
, Lookup(lookup) , Lookup(lookup)
, SupportedDependencies(std::move(dependencies)) , SupportedDependencies(std::move(dependencies))
, DefaultDependency(defaultDependency) , DefaultDependency(defaultDependency)
, FrameworkSupported(frameworkSupported)
{ {
} }
@@ -69,6 +77,7 @@ struct FileSetDescriptor
, Lookup(lookup) , Lookup(lookup)
, SupportedDependencies({ DependencyMode::Includables }) , SupportedDependencies({ DependencyMode::Includables })
, DefaultDependency(DependencyMode::Includables) , DefaultDependency(DependencyMode::Includables)
, FrameworkSupported(FrameworkCompatible::No)
{ {
} }
@@ -76,12 +85,14 @@ struct FileSetDescriptor
FileSetLookup const Lookup; FileSetLookup const Lookup;
DependencySet const SupportedDependencies; DependencySet const SupportedDependencies;
DependencyMode const DefaultDependency; DependencyMode const DefaultDependency;
FrameworkCompatible const FrameworkSupported;
}; };
cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type); cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type);
DependencyMode GetDependencyMode(cm::string_view type); DependencyMode GetDependencyMode(cm::string_view type);
DependencyMode GetDependencyMode(cm::string_view type, DependencyMode GetDependencyMode(cm::string_view type,
DependencyMode requestedMode); DependencyMode requestedMode);
bool IsFrameworkSupported(cm::string_view type);
std::vector<cm::string_view> const& GetKnownTypes(); std::vector<cm::string_view> const& GetKnownTypes();
bool IsKnownType(cm::string_view type); bool IsKnownType(cm::string_view type);

View File

@@ -13,6 +13,7 @@
#include <cm/optional> #include <cm/optional>
#include <cmext/algorithm> #include <cmext/algorithm>
#include "cmFileSet.h"
#include "cmFileSetMetadata.h" #include "cmFileSetMetadata.h"
#include "cmGenExContext.h" #include "cmGenExContext.h"
#include "cmGenExEvaluation.h" #include "cmGenExEvaluation.h"
@@ -24,6 +25,8 @@
#include "cmLinkItem.h" #include "cmLinkItem.h"
#include "cmList.h" #include "cmList.h"
#include "cmListFileCache.h" #include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSourceFile.h" #include "cmSourceFile.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
@@ -35,30 +38,47 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target,
: Target(target) : Target(target)
, LocalGenerator(lg) , LocalGenerator(lg)
{ {
for (auto const& name : target->Target->GetAllPrivateFileSets()) { bool isFramework = target->IsFrameworkOnApple();
auto entry = auto issueMessage = [&target](cmFileSet const* fileSet) {
this->FileSets.emplace(name, target->Makefile->IssueMessage(
cm::make_unique<cmGeneratorFileSet>( MessageType::FATAL_ERROR,
target, target->Target->GetFileSet(name))); cmStrCat(R"(The file set ")", fileSet->GetName(), R"(", of type ")",
fileSet->GetType(),
R"(", is incompatible with the "FRAMEWORK" target ")",
target->GetName(), R"(".)"));
};
auto const* fileSet = entry.first->second.get(); for (auto const& name : target->Target->GetAllPrivateFileSets()) {
this->AllFileSets.push_back(fileSet); cmFileSet const* fileSet = target->Target->GetFileSet(name);
this->SelfFileSets[fileSet->GetType()].push_back(fileSet); if (isFramework &&
!cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) {
issueMessage(fileSet);
continue;
}
auto entry = this->FileSets.emplace(
name, cm::make_unique<cmGeneratorFileSet>(target, fileSet));
auto const* genFileSet = entry.first->second.get();
this->AllFileSets.push_back(genFileSet);
this->SelfFileSets[genFileSet->GetType()].push_back(genFileSet);
} }
for (auto const& name : target->Target->GetAllInterfaceFileSets()) { for (auto const& name : target->Target->GetAllInterfaceFileSets()) {
auto it = this->FileSets.find(name); auto it = this->FileSets.find(name);
cmGeneratorFileSet const* fileSet = nullptr; cmGeneratorFileSet const* genFileSet = nullptr;
if (it == this->FileSets.end()) { if (it == this->FileSets.end()) {
auto entry = cmFileSet const* fileSet = target->Target->GetFileSet(name);
this->FileSets.emplace(name, if (isFramework &&
cm::make_unique<cmGeneratorFileSet>( !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) {
target, target->Target->GetFileSet(name))); issueMessage(fileSet);
fileSet = entry.first->second.get(); continue;
this->AllFileSets.push_back(fileSet); }
auto entry = this->FileSets.emplace(
name, cm::make_unique<cmGeneratorFileSet>(target, fileSet));
genFileSet = entry.first->second.get();
this->AllFileSets.push_back(genFileSet);
} else { } else {
fileSet = it->second.get(); genFileSet = it->second.get();
} }
this->InterfaceFileSets[fileSet->GetType()].push_back(fileSet); this->InterfaceFileSets[genFileSet->GetType()].push_back(genFileSet);
} }
} }
cmGeneratorFileSets::~cmGeneratorFileSets() = default; cmGeneratorFileSets::~cmGeneratorFileSets() = default;

View File

@@ -228,10 +228,6 @@ bool TargetSourcesImpl::HandleOneFileSet(
this->SetError("FILE_SETs may not be added to custom targets"); this->SetError("FILE_SETs may not be added to custom targets");
return false; return false;
} }
if (this->Target->IsFrameworkOnApple()) {
this->SetError("FILE_SETs may not be added to FRAMEWORK targets");
return false;
}
if (!args.Type.empty() && !cm::FileSetMetadata::IsKnownType(args.Type)) { if (!args.Type.empty() && !cm::FileSetMetadata::IsKnownType(args.Type)) {
this->SetError( this->SetError(
@@ -269,6 +265,13 @@ bool TargetSourcesImpl::HandleOneFileSet(
cm::FileSetMetadata::Visibility visibility = cm::FileSetMetadata::Visibility visibility =
cm::FileSetMetadata::VisibilityFromName(scope, this->Makefile); cm::FileSetMetadata::VisibilityFromName(scope, this->Makefile);
if (this->Target->IsFrameworkOnApple() &&
!cm::FileSetMetadata::IsFrameworkSupported(type)) {
this->SetError(cmStrCat(R"(FILE_SETs, of type ")", type,
R"(", may not be added to FRAMEWORK targets)"));
return false;
}
auto fileSet = auto fileSet =
this->Target->GetOrCreateFileSet(args.FileSet, type, visibility); this->Target->GetOrCreateFileSet(args.FileSet, type, visibility);
if (fileSet.second) { if (fileSet.second) {

View File

@@ -0,0 +1,3 @@
CMake Error in CMakeLists\.txt:
The file set "HEADERS", of type "HEADERS", is incompatible with the
"FRAMEWORK" target "lib1"\.

View File

@@ -0,0 +1,8 @@
enable_language(C)
add_library(lib1 SHARED)
target_sources(lib1
PUBLIC FILE_SET SOURCES FILES lib1.c
FILE_SET HEADERS FILES lib1.h
)
set_property(TARGET lib1 PROPERTY FRAMEWORK ON)

View File

@@ -0,0 +1,7 @@
enable_language(C)
add_library(lib1 SHARED)
target_sources(lib1
PUBLIC FILE_SET SOURCES FILES lib1.c
)
set_property(TARGET lib1 PROPERTY FRAMEWORK ON)

View File

@@ -34,6 +34,10 @@ run_and_build(CustomCommandInput)
run_and_build(IncludeDirectoriesOrder) run_and_build(IncludeDirectoriesOrder)
run_and_build(FileSetTransitivity) run_and_build(FileSetTransitivity)
run_and_check(CompileOptionsOrder) run_and_check(CompileOptionsOrder)
if(APPLE)
run_cmake(FileSetFramework1)
run_and_build(FileSetFramework2)
endif()
if (RunCMake_GENERATOR MATCHES "Ninja") if (RunCMake_GENERATOR MATCHES "Ninja")
run_cmake(IndependentFilesWarning) run_cmake(IndependentFilesWarning)
if (RunCMake_GENERATOR_IS_MULTI_CONFIG) if (RunCMake_GENERATOR_IS_MULTI_CONFIG)

View File

@@ -1,4 +0,0 @@
^CMake Error at FileSetFramework\.cmake:[0-9]+ \(target_sources\):
target_sources FILE_SETs may not be added to FRAMEWORK targets
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
CMake Error at FileSetFramework1\.cmake:[0-9]+ \(target_sources\):
target_sources FILE_SETs, of type "HEADERS", may not be added to FRAMEWORK
targets
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,3 @@
CMake Error in CMakeLists\.txt:
The file set "HEADERS", of type "HEADERS", is incompatible with the
"FRAMEWORK" target "lib1"\.

View File

@@ -0,0 +1,7 @@
enable_language(C)
add_library(lib1 SHARED lib1.c)
target_sources(lib1
PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES h1.h
)
set_property(TARGET lib1 PROPERTY FRAMEWORK ON)

View File

@@ -46,7 +46,8 @@ run_cmake(FileSetWrongSyntax)
run_cmake(FileSetDirect) run_cmake(FileSetDirect)
run_cmake(FileSetDuplicateSource) run_cmake(FileSetDuplicateSource)
if(APPLE) if(APPLE)
run_cmake(FileSetFramework) run_cmake(FileSetFramework1)
run_cmake(FileSetFramework2)
endif() endif()
run_cmake(CMP0211-OLD) run_cmake(CMP0211-OLD)
run_cmake(CMP0211-NEW) run_cmake(CMP0211-NEW)