From 2dda1f21ab91314f5ccb739802448dfe4d39f461 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Fri, 10 Apr 2026 15:37:44 +0200 Subject: [PATCH] FILE_SET: enhance FRAMEWORK compatibility checks The SOURCES file set type is compatible with the FRAMEWORK target. Add check at generation time. Fixes: #27705 --- Help/command/target_sources.rst | 3 +- Source/cmFileSetMetadata.cxx | 18 +++++-- Source/cmFileSetMetadata.h | 13 ++++- Source/cmGeneratorFileSets.cxx | 54 +++++++++++++------ Source/cmTargetSourcesCommand.cxx | 11 ++-- .../FileSetFramework1-result.txt} | 0 .../FileSetFramework1-stderr.txt | 3 ++ .../FileSet-SOURCES/FileSetFramework1.cmake | 8 +++ .../FileSet-SOURCES/FileSetFramework2.cmake | 7 +++ .../FileSet-SOURCES/RunCMakeTest.cmake | 4 ++ .../FileSetFramework-stderr.txt | 4 -- .../FileSetFramework1-result.txt | 1 + .../FileSetFramework1-stderr.txt | 5 ++ ...ramework.cmake => FileSetFramework1.cmake} | 0 .../FileSetFramework2-result.txt | 1 + .../FileSetFramework2-stderr.txt | 3 ++ .../target_sources/FileSetFramework2.cmake | 7 +++ .../target_sources/RunCMakeTest.cmake | 3 +- 18 files changed, 114 insertions(+), 31 deletions(-) rename Tests/RunCMake/{target_sources/FileSetFramework-result.txt => FileSet-SOURCES/FileSetFramework1-result.txt} (100%) create mode 100644 Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt create mode 100644 Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake create mode 100644 Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake delete mode 100644 Tests/RunCMake/target_sources/FileSetFramework-stderr.txt create mode 100644 Tests/RunCMake/target_sources/FileSetFramework1-result.txt create mode 100644 Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt rename Tests/RunCMake/target_sources/{FileSetFramework.cmake => FileSetFramework1.cmake} (100%) create mode 100644 Tests/RunCMake/target_sources/FileSetFramework2-result.txt create mode 100644 Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt create mode 100644 Tests/RunCMake/target_sources/FileSetFramework2.cmake diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index c95b95d8da..374d828a52 100644 --- a/Help/command/target_sources.rst +++ b/Help/command/target_sources.rst @@ -109,7 +109,8 @@ files within those directories. ``INTERFACE`` scope except on ``IMPORTED`` targets. 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 the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets diff --git a/Source/cmFileSetMetadata.cxx b/Source/cmFileSetMetadata.cxx index 5d98e66f5a..821a969b15 100644 --- a/Source/cmFileSetMetadata.cxx +++ b/Source/cmFileSetMetadata.cxx @@ -85,17 +85,20 @@ std::map const FileSetDescriptors{ { cm::FileSetMetadata::HEADERS, cm::FileSetMetadata::FileSetLookup::Target, { DependencyMode ::Includables }, - DependencyMode ::Includables } }, + DependencyMode ::Includables, + FrameworkCompatible::No } }, { cm::FileSetMetadata::SOURCES, { cm::FileSetMetadata::SOURCES, cm::FileSetMetadata::FileSetLookup::Dependencies, { DependencyMode ::IndependentFiles, DependencyMode ::Includables }, - DependencyMode ::Includables } }, + DependencyMode ::Includables, + FrameworkCompatible::Yes } }, { cm::FileSetMetadata::CXX_MODULES, { cm::FileSetMetadata::CXX_MODULES, cm::FileSetMetadata::FileSetLookup::Target, { DependencyMode ::IndependentFiles }, - DependencyMode ::IndependentFiles } }, + DependencyMode ::IndependentFiles, + FrameworkCompatible::No } }, }; std::vector KnownTypes{ HEADERS, SOURCES, CXX_MODULES }; @@ -135,6 +138,15 @@ DependencyMode GetDependencyMode(cm::string_view type, return DependencyMode::Includables; } +bool IsFrameworkSupported(cm::string_view type) +{ + auto descriptor = GetFileSetDescriptor(type); + if (descriptor) { + return descriptor->FrameworkSupported == FrameworkCompatible::Yes; + } + return false; +} + std::vector const& GetKnownTypes() { return KnownTypes; diff --git a/Source/cmFileSetMetadata.h b/Source/cmFileSetMetadata.h index 9288e87a6c..9a28dd297b 100644 --- a/Source/cmFileSetMetadata.h +++ b/Source/cmFileSetMetadata.h @@ -52,15 +52,23 @@ enum class DependencyMode }; using DependencySet = std::set; +enum class FrameworkCompatible +{ + No, + Yes +}; + struct FileSetDescriptor { FileSetDescriptor(cm::string_view type, FileSetLookup lookup, DependencySet dependencies, - DependencyMode defaultDependency) + DependencyMode defaultDependency, + FrameworkCompatible frameworkSupported) : Type(type) , Lookup(lookup) , SupportedDependencies(std::move(dependencies)) , DefaultDependency(defaultDependency) + , FrameworkSupported(frameworkSupported) { } @@ -69,6 +77,7 @@ struct FileSetDescriptor , Lookup(lookup) , SupportedDependencies({ DependencyMode::Includables }) , DefaultDependency(DependencyMode::Includables) + , FrameworkSupported(FrameworkCompatible::No) { } @@ -76,12 +85,14 @@ struct FileSetDescriptor FileSetLookup const Lookup; DependencySet const SupportedDependencies; DependencyMode const DefaultDependency; + FrameworkCompatible const FrameworkSupported; }; cm::optional GetFileSetDescriptor(cm::string_view type); DependencyMode GetDependencyMode(cm::string_view type); DependencyMode GetDependencyMode(cm::string_view type, DependencyMode requestedMode); +bool IsFrameworkSupported(cm::string_view type); std::vector const& GetKnownTypes(); bool IsKnownType(cm::string_view type); diff --git a/Source/cmGeneratorFileSets.cxx b/Source/cmGeneratorFileSets.cxx index 14b6adfcb9..00917514a4 100644 --- a/Source/cmGeneratorFileSets.cxx +++ b/Source/cmGeneratorFileSets.cxx @@ -13,6 +13,7 @@ #include #include +#include "cmFileSet.h" #include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGenExEvaluation.h" @@ -24,6 +25,8 @@ #include "cmLinkItem.h" #include "cmList.h" #include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmMessageType.h" #include "cmSourceFile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -35,30 +38,47 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target, : Target(target) , LocalGenerator(lg) { - for (auto const& name : target->Target->GetAllPrivateFileSets()) { - auto entry = - this->FileSets.emplace(name, - cm::make_unique( - target, target->Target->GetFileSet(name))); + bool isFramework = target->IsFrameworkOnApple(); + auto issueMessage = [&target](cmFileSet const* fileSet) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + 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(); - this->AllFileSets.push_back(fileSet); - this->SelfFileSets[fileSet->GetType()].push_back(fileSet); + for (auto const& name : target->Target->GetAllPrivateFileSets()) { + cmFileSet const* fileSet = target->Target->GetFileSet(name); + if (isFramework && + !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { + issueMessage(fileSet); + continue; + } + auto entry = this->FileSets.emplace( + name, cm::make_unique(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()) { auto it = this->FileSets.find(name); - cmGeneratorFileSet const* fileSet = nullptr; + cmGeneratorFileSet const* genFileSet = nullptr; if (it == this->FileSets.end()) { - auto entry = - this->FileSets.emplace(name, - cm::make_unique( - target, target->Target->GetFileSet(name))); - fileSet = entry.first->second.get(); - this->AllFileSets.push_back(fileSet); + cmFileSet const* fileSet = target->Target->GetFileSet(name); + if (isFramework && + !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { + issueMessage(fileSet); + continue; + } + auto entry = this->FileSets.emplace( + name, cm::make_unique(target, fileSet)); + genFileSet = entry.first->second.get(); + this->AllFileSets.push_back(genFileSet); } 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; diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index 7a0a58e8cd..1dd3197c64 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -228,10 +228,6 @@ bool TargetSourcesImpl::HandleOneFileSet( this->SetError("FILE_SETs may not be added to custom targets"); 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)) { this->SetError( @@ -269,6 +265,13 @@ bool TargetSourcesImpl::HandleOneFileSet( cm::FileSetMetadata::Visibility visibility = 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 = this->Target->GetOrCreateFileSet(args.FileSet, type, visibility); if (fileSet.second) { diff --git a/Tests/RunCMake/target_sources/FileSetFramework-result.txt b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-result.txt similarity index 100% rename from Tests/RunCMake/target_sources/FileSetFramework-result.txt rename to Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-result.txt diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt new file mode 100644 index 0000000000..5c54059e55 --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists\.txt: + The file set "HEADERS", of type "HEADERS", is incompatible with the + "FRAMEWORK" target "lib1"\. diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake new file mode 100644 index 0000000000..7c166a0aea --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework1.cmake @@ -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) diff --git a/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake new file mode 100644 index 0000000000..f57738116f --- /dev/null +++ b/Tests/RunCMake/FileSet-SOURCES/FileSetFramework2.cmake @@ -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) diff --git a/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake b/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake index c04bd29a71..2e2c2655cc 100644 --- a/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake +++ b/Tests/RunCMake/FileSet-SOURCES/RunCMakeTest.cmake @@ -34,6 +34,10 @@ run_and_build(CustomCommandInput) run_and_build(IncludeDirectoriesOrder) run_and_build(FileSetTransitivity) run_and_check(CompileOptionsOrder) +if(APPLE) + run_cmake(FileSetFramework1) + run_and_build(FileSetFramework2) +endif() if (RunCMake_GENERATOR MATCHES "Ninja") run_cmake(IndependentFilesWarning) if (RunCMake_GENERATOR_IS_MULTI_CONFIG) diff --git a/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt deleted file mode 100644 index ae7026a0e0..0000000000 --- a/Tests/RunCMake/target_sources/FileSetFramework-stderr.txt +++ /dev/null @@ -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\)$ diff --git a/Tests/RunCMake/target_sources/FileSetFramework1-result.txt b/Tests/RunCMake/target_sources/FileSetFramework1-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt new file mode 100644 index 0000000000..5a2e65a595 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt @@ -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\)$ diff --git a/Tests/RunCMake/target_sources/FileSetFramework.cmake b/Tests/RunCMake/target_sources/FileSetFramework1.cmake similarity index 100% rename from Tests/RunCMake/target_sources/FileSetFramework.cmake rename to Tests/RunCMake/target_sources/FileSetFramework1.cmake diff --git a/Tests/RunCMake/target_sources/FileSetFramework2-result.txt b/Tests/RunCMake/target_sources/FileSetFramework2-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt new file mode 100644 index 0000000000..5c54059e55 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework2-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists\.txt: + The file set "HEADERS", of type "HEADERS", is incompatible with the + "FRAMEWORK" target "lib1"\. diff --git a/Tests/RunCMake/target_sources/FileSetFramework2.cmake b/Tests/RunCMake/target_sources/FileSetFramework2.cmake new file mode 100644 index 0000000000..6d89d41a6e --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetFramework2.cmake @@ -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) diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake index 9226c67a7c..ff89e3c552 100644 --- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake @@ -46,7 +46,8 @@ run_cmake(FileSetWrongSyntax) run_cmake(FileSetDirect) run_cmake(FileSetDuplicateSource) if(APPLE) - run_cmake(FileSetFramework) + run_cmake(FileSetFramework1) + run_cmake(FileSetFramework2) endif() run_cmake(CMP0211-OLD) run_cmake(CMP0211-NEW)