FILE_SET: add the support of properties for file set inspection

This commit is contained in:
Marc Chevrier
2026-02-28 13:46:57 +01:00
parent 9696a44cd3
commit 63775fe60f
25 changed files with 533 additions and 28 deletions
+14
View File
@@ -525,6 +525,20 @@ Properties on Targets
/prop_tgt/XCODE_XCCONFIG
/prop_tgt/XCTEST
.. _`File Set Properties`:
Properties on File Sets
=======================
.. toctree::
:maxdepth: 1
/prop_fs/BASE_DIRS
/prop_fs/INTERFACE_SOURCES
/prop_fs/SCOPE
/prop_fs/SOURCES
/prop_fs/TYPE
.. _`Test Properties`:
Properties on Tests
+13
View File
@@ -0,0 +1,13 @@
BASE_DIRS
---------
List of base directories of the file set. The :command:`target_sources` command
sets or adds to the ``BASE_DIRS`` file set property and is the usual way to
manipulate it.
Contents of ``BASE_DIRS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
Any relative paths are considered relative to the target's source directory. No
two base directories for a file set may be sub-directories of each other. This
requirement must be met across all base directories added to a file set.
+32
View File
@@ -0,0 +1,32 @@
INTERFACE_SOURCES
-----------------
List of interface sources to compile into consuming targets.
The :command:`target_sources` command sets or adds to the ``INTERFACE_SOURCES``
file set property for the file sets defined with the ``PUBLIC`` or
``INTERFACE`` keyword and is the usual way to manipulate it.
Contents of ``INTERFACE_SOURCES`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
Each file must be in one of the base directories, or a subdirectory of one of
the base directories.
If relative paths are specified, they are considered relative to the target's
source directory.
The following behavior applies for the :prop_fs:`SOURCES` and
``INTERFACE_SOURCES`` file set properties, dependent on the value of the
:prop_fs:`SCOPE` file set property:
``PRIVATE``
Only the :prop_fs:`SOURCES` property can be set. Any change to the
``INTERFACE_SOURCES`` property will be ignored.
``PUBLIC``
:prop_fs:`SOURCES` and ``INTERFACE_SOURCES`` properties will always have the
same content.
``INTERFACE``
Only the ``INTERFACE_SOURCES`` property can be set. Any change to the
:prop_fs:`SOURCES` property will be ignored.
+8
View File
@@ -0,0 +1,8 @@
SCOPE
-----
The scope of the file set.
This read-only property can be used to retrieve the
:ref:`scope <Target Command Scope>` of the given file set.
It will be ``PRIVATE``, ``PUBLIC``, or ``INTERFACE``.
+30
View File
@@ -0,0 +1,30 @@
SOURCES
-------
This specifies the list of paths to sources for the file set.
The :command:`target_sources` command sets or adds to the ``SOURCES`` file set
property for the file sets defined with the ``PRIVATE`` or ``PUBLIC`` keyword
and is the usual way to manipulate it.
Contents of ``SOURCES`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
Each file must be in one of the base directories, or a subdirectory of one of
the base directories. If relative paths are specified, they are considered
relative to the target's source directory.
The following behavior applies for the ``SOURCES`` and
:prop_fs:`INTERFACE_SOURCES` file set properties, dependent on the value of the
:prop_fs:`SCOPE` file set property:
``PRIVATE``
Only the ``SOURCES`` property can be set. Any change to
the :prop_fs:`INTERFACE_SOURCES` property will be ignored.
``PUBLIC``
``SOURCES`` and :prop_fs:`INTERFACE_SOURCES` properties will always have the
same content.
``INTERFACE``
Only the :prop_fs:`INTERFACE_SOURCES` property can be set. Any change to
``SOURCES`` property will be ignored.
+8
View File
@@ -0,0 +1,8 @@
TYPE
----
The type of the file set.
This read-only property can be used to retrieve the
:ref:`type <File Sets>` of the given file set.
Possible values are ``CXX_MODULES`` or ``HEADERS``.
@@ -0,0 +1,6 @@
FILE_SET-new-properties
-----------------------
* File sets gained support for the :prop_fs:`SCOPE`, :prop_fs:`TYPE`,
:prop_fs:`BASE_DIRS`, :prop_fs:`SOURCES`, and
:prop_fs:`INTERFACE_SOURCES` file set properties.
+195 -14
View File
@@ -3,20 +3,28 @@
#include "cmFileSet.h"
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmStringAlgorithms.h"
#include "cmTarget.h"
namespace Metadata = cm::FileSetMetadata;
cmFileSet::cmFileSet(cmMakefile* makefile, std::string name, std::string type,
Metadata::Visibility visibility)
cmFileSet::cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name,
std::string type, Metadata::Visibility visibility)
: Makefile(makefile)
, Target(target)
, Name(std::move(name))
, Type(std::move(type))
, Visibility(visibility)
@@ -49,25 +57,139 @@ void cmFileSet::AddFileEntry(BT<std::string> files)
this->FileEntries.push_back(std::move(files));
}
std::string const cmFileSet::propCOMPILE_DEFINITIONS = "COMPILE_DEFINITIONS";
std::string const cmFileSet::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
std::string const cmFileSet::propINCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES";
namespace {
enum class ReadOnlyCondition
{
All,
Imported,
NonImported,
};
struct ReadOnlyProperty
{
ReadOnlyProperty(ReadOnlyCondition cond)
: Condition{ cond }
{
}
// ReadOnlyProperty(ReadOnlyCondition cond, cmPolicies::PolicyID id)
// : Condition{ cond }
// , Policy{ id }
// {
// }
ReadOnlyCondition Condition;
cm::optional<cmPolicies::PolicyID> Policy;
std::string message(std::string const& prop, cmTarget* target,
cmFileSet* fileSet) const
{
std::string msg;
if (this->Condition == ReadOnlyCondition::All) {
msg = cmStrCat(" property is read-only for the file set \"",
fileSet->GetName(), " of the target \"");
} else if (this->Condition == ReadOnlyCondition::Imported) {
msg = " property can't be set on a file set attached to the imported "
"target \"";
} else if (this->Condition == ReadOnlyCondition::NonImported) {
msg =
" property can't be set on a file set attached to the non-imported "
"target \"";
}
return cmStrCat(prop, msg, target->GetName(), "\"\n");
}
bool isReadOnly(std::string const& prop, cmMakefile* context,
cmTarget* target, cmFileSet* fileSet) const
{
auto importedTarget = target->IsImported();
bool matchingCondition = true;
if ((!importedTarget && this->Condition == ReadOnlyCondition::Imported) ||
(importedTarget &&
this->Condition == ReadOnlyCondition::NonImported)) {
matchingCondition = false;
}
if (!matchingCondition) {
// Not read-only in this scenario
return false;
}
bool readOnly = true;
if (!this->Policy) {
// No policy associated, so is always read-only
context->IssueMessage(MessageType::FATAL_ERROR,
this->message(prop, target, fileSet));
}
return readOnly;
}
};
bool IsSettableProperty(cmMakefile* context, cmTarget* target,
cmFileSet* fileSet, std::string const& prop)
{
using ROC = ReadOnlyCondition;
static std::unordered_map<std::string, ReadOnlyProperty> const readOnlyProps{
{ "TYPE", { ROC::All } }, { "SCOPE", { ROC::All } }
};
auto it = readOnlyProps.find(prop);
if (it != readOnlyProps.end()) {
return !(it->second.isReadOnly(prop, context, target, fileSet));
}
return true;
}
cm::string_view const BASE_DIRS = "BASE_DIRS"_s;
cm::string_view const SOURCES = "SOURCES"_s;
cm::string_view const INTERFACE_SOURCES = "INTERFACE_SOURCES"_s;
cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s;
cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s;
cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s;
}
void cmFileSet::SetProperty(std::string const& prop, cmValue value)
{
if (prop == propINCLUDE_DIRECTORIES) {
if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) {
return;
}
if (prop == BASE_DIRS) {
this->ClearDirectoryEntries();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddDirectoryEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == SOURCES) {
if (!this->IsForSelf()) {
return;
}
this->ClearFileEntries();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INTERFACE_SOURCES) {
if (!this->IsForInterface()) {
return;
}
this->ClearFileEntries();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INCLUDE_DIRECTORIES) {
this->IncludeDirectories.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->IncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == propCOMPILE_OPTIONS) {
} else if (prop == COMPILE_OPTIONS) {
this->CompileOptions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileOptions.emplace_back(value, lfbt);
}
} else if (prop == propCOMPILE_DEFINITIONS) {
} else if (prop == COMPILE_DEFINITIONS) {
this->CompileDefinitions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
@@ -81,17 +203,42 @@ void cmFileSet::SetProperty(std::string const& prop, cmValue value)
void cmFileSet::AppendProperty(std::string const& prop,
std::string const& value, bool asString)
{
if (prop == propINCLUDE_DIRECTORIES) {
if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) {
return;
}
if (prop == BASE_DIRS) {
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddDirectoryEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == SOURCES) {
if (!this->IsForSelf()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INTERFACE_SOURCES) {
if (!this->IsForInterface()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INCLUDE_DIRECTORIES) {
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->IncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == propCOMPILE_OPTIONS) {
} else if (prop == COMPILE_OPTIONS) {
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileOptions.emplace_back(value, lfbt);
}
} else if (prop == propCOMPILE_DEFINITIONS) {
} else if (prop == COMPILE_DEFINITIONS) {
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileDefinitions.emplace_back(value, lfbt);
@@ -104,7 +251,32 @@ void cmFileSet::AppendProperty(std::string const& prop,
cmValue cmFileSet::GetProperty(std::string const& prop) const
{
// Check for the properties with backtraces.
if (prop == propINCLUDE_DIRECTORIES) {
if (prop == BASE_DIRS) {
static std::string output;
output = cmList::to_string(this->GetDirectoryEntries());
return cmValue(output);
}
if (prop == SOURCES) {
if (!this->IsForSelf() || this->GetFileEntries().empty()) {
return nullptr;
}
static std::string output;
output = cmList::to_string(this->GetFileEntries());
return cmValue(output);
}
if (prop == INTERFACE_SOURCES) {
if (!this->IsForInterface() || this->GetFileEntries().empty()) {
return nullptr;
}
static std::string output;
output = cmList::to_string(this->GetFileEntries());
return cmValue(output);
}
if (prop == INCLUDE_DIRECTORIES) {
if (this->IncludeDirectories.empty()) {
return nullptr;
}
@@ -114,7 +286,7 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == propCOMPILE_OPTIONS) {
if (prop == COMPILE_OPTIONS) {
if (this->CompileOptions.empty()) {
return nullptr;
}
@@ -124,7 +296,7 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == propCOMPILE_DEFINITIONS) {
if (prop == COMPILE_DEFINITIONS) {
if (this->CompileDefinitions.empty()) {
return nullptr;
}
@@ -134,5 +306,14 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == "TYPE"_s) {
return cmValue{ this->GetType() };
}
if (prop == "SCOPE"_s) {
static std::string scope =
std::string{ Metadata::VisibilityToName(this->GetVisibility()) };
return cmValue{ scope };
}
return this->Properties.GetPropertyValue(prop);
}
+4 -6
View File
@@ -14,12 +14,13 @@
#include "cmValue.h"
class cmMakefile;
class cmTarget;
class cmFileSet
{
public:
cmFileSet(cmMakefile* makefile, std::string name, std::string type,
cm::FileSetMetadata::Visibility visibility);
cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name,
std::string type, cm::FileSetMetadata::Visibility visibility);
std::string const& GetName() const { return this->Name; }
std::string const& GetType() const { return this->Type; }
@@ -80,6 +81,7 @@ public:
private:
cmMakefile* Makefile;
cmTarget* Target;
std::string Name;
std::string Type;
cm::FileSetMetadata::Visibility Visibility;
@@ -89,8 +91,4 @@ private:
std::vector<BT<std::string>> CompileOptions;
std::vector<BT<std::string>> CompileDefinitions;
std::vector<BT<std::string>> IncludeDirectories;
static std::string const propCOMPILE_DEFINITIONS;
static std::string const propCOMPILE_OPTIONS;
static std::string const propINCLUDE_DIRECTORIES;
};
+33 -5
View File
@@ -3583,6 +3583,7 @@ namespace {
bool GetFileSet(std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorTarget const*& target,
cmGeneratorFileSet const*& fileSet)
{
auto const& fileSetName = parameters[0];
@@ -3602,7 +3603,7 @@ bool GetFileSet(std::vector<std::string> const& parameters,
cmLocalGenerator const* lg = eval->CurrentTarget
? eval->CurrentTarget->GetLocalGenerator()
: eval->Context.LG;
auto const* target = lg->FindGeneratorTargetToUse(targetName);
target = lg->FindGeneratorTargetToUse(targetName);
if (!target) {
reportError(eval, content->GetOriginalExpression(),
cmStrCat("Non-existent target: ", targetName));
@@ -3638,8 +3639,9 @@ static const struct FileSetExistsNode : public cmGeneratorExpressionNode
return std::string{};
}
cmGeneratorTarget const* target = nullptr;
cmGeneratorFileSet const* fileSet = nullptr;
if (!GetFileSet(parameters, eval, content, fileSet)) {
if (!GetFileSet(parameters, eval, content, target, fileSet)) {
return std::string{};
}
@@ -3657,7 +3659,7 @@ static const struct FileSetPropertyNode : public cmGeneratorExpressionNode
std::string Evaluate(
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override
cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
@@ -3690,8 +3692,9 @@ static const struct FileSetPropertyNode : public cmGeneratorExpressionNode
return std::string{};
}
cmGeneratorTarget const* target = nullptr;
cmGeneratorFileSet const* fileSet = nullptr;
if (!GetFileSet(parameters, eval, content, fileSet)) {
if (!GetFileSet(parameters, eval, content, target, fileSet)) {
return std::string{};
}
if (!fileSet) {
@@ -3701,7 +3704,32 @@ static const struct FileSetPropertyNode : public cmGeneratorExpressionNode
return std::string{};
}
return fileSet->GetProperty(propertyName);
auto result = fileSet->GetProperty(propertyName);
if (propertyName == "BASE_DIRS"_s || propertyName == "SOURCES"_s ||
propertyName == "INTERFACE_SOURCES"_s) {
cmGeneratorExpressionDAGChecker dagChecker{
target, propertyName, content,
dagCheckerParent, eval->Context, eval->Backtrace,
};
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.ReportError(eval, content->GetOriginalExpression());
return std::string{};
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
// No error. We just skip cyclic references.
return std::string{};
case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
case cmGeneratorExpressionDAGChecker::DAG:
break;
}
return cmGeneratorExpression::StripEmptyListElements(
this->EvaluateDependentExpression(result, eval, target, &dagChecker,
target));
}
return result;
}
} fileSetPropertyNode;
+1 -1
View File
@@ -3160,7 +3160,7 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
cm::FileSetMetadata::Visibility vis)
{
auto result = this->impl->FileSets.emplace(
name, cmFileSet(this->GetMakefile(), name, type, vis));
name, cmFileSet(this->GetMakefile(), this, name, type, vis));
if (result.second) {
auto bt = this->impl->Makefile->GetBacktrace();
if (cm::contains(this->impl->FileSetTypes, type)) {
@@ -502,7 +502,8 @@ static bool testCreateFromFileSet()
std::make_shared<cmDebugger::cmDebuggerVariablesManager>();
auto dummies = CreateDummies("Foo");
cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS",
cmFileSet fileSet(dummies.Makefile.get(),
dummies.Makefile->GetOrderedTargets()[0], "Foo", "HEADERS",
cm::FileSetMetadata::Visibility::Public);
BT<std::string> directory;
directory.Value = "c:/";
@@ -547,7 +548,8 @@ static bool testCreateFromFileSets()
std::make_shared<cmDebugger::cmDebuggerVariablesManager>();
auto dummies = CreateDummies("Foo");
cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS",
cmFileSet fileSet(dummies.Makefile.get(),
dummies.Makefile->GetOrderedTargets()[0], "Foo", "HEADERS",
cm::FileSetMetadata::Visibility::Public);
BT<std::string> directory;
directory.Value = "c:/";
+1
View File
@@ -611,6 +611,7 @@ add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
add_RunCMake_test(TargetObjects)
add_RunCMake_test(TargetProperties)
add_RunCMake_test(FileSetProperties)
add_RunCMake_test(ToolchainFile)
if(NOT CMake_TEST_EXTERNAL_CMAKE)
@@ -0,0 +1,21 @@
enable_language(C)
add_library(foo STATIC foo.c)
target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h)
get_property(dirs FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS)
if(NOT dirs STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
message(SEND_ERROR "wrong base dirs: '${dirs}' instead of '${CMAKE_CURRENT_SOURCE_DIR}'")
endif()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/foo.h" "")
set_property(FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}")
set_property(FILE_SET HEADERS TARGET foo PROPERTY SOURCES "${CMAKE_CURRENT_BINARY_DIR}/foo.h")
get_property(dirs FILE_SET HEADERS TARGET foo PROPERTY BASE_DIRS)
if(NOT dirs STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
message(SEND_ERROR "wrong base dirs: '${dirs}' instead of '${CMAKE_CURRENT_BINARY_DIR}'")
endif()
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 4.3)
project(${RunCMake_TEST})
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1 @@
1
@@ -0,0 +1,12 @@
CMake Error at ReadOnly-Properties\.cmake:[0-9]+ \(set_property\):
TYPE property is read-only for the file set "HEADERS of the target "foo"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at ReadOnly-Properties\.cmake:[0-9]+ \(set_property\):
SCOPE property is read-only for the file set "HEADERS of the target "foo"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
@@ -0,0 +1,7 @@
add_library(foo STATIC)
target_sources(foo PUBLIC FILE_SET HEADERS)
set_property(FILE_SET HEADERS TARGET foo PROPERTY TYPE CXX_MODULES)
set_property(FILE_SET HEADERS TARGET foo PROPERTY SCOPE PRIVATE)
@@ -0,0 +1,8 @@
include(RunCMake)
run_cmake(ReadOnly-Properties)
run_cmake(TYPE)
run_cmake(SCOPE)
run_cmake(SOURCES)
run_cmake(BASE_DIRS)
@@ -0,0 +1,10 @@
enable_language(C)
add_library(foo STATIC foo.c)
target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h)
get_property(scope FILE_SET HEADERS TARGET foo PROPERTY SCOPE)
if(NOT scope STREQUAL "PUBLIC")
message(SEND_ERROR "wrong scope: ${scope} instead of PUBLIC")
endif()
@@ -0,0 +1,112 @@
enable_language(C)
add_library(foo STATIC foo.c)
target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h)
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES ".*/foo.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'foo.h'")
endif()
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES ".*/foo.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h'")
endif()
set_property(FILE_SET HEADERS TARGET foo PROPERTY SOURCES bar.h)
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'bar.h'")
endif()
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'")
endif()
set_property(FILE_SET HEADERS TARGET foo APPEND PROPERTY SOURCES foo.h)
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h;[^;]*foo.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'foo.h;bar.h'")
endif()
get_property(srcs FILE_SET HEADERS TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h;[^;]*foo.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h;bar.h'")
endif()
target_sources(foo PRIVATE FILE_SET foo TYPE HEADERS FILES foo.h)
get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES ".*/foo.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'foo.h'")
endif()
get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES)
if(srcs)
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list")
endif()
set_property(FILE_SET foo TARGET foo PROPERTY SOURCES bar.h)
get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'bar.h'")
endif()
get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES)
if(srcs)
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list")
endif()
set_property(FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES foo.h)
get_property(srcs FILE_SET foo TARGET foo PROPERTY SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong sources: '${srcs}' instead of 'bar.h'")
endif()
get_property(srcs FILE_SET foo TARGET foo PROPERTY INTERFACE_SOURCES)
if(srcs)
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of empty list")
endif()
target_sources(foo INTERFACE FILE_SET bar TYPE HEADERS FILES foo.h)
get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES)
if(srcs)
message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list")
endif()
get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES "[^;]*/foo.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'foo.h'")
endif()
set_property(FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES bar.h)
get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES)
if(srcs)
message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list")
endif()
get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'")
endif()
set_property(FILE_SET bar TARGET foo PROPERTY SOURCES foo.h)
get_property(srcs FILE_SET bar TARGET foo PROPERTY SOURCES)
if(srcs)
message(SEND_ERROR "wrong sources: '${srcs}' instead of empty list")
endif()
get_property(srcs FILE_SET bar TARGET foo PROPERTY INTERFACE_SOURCES)
if(NOT srcs MATCHES "[^;]*bar.h$")
message(SEND_ERROR "wrong interface sources: '${srcs}' instead of 'bar.h'")
endif()
@@ -0,0 +1,10 @@
enable_language(C)
add_library(foo STATIC foo.c)
target_sources(foo PUBLIC FILE_SET HEADERS FILES foo.h)
get_property(type FILE_SET HEADERS TARGET foo PROPERTY TYPE)
if(NOT type STREQUAL "HEADERS")
message(SEND_ERROR "wrong type: ${type} instead of HEADERS")
endif()