GenEx FILE_SET_PROPERTY

This commit is contained in:
Marc Chevrier
2026-01-26 16:53:18 +01:00
parent 810e5d3a7a
commit 209d86c5fe
59 changed files with 435 additions and 0 deletions

View File

@@ -2511,6 +2511,46 @@ These expressions look up the values of
The source file property will be read from the directory scope in which
``target`` was created (``target`` must therefore exist).
.. _`FileSet-Dependent Expressions`:
FileSet-Dependent Expressions
-----------------------------
FileSet Meta-Data
^^^^^^^^^^^^^^^^^
These expressions look up information about a file set.
.. genex:: $<FILE_SET_EXISTS:fileset,TARGET:target>
.. versionadded:: 4.3
``1`` if the ``fileset`` exists as a CMake file set attached to the
``target``, else ``0``.
The possible sub-options are:
``TARGET:target``
The target on which the file set depends.
FileSet Properties
^^^^^^^^^^^^^^^^^^
These expressions look up the values of file set properties.
.. genex:: $<FILE_SET_PROPERTY:fileset,TARGET:target,prop>
.. versionadded:: 4.3
Value of the property ``prop`` on the file set ``fileset``, or empty if
the property is not set. An error will be raised if the file set is not
known by CMake.
The possible sub-options are:
``TARGET:target``
The target on which the file set depends.
.. _`Target-Dependent Expressions`:
Target-Dependent Expressions

View File

@@ -0,0 +1,5 @@
GenEx-FILE_SET_PROPERTY
-----------------------
* :genex:`$<FILE_SET_EXISTS>` and :genex:`$<FILE_SET_PROPERTY>` generator
expressions were added to query CMake file set existence and properties.

View File

@@ -29,6 +29,7 @@
#include "cmCMakePath.h"
#include "cmCMakeString.hxx"
#include "cmComputeLinkInformation.h"
#include "cmFileSet.h"
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorExpression.h"
@@ -3578,6 +3579,128 @@ static const struct DeviceLinkNode : public cmGeneratorExpressionNode
}
} deviceLinkNode;
namespace {
bool GetFileSet(std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content, cmFileSet*& fileSet)
{
auto const& fileSetName = parameters[0];
auto targetName = parameters[1];
auto* makefile = eval->Context.LG->GetMakefile();
fileSet = nullptr;
auto const TARGET = "TARGET:"_s;
if (cmHasPrefix(targetName, TARGET)) {
targetName = targetName.substr(TARGET.length());
if (targetName.empty()) {
reportError(eval, content->GetOriginalExpression(),
cmStrCat("No value provided for the ", TARGET, " option."));
return false;
}
auto* target = makefile->FindTargetToUse(targetName);
if (!target) {
reportError(eval, content->GetOriginalExpression(),
cmStrCat("Non-existent target: ", targetName));
return false;
}
fileSet = target->GetFileSet(fileSetName);
} else {
reportError(eval, content->GetOriginalExpression(),
cmStrCat("Invalid option. ", TARGET, " expected."));
return false;
}
return true;
}
}
static const struct FileSetExistsNode : public cmGeneratorExpressionNode
{
FileSetExistsNode() {} // NOLINT(modernize-use-equals-default)
// This node handles errors on parameter count itself.
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override
{
if (parameters[0].empty()) {
reportError(
eval, content->GetOriginalExpression(),
"$<FILE_SET_EXISTS:fileset,TARGET:tgt> expression requires a "
"non-empty FILE_SET name.");
return std::string{};
}
cmFileSet* fileSet = nullptr;
if (!GetFileSet(parameters, eval, content, fileSet)) {
return std::string{};
}
return fileSet ? "1" : "0";
}
} fileSetExistsNode;
static const struct FileSetPropertyNode : public cmGeneratorExpressionNode
{
FileSetPropertyNode() {} // NOLINT(modernize-use-equals-default)
// This node handles errors on parameter count itself.
int NumExpectedParameters() const override { return 3; }
std::string Evaluate(
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
std::string const& fileSetName = parameters.front();
std::string const& propertyName = parameters.back();
if (fileSetName.empty() && propertyName.empty()) {
reportError(eval, content->GetOriginalExpression(),
"$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression "
"requires a non-empty FILE_SET name and property name.");
return std::string{};
}
if (fileSetName.empty()) {
reportError(
eval, content->GetOriginalExpression(),
"$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a "
"non-empty FILE_SET name.");
return std::string{};
}
if (propertyName.empty()) {
reportError(
eval, content->GetOriginalExpression(),
"$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a "
"non-empty property name.");
return std::string{};
}
if (!propertyNameValidator.find(propertyName)) {
reportError(eval, content->GetOriginalExpression(),
"Property name not supported.");
return std::string{};
}
cmFileSet* fileSet = nullptr;
if (!GetFileSet(parameters, eval, content, fileSet)) {
return std::string{};
}
if (!fileSet) {
reportError(
eval, content->GetOriginalExpression(),
cmStrCat("FILE_SET \"", fileSetName, "\" is not known from CMake."));
return std::string{};
}
return fileSet->GetProperty(propertyName);
}
} fileSetPropertyNode;
namespace {
bool GetSourceFile(
cmRange<std::vector<std::string>::const_iterator> parameters,
@@ -5701,6 +5824,8 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
{ "QUOTE", &quoteNode },
{ "SOURCE_EXISTS", &sourceExistsNode },
{ "SOURCE_PROPERTY", &sourcePropertyNode },
{ "FILE_SET_EXISTS", &fileSetExistsNode },
{ "FILE_SET_PROPERTY", &fileSetPropertyNode },
{ "TARGET_PROPERTY", &targetPropertyNode },
{ "TARGET_INTERMEDIATE_DIR", &targetIntermediateDirNode },
{ "TARGET_NAME", &targetNameNode },

View File

@@ -531,6 +531,8 @@ add_RunCMake_test(GenEx-TARGET_IMPORT_FILE)
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-SOURCE_EXISTS)
add_RunCMake_test(GenEx-SOURCE_PROPERTY)
add_RunCMake_test(GenEx-FILE_SET_EXISTS)
add_RunCMake_test(GenEx-FILE_SET_PROPERTY)
add_RunCMake_test(GenEx-TARGET_PROPERTY)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
add_RunCMake_test(GenEx-STRING)

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 4.0...4.3)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@@ -0,0 +1,2 @@
include ("${RunCMake_TEST_BINARY_DIR}/FILE_SET_EXISTS-generated.cmake")

View File

@@ -0,0 +1,19 @@
add_library(foo INTERFACE)
target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS FILES file.h)
set (GENERATE_CONTENT [[
macro (CHECK_VALUE test_msg value expected)
if (NOT "${value}" STREQUAL "${expected}")
string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
endif()
endmacro()
]])
string(APPEND GENERATE_CONTENT
"check_value (\"<FILE_SET_EXISTS:bar,TARGET:foo>\" \"$<FILE_SET_EXISTS:bar,TARGET:foo>\" \"0\")\n"
"check_value (\"<FILE_SET_EXISTS:foo,TARGET:foo>\" \"$<FILE_SET_EXISTS:foo,TARGET:foo>\" \"1\")\n")
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/FILE_SET_EXISTS-generated.cmake" CONTENT "${GENERATE_CONTENT}")

View File

@@ -0,0 +1,11 @@
include(RunCMake)
run_cmake(no-arguments)
run_cmake(bad-arguments)
run_cmake(no-fileset)
run_cmake(bad-option)
run_cmake(no-TARGET)
run_cmake(bad-TARGET)
run_cmake(FILE_SET_EXISTS)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-TARGET\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS:foo,TARGET:foo>
Non-existent target: foo
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,3 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS:foo,TARGET:foo>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at bad-arguments\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS:fs,opt,foo>
\$<FILE_SET_EXISTS> expression requires 2 comma separated parameters, but
got 3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS:fs,opt,foo>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-option\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS:foo,BAD_OPTION>
Invalid option. TARGET: expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS:foo,BAD_OPTION>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at no-TARGET\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS:foo,TARGET:>
No value provided for the TARGET: option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,3 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS:foo,TARGET:>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at no-arguments\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS>
\$<FILE_SET_EXISTS> expression requires 2 comma separated parameters, but
got 0 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at no-fileset\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_EXISTS:,TARGET:tgt>
\$<FILE_SET_EXISTS:fileset,TARGET:tgt> expression requires a non-empty
FILE_SET name.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_EXISTS:,TARGET:tgt>")

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 4.0...4.3)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@@ -0,0 +1,2 @@
include ("${RunCMake_TEST_BINARY_DIR}/FILE_SET_PROPERTY-generated.cmake")

View File

@@ -0,0 +1,25 @@
set (GENERATE_CONTENT [[
macro (CHECK_VALUE test_msg value expected)
if (NOT "${value}" STREQUAL "${expected}")
string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
endif()
endmacro()
]])
add_library(foo INTERFACE)
target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS FILES file.h)
set_property(FILE_SET foo TARGET foo PROPERTY FOO BAR)
get_property(reference FILE_SET foo TARGET foo PROPERTY FOO)
string (APPEND GENERATE_CONTENT
"check_value (\"<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>\" \"$<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>\" \"${reference}\")\n")
get_property(reference FILE_SET foo TARGET foo PROPERTY VOID)
string (APPEND GENERATE_CONTENT
"check_value (\"<FILE_SET_PROPERTY:foo,TARGET:foo,VOID>\" \"$<FILE_SET_PROPERTY:foo,TARGET:foo,VOID>\" \"${reference}\")\n")
file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/FILE_SET_PROPERTY-generated.cmake"
CONTENT "${GENERATE_CONTENT}")

View File

@@ -0,0 +1,14 @@
include(RunCMake)
run_cmake(no-arguments)
run_cmake(bad-arguments)
run_cmake(no-fileset)
run_cmake(bad-fileset)
run_cmake(no-property)
run_cmake(bad-property)
run_cmake(bad-option)
run_cmake(no-TARGET)
run_cmake(bad-TARGET)
run_cmake(FILE_SET_PROPERTY)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-TARGET\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>
Non-existent target: foo
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,3 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at bad-arguments\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:fs,opt,prop,foo>
\$<FILE_SET_PROPERTY> expression requires 3 comma separated parameters, but
got 4 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:fs,opt,prop,foo>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-fileset\.cmake:4 \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>
FILE_SET "foo" is not known from CMake.
Call Stack \(most recent call first\):
CMakeLists\.txt:5 \(include\)

View File

@@ -0,0 +1,4 @@
add_library(foo INTERFACE)
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,TARGET:foo,FOO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-option\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,foo,FOO>
Invalid option\. TARGET: expected\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,4 @@
add_library(foo INTERFACE)
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,foo,FOO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-property\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,TARGET:foo,F\?OO>
Property name not supported.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,TARGET:foo,F?OO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at no-TARGET\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,TARGET:,FOO>
No value provided for the TARGET: option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,3 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,TARGET:,FOO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at no-arguments\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:>
\$<FILE_SET_PROPERTY> expression requires 3 comma separated parameters, but
got 1 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at no-fileset\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:,TARGET:foo,FOO>
\$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a
non-empty FILE_SET name.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:,TARGET:foo,FOO>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
CMake Error at no-property\.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<FILE_SET_PROPERTY:foo,TARGET:foo,>
\$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a
non-empty property name.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<FILE_SET_PROPERTY:foo,TARGET:foo,>")