FILE_SET: Add support for the SOURCES type

Fixes: #27550
This commit is contained in:
Marc Chevrier
2026-03-31 09:49:59 +02:00
parent 5697bcced0
commit 5c5b68f44e
80 changed files with 1818 additions and 102 deletions
+23 -2
View File
@@ -89,6 +89,18 @@ files within those directories.
Sources intended to be used via a language's ``#include`` mechanism.
``SOURCES``
.. versionadded:: 4.4
Specifies sources to use when building a target and/or its dependents.
With the scope ``PRIVATE`` and ``PUBLIC``, items will populate the
:prop_fs:`SOURCES` property of ``<set>``, which are used when building the
target itself. With the scope ``PUBLIC`` and ``INTERFACE``, items will
populate the :prop_fs:`INTERFACE_SOURCES` property of ``<set>``, which are
used when building dependents. The sources specified by the
:prop_fs:`INTERFACE_SOURCES` property are propagated, transitively, to all
the dependents.
``CXX_MODULES``
.. versionadded:: 3.28
@@ -106,8 +118,8 @@ have their :prop_sf:`HEADER_FILE_ONLY` property set to ``TRUE``. Files in an
:command:`install(TARGETS)` command, and exported with the
:command:`install(EXPORT)` and :command:`export` commands.
Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, or
``PRIVATE`` and accepts the following arguments:
Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``,
or ``PRIVATE`` and accepts the following arguments:
``FILE_SET <set>``
@@ -164,6 +176,15 @@ For file sets of type ``HEADERS``:
* :prop_tgt:`HEADER_DIRS`
* :prop_tgt:`HEADER_DIRS_<NAME>`
For file sets of type ``SOURCES``:
* :prop_tgt:`SOURCE_SETS`
* :prop_tgt:`INTERFACE_SOURCE_SETS`
* :prop_tgt:`SOURCE_SET`
* :prop_tgt:`SOURCE_SET_<NAME>`
* :prop_tgt:`SOURCE_DIRS`
* :prop_tgt:`SOURCE_DIRS_<NAME>`
For file sets of type ``CXX_MODULES``:
* :prop_tgt:`CXX_MODULE_SETS`
+12
View File
@@ -307,6 +307,7 @@ Properties on Targets
/prop_tgt/INTERFACE_LINK_OPTIONS
/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
/prop_tgt/INTERFACE_PRECOMPILE_HEADERS
/prop_tgt/INTERFACE_SOURCE_SETS
/prop_tgt/INTERFACE_SOURCES
/prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION
@@ -409,6 +410,11 @@ Properties on Targets
/prop_tgt/SKIP_BUILD_RPATH
/prop_tgt/SKIP_LINTING
/prop_tgt/SOURCE_DIR
/prop_tgt/SOURCE_DIRS
/prop_tgt/SOURCE_DIRS_NAME
/prop_tgt/SOURCE_SET
/prop_tgt/SOURCE_SET_NAME
/prop_tgt/SOURCE_SETS
/prop_tgt/SOURCES
/prop_tgt/SOVERSION
/prop_tgt/SPDX_LICENSE
@@ -535,6 +541,12 @@ Properties on File Sets
:maxdepth: 1
/prop_fs/BASE_DIRS
/prop_fs/COMPILE_DEFINITIONS
/prop_fs/COMPILE_OPTIONS
/prop_fs/INCLUDE_DIRECTORIES
/prop_fs/INTERFACE_COMPILE_DEFINITIONS
/prop_fs/INTERFACE_COMPILE_OPTIONS
/prop_fs/INTERFACE_INCLUDE_DIRECTORIES
/prop_fs/INTERFACE_SOURCES
/prop_fs/SCOPE
/prop_fs/SOURCES
+37
View File
@@ -0,0 +1,37 @@
COMPILE_DEFINITIONS
-------------------
.. versionadded:: 4.4
Preprocessor definitions for compiling a source file.
The ``COMPILE_DEFINITIONS`` property may be set to a semicolon-separated
list of preprocessor definitions using the syntax ``VAR`` or ``VAR=value``.
Function-style definitions are not supported. CMake will
automatically escape the value correctly for the native build system
(note that CMake language syntax may require escapes to specify some
values).
CMake will automatically drop some definitions that are not supported
by the native build tool. :generator:`Xcode` does not support
per-configuration definitions on source files.
.. include:: /include/COMPILE_DEFINITIONS_DISCLAIMER.rst
Contents of ``COMPILE_DEFINITIONS`` may use :manual:`cmake-generator-expressions(7)`
with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
manual for available expressions. However, :generator:`Xcode`
does not support per-config per-source settings, so expressions
that depend on the build configuration are not allowed with that
generator.
Related properties:
* Use :prop_fs:`COMPILE_OPTIONS` to pass additional compile flags.
* Use :prop_fs:`INCLUDE_DIRECTORIES` to pass additional include directories.
Related commands:
* :command:`add_compile_definitions` for directory-wide settings
* :command:`target_compile_definitions` for target-specific settings
* :command:`set_source_files_properties` for source-specific settings
+36
View File
@@ -0,0 +1,36 @@
COMPILE_OPTIONS
---------------
.. versionadded:: 4.4
List of additional options to pass to the compiler.
This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
of options and will be added to the list of compile flags when the sources of
this file set are built. The options will be added after target-wide options
and source level ones.
Contents of ``COMPILE_OPTIONS`` may use "generator expressions" with the
syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual
for available expressions. However, :generator:`Xcode`
does not support per-config per-source settings, so expressions
that depend on the build configuration are not allowed with that
generator.
Usage example:
.. code-block:: cmake
set_property(FILE_SET SOURCES TARGET foo PROPERTY
COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-missing-field-initializer")
Related properties:
* Use :prop_fs:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions.
* Use :prop_fs:`INCLUDE_DIRECTORIES` to pass additional include directories.
Related commands:
* :command:`add_compile_options` for directory-wide settings
* :command:`target_compile_options` for target-specific settings
* :command:`set_source_files_properties` for source-specific settings
+31
View File
@@ -0,0 +1,31 @@
INCLUDE_DIRECTORIES
-------------------
.. versionadded:: 4.4
List of preprocessor include file search directories.
This property holds a :ref:`semicolon-separated list <CMake Language Lists>` of paths
and will be added to the list of include directories when the sources of this
file set are built. These directories will take precedence over directories
defined at target level and source level except for :generator:`Xcode`
generator due to technical limitations.
Relative paths should not be added to this property directly.
Contents of ``INCLUDE_DIRECTORIES`` may use "generator expressions" with
the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual
for available expressions. However, :generator:`Xcode` does not support
per-config per-source settings, so expressions that depend on the build
configuration are not allowed with that generator.
Related properties:
* Use :prop_fs:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions.
* Use :prop_fs:`COMPILE_OPTIONS` to pass additional compile flags.
Related commands:
* :command:`include_directories` for directory-wide settings
* :command:`target_include_directories` for target-specific settings
* :command:`set_source_files_properties` for source-specific settings
@@ -0,0 +1,15 @@
INTERFACE_COMPILE_DEFINITIONS
-----------------------------
.. versionadded:: 4.4
.. |property_name| replace:: compile definitions
.. |command_name| replace:: :command:`set_property(FILE_SET)`
.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_COMPILE_DEFINITIONS``
.. include:: include/INTERFACE_BUILD_PROPERTY.rst
Related properties:
* Use :prop_fs:`INTERFACE_COMPILE_OPTIONS` to pass additional compile flags.
* Use :prop_fs:`INTERFACE_INCLUDE_DIRECTORIES` to pass additional include
directories.
@@ -0,0 +1,16 @@
INTERFACE_COMPILE_OPTIONS
-------------------------
.. versionadded:: 4.4
.. |property_name| replace:: compile options
.. |command_name| replace:: :command:`set_property(FILE_SET)`
.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_COMPILE_OPTIONS``
.. include:: include/INTERFACE_BUILD_PROPERTY.rst
Related properties:
* Use :prop_fs:`INTERFACE_COMPILE_DEFINITIONS` to pass additional preprocessor
definitions.
* Use :prop_fs:`INTERFACE_INCLUDE_DIRECTORIES` to pass additional include
directories.
@@ -0,0 +1,35 @@
INTERFACE_INCLUDE_DIRECTORIES
-----------------------------
.. versionadded:: 4.4
.. |property_name| replace:: include directories
.. |command_name| replace:: :command:`set_property(FILE_SET)`
.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_INCLUDE_DIRECTORIES``
.. include:: include/INTERFACE_BUILD_PROPERTY.rst
Include directories usage requirements commonly differ between the build-tree
and the install-tree. The ``BUILD_INTERFACE`` and ``INSTALL_INTERFACE``
generator expressions can be used to describe separate usage requirements
based on the usage location. Relative paths are allowed within the
``INSTALL_INTERFACE`` expression and are interpreted relative to the
installation prefix. For example:
.. code-block:: cmake
set_property(FILE_SET myfile_set TARGET mylib PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>
$<INSTALL_INTERFACE:include/mylib> # <prefix>/include/mylib
)
Related properties:
* Use :prop_fs:`INTERFACE_COMPILE_DEFINITIONS` to pass additional preprocessor
definitions.
* Use :prop_fs:`INTERFACE_COMPILE_OPTIONS` to pass additional compile flags.
Creating Relocatable Packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. |INTERFACE_PROPERTY_LINK| replace:: ``INTERFACE_INCLUDE_DIRECTORIES``
.. include:: /include/INTERFACE_INCLUDE_DIRECTORIES_WARNING.rst
+1 -1
View File
@@ -7,4 +7,4 @@ 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``.
Possible values are ``CXX_MODULES``, ``SOURCES``, or ``HEADERS``.
@@ -0,0 +1,16 @@
List of public |property_name| requirements for a file set.
File sets may populate this property to publish the |property_name|
required to compile the sources for the target. The |command_name|
command populates this property.
When target dependencies are specified using :command:`target_link_libraries`,
CMake will read this property from file sets of all target dependencies to
determine the build properties of the consumer. These build properties are only
applied to the sources of the file sets. The other sources of the consumer are
unaffected.
Contents of |PROPERTY_INTERFACE_NAME| may use "generator expressions"
with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
manual for available expressions. See the :manual:`cmake-buildsystem(7)`
-manual for more on defining buildsystem properties.
+14
View File
@@ -0,0 +1,14 @@
INTERFACE_SOURCE_SETS
---------------------
.. versionadded:: 4.4
Read-only list of the target's ``INTERFACE`` and ``PUBLIC`` source sets (i.e.
all file sets with the type ``SOURCES``). Files listed in these source sets
can be installed with :command:`install(TARGETS)` and exported with
:command:`install(EXPORT)` and :command:`export`.
Source sets may be defined using the :command:`target_sources` command
``FILE_SET`` option with type ``SOURCES``.
See also :prop_tgt:`SOURCE_SETS`.
+14
View File
@@ -0,0 +1,14 @@
SOURCE_DIRS
-----------
.. versionadded:: 4.4
Semicolon-separated list of base directories of the target's default
source set (i.e. the file set with name and type ``SOURCES``). The property
supports :manual:`generator expressions <cmake-generator-expressions(7)>`.
This property is normally only set by :command:`target_sources(FILE_SET)`
rather than being manipulated directly.
See :prop_tgt:`SOURCE_DIRS_<NAME>` for the list of base directories in
other source sets.
+15
View File
@@ -0,0 +1,15 @@
SOURCE_DIRS_<NAME>
------------------
.. versionadded:: 4.4
Semicolon-separated list of base directories of the target's ``<NAME>``
source set, which has the set type ``SOURCES``. The property supports
:manual:`generator expressions <cmake-generator-expressions(7)>`.
This property is normally only set by :command:`target_sources(FILE_SET)`
rather than being manipulated directly.
See :prop_tgt:`SOURCE_DIRS` for the list of base directories in the
default source set. See :prop_tgt:`SOURCE_SETS` for the file set names of all
source sets.
+15
View File
@@ -0,0 +1,15 @@
SOURCE_SET
----------
.. versionadded:: 4.4
Semicolon-separated list of files in the target's default source set,
(i.e. the file set with name and type ``SOURCES``). If any of the paths
are relative, they are computed relative to the target's source directory.
The property supports
:manual:`generator expressions <cmake-generator-expressions(7)>`.
This property is normally only set by :command:`target_sources(FILE_SET)`
rather than being manipulated directly.
See :prop_tgt:`SOURCE_SET_<NAME>` for the list of files in other source sets.
+14
View File
@@ -0,0 +1,14 @@
SOURCE_SETS
-----------
.. versionadded:: 4.4
Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` source sets (i.e.
all file sets with the type ``SOURCES``). Files listed in these file sets are
treated as source files.
Source sets may be defined using the :command:`target_sources` command
``FILE_SET`` option with type ``SOURCES``.
See also :prop_tgt:`SOURCE_SET_<NAME>`, :prop_tgt:`SOURCE_SET` and
:prop_tgt:`INTERFACE_SOURCE_SETS`.
+15
View File
@@ -0,0 +1,15 @@
SOURCE_SET_<NAME>
-----------------
.. versionadded:: 4.4
Semicolon-separated list of files in the target's ``<NAME>`` source set,
which has the set type ``SOURCES``. If any of the paths are relative,
they are computed relative to the target's source directory. The property
supports :manual:`generator expressions <cmake-generator-expressions(7)>`.
This property is normally only set by :command:`target_sources(FILE_SET)`
rather than being manipulated directly.
See :prop_tgt:`SOURCE_SET` for the list of files in the default source set.
See :prop_tgt:`SOURCE_SETS` for the file set names of all source sets.
+4
View File
@@ -0,0 +1,4 @@
FILE_SET-SOURCES
----------------
* The :ref:`file set <file sets>` gains the support of the ``SOURCES`` type.
+41
View File
@@ -7,6 +7,7 @@
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmList.h"
@@ -73,6 +74,31 @@ void addInterfaceEntry(cmGeneratorTarget const* headTarget,
}
}
}
void addInterfaceFileSetsEntry(cmGeneratorTarget const* headTarget,
cm::string_view type, std::string const& prop,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
EvaluatedTargetPropertyEntries& entries,
std::vector<cmLinkItem> const& libraries)
{
for (cmLinkItem const& lib : libraries) {
if (lib.Target) {
EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
// Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
// caller's property and hand-evaluate it as if it were compiled.
// Create a context as cmCompiledGeneratorExpression::Evaluate does.
cm::GenEx::Evaluation eval(context, false, headTarget, headTarget, true,
lib.Backtrace);
cmExpandList(
lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty(
type, prop, &eval, dagChecker),
ee.Values);
ee.ContextDependent = eval.HadContextSensitiveCondition;
entries.Entries.emplace_back(std::move(ee));
}
}
}
}
void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
@@ -108,4 +134,19 @@ void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
}
}
}
void AddInterfaceFileSetsEntries(cmGeneratorTarget const* headTarget,
cm::string_view type, std::string const& prop,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
EvaluatedTargetPropertyEntries& entries)
{
if (cmLinkImplementationLibraries const* impl =
headTarget->GetLinkImplementationLibraries(
context.Config, cmGeneratorTarget::UseTo::Compile)) {
entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
addInterfaceFileSetsEntry(headTarget, type, prop, context, dagChecker,
entries, impl->Libraries);
}
}
}
+8
View File
@@ -6,6 +6,8 @@
#include <string>
#include <vector>
#include <cm/string_view>
#include "cmGeneratorTarget.h"
#include "cmListFileCache.h"
@@ -83,4 +85,10 @@ void AddInterfaceEntries(
EvaluatedTargetPropertyEntries& entries,
IncludeRuntimeInterface searchRuntime,
cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile);
void AddInterfaceFileSetsEntries(cmGeneratorTarget const* headTarget,
cm::string_view type, std::string const& prop,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
EvaluatedTargetPropertyEntries& entries);
}
@@ -27,6 +27,8 @@
#include "cmFastbuildTargetGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalCommonGenerator.h"
#include "cmGlobalFastbuildGenerator.h"
@@ -117,7 +119,20 @@ std::string cmFastbuildNormalTargetGenerator::DetectCompilerFlags(
cmGeneratorExpressionInterpreter genexInterpreter(
this->GetLocalGenerator(), Config, this->GeneratorTarget, language);
auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
this->Config, &srcFile);
std::vector<std::string> sourceIncludesVec;
if (fileSet) {
auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetIncludeDirectories(this->Config, language)
: fileSet->GetInterfaceIncludeDirectories(this->Config, language);
if (!fsIncludes.empty()) {
this->LocalGenerator->AppendIncludeDirectories(
sourceIncludesVec, cm::remove_BT(fsIncludes), srcFile);
}
}
if (cmValue cincludes = srcFile.GetProperty(INCLUDE_DIRECTORIES)) {
this->LocalGenerator->AppendIncludeDirectories(
sourceIncludesVec,
@@ -140,6 +155,17 @@ std::string cmFastbuildNormalTargetGenerator::DetectCompilerFlags(
this->LocalGenerator->AppendCompileOptions(
compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
}
// Add flags from file set properties.
if (fileSet) {
auto options = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileOptions(this->Config, language)
: fileSet->GetInterfaceCompileOptions(this->Config, language);
if (!options.empty()) {
this->LocalGenerator->AppendCompileOptions(compileFlags,
cm::remove_BT(options));
}
}
// Source includes take precedence over target includes.
this->LocalGenerator->AppendFlags(compileFlags, sourceIncludesStr);
this->LocalGenerator->AppendFlags(compileFlags,
@@ -411,6 +437,17 @@ std::string cmFastbuildNormalTargetGenerator::ComputeDefines(
genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
}
if (auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
this->Config, &srcFile)) {
auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileDefinitions(this->Config, language)
: fileSet->GetInterfaceCompileDefinitions(this->Config, language);
if (!fsDefines.empty()) {
this->LocalGenerator->AppendDefines(defines, fsDefines);
}
}
std::string definesString = this->GetDefines(language, Config);
LogMessage(cmStrCat("TARGET DEFINES = ", definesString));
this->GetLocalGenerator()->JoinDefines(defines, definesString, language);
+135 -2
View File
@@ -16,6 +16,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmTarget.h"
@@ -57,6 +58,33 @@ void cmFileSet::AddFileEntry(BT<std::string> files)
this->FileEntries.push_back(std::move(files));
}
cmBTStringRange cmFileSet::GetIncludeDirectories() const
{
return cmMakeRange(this->IncludeDirectories);
}
cmBTStringRange cmFileSet::GetInterfaceIncludeDirectories() const
{
return cmMakeRange(this->InterfaceIncludeDirectories);
}
cmBTStringRange cmFileSet::GetCompileOptions() const
{
return cmMakeRange(this->CompileOptions);
}
cmBTStringRange cmFileSet::GetInterfaceCompileOptions() const
{
return cmMakeRange(this->InterfaceCompileOptions);
}
cmBTStringRange cmFileSet::GetCompileDefinitions() const
{
return cmMakeRange(this->CompileDefinitions);
}
cmBTStringRange cmFileSet::GetInterfaceCompileDefinitions() const
{
return cmMakeRange(this->InterfaceCompileDefinitions);
}
namespace {
enum class ReadOnlyCondition
{
@@ -142,9 +170,15 @@ bool IsSettableProperty(cmMakefile* context, cmTarget* target,
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;
cm::string_view const INTERFACE_INCLUDE_DIRECTORIES =
"INTERFACE_INCLUDE_DIRECTORIES"_s;
cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s;
cm::string_view const INTERFACE_COMPILE_DEFINITIONS =
"INTERFACE_COMPILE_DEFINITIONS"_s;
cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s;
cm::string_view const INTERFACE_COMPILE_OPTIONS =
"INTERFACE_COMPILE_OPTIONS"_s;
}
void cmFileSet::SetProperty(std::string const& prop, cmValue value)
@@ -178,23 +212,59 @@ void cmFileSet::SetProperty(std::string const& prop, cmValue value)
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INCLUDE_DIRECTORIES) {
if (!this->IsForSelf()) {
return;
}
this->IncludeDirectories.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->IncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
if (!this->IsForInterface()) {
return;
}
this->InterfaceIncludeDirectories.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceIncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == COMPILE_OPTIONS) {
if (!this->IsForSelf()) {
return;
}
this->CompileOptions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileOptions.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_COMPILE_OPTIONS) {
if (!this->IsForInterface()) {
return;
}
this->InterfaceCompileOptions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceCompileOptions.emplace_back(value, lfbt);
}
} else if (prop == COMPILE_DEFINITIONS) {
if (!this->IsForSelf()) {
return;
}
this->CompileDefinitions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileDefinitions.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_COMPILE_DEFINITIONS) {
if (!this->IsForInterface()) {
return;
}
this->InterfaceCompileDefinitions.clear();
if (value) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceCompileDefinitions.emplace_back(value, lfbt);
}
} else {
this->Properties.SetProperty(prop, value);
}
@@ -229,20 +299,53 @@ void cmFileSet::AppendProperty(std::string const& prop,
this->AddFileEntry(BT<std::string>{ value, lfbt });
}
} else if (prop == INCLUDE_DIRECTORIES) {
if (!this->IsForSelf()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->IncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
if (!this->IsForInterface()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceIncludeDirectories.emplace_back(value, lfbt);
}
} else if (prop == COMPILE_OPTIONS) {
if (!this->IsForSelf()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileOptions.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_COMPILE_OPTIONS) {
if (!this->IsForInterface()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceCompileOptions.emplace_back(value, lfbt);
}
} else if (prop == COMPILE_DEFINITIONS) {
if (!this->IsForSelf()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->CompileDefinitions.emplace_back(value, lfbt);
}
} else if (prop == INTERFACE_COMPILE_DEFINITIONS) {
if (!this->IsForInterface()) {
return;
}
if (!value.empty()) {
cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
this->InterfaceCompileDefinitions.emplace_back(value, lfbt);
}
} else {
this->Properties.AppendProperty(prop, value, asString);
}
@@ -286,6 +389,16 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == INTERFACE_INCLUDE_DIRECTORIES) {
if (this->InterfaceIncludeDirectories.empty()) {
return nullptr;
}
static std::string output;
output = cmList::to_string(this->InterfaceIncludeDirectories);
return cmValue(output);
}
if (prop == COMPILE_OPTIONS) {
if (this->CompileOptions.empty()) {
return nullptr;
@@ -296,6 +409,16 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == INTERFACE_COMPILE_OPTIONS) {
if (this->InterfaceCompileOptions.empty()) {
return nullptr;
}
static std::string output;
output = cmList::to_string(this->InterfaceCompileOptions);
return cmValue(output);
}
if (prop == COMPILE_DEFINITIONS) {
if (this->CompileDefinitions.empty()) {
return nullptr;
@@ -306,6 +429,16 @@ cmValue cmFileSet::GetProperty(std::string const& prop) const
return cmValue(output);
}
if (prop == INTERFACE_COMPILE_DEFINITIONS) {
if (this->InterfaceCompileDefinitions.empty()) {
return nullptr;
}
static std::string output;
output = cmList::to_string(this->InterfaceCompileDefinitions);
return cmValue(output);
}
if (prop == "TYPE"_s) {
return cmValue{ this->GetType() };
}
+17 -3
View File
@@ -8,6 +8,7 @@
#include <cm/string_view>
#include "cmAlgorithms.h"
#include "cmFileSetMetadata.h"
#include "cmListFileCache.h"
#include "cmPropertyMap.h"
@@ -42,7 +43,7 @@ public:
}
bool CanBeIncluded() const
{
return this->Type == cm::FileSetMetadata::HEADERS;
return this->Type != cm::FileSetMetadata::CXX_MODULES;
}
void CopyEntries(cmFileSet const* fs);
@@ -61,6 +62,16 @@ public:
return this->FileEntries;
}
// Special properties
cmBTStringRange GetIncludeDirectories() const;
cmBTStringRange GetInterfaceIncludeDirectories() const;
cmBTStringRange GetCompileOptions() const;
cmBTStringRange GetInterfaceCompileOptions() const;
cmBTStringRange GetCompileDefinitions() const;
cmBTStringRange GetInterfaceCompileDefinitions() const;
//! Set/Get a property of this file set
void SetProperty(std::string const& prop, cmValue value);
void SetProperty(std::string const& prop, std::nullptr_t)
@@ -88,7 +99,10 @@ private:
std::vector<BT<std::string>> DirectoryEntries;
std::vector<BT<std::string>> FileEntries;
cmPropertyMap Properties;
std::vector<BT<std::string>> CompileOptions;
std::vector<BT<std::string>> CompileDefinitions;
std::vector<BT<std::string>> IncludeDirectories;
std::vector<BT<std::string>> InterfaceIncludeDirectories;
std::vector<BT<std::string>> CompileOptions;
std::vector<BT<std::string>> InterfaceCompileOptions;
std::vector<BT<std::string>> CompileDefinitions;
std::vector<BT<std::string>> InterfaceCompileDefinitions;
};
+25 -2
View File
@@ -2,7 +2,9 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmFileSetMetadata.h"
#include <map>
#include <string>
#include <utility>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -74,14 +76,36 @@ bool VisibilityIsForInterface(Visibility vis)
}
cm::string_view const HEADERS = "HEADERS"_s;
cm::string_view const SOURCES = "SOURCES"_s;
cm::string_view const CXX_MODULES = "CXX_MODULES"_s;
namespace {
std::vector<cm::string_view> KnownTypes{ HEADERS, CXX_MODULES };
std::map<cm::string_view, FileSetDescriptor> const FileSetDescriptors{
{ cm::FileSetMetadata::HEADERS,
{ cm::FileSetMetadata::HEADERS,
cm::FileSetMetadata::FileSetLookup::Target } },
{ cm::FileSetMetadata::SOURCES,
{ cm::FileSetMetadata::SOURCES,
cm::FileSetMetadata::FileSetLookup::Dependencies } },
{ cm::FileSetMetadata::CXX_MODULES,
{ cm::FileSetMetadata::CXX_MODULES,
cm::FileSetMetadata::FileSetLookup::Target } },
};
std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES };
cmsys::RegularExpression const ValidNameRegex("^[a-z0-9][a-zA-Z0-9_]*$");
}
cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type)
{
auto it = FileSetDescriptors.find(type);
if (it != FileSetDescriptors.end()) {
return it->second;
}
return cm::nullopt;
}
std::vector<cm::string_view> const& GetKnownTypes()
{
return KnownTypes;
@@ -97,6 +121,5 @@ bool IsValidName(cm::string_view name)
cmsys::RegularExpressionMatch match;
return ValidNameRegex.find(name.data(), match);
}
}
}
+24
View File
@@ -4,6 +4,7 @@
#include <vector>
#include <cm/optional>
#include <cm/string_view>
class cmMakefile;
@@ -25,8 +26,31 @@ bool VisibilityIsForInterface(Visibility vis);
// Pre-defined FileSet types
extern cm::string_view const HEADERS;
extern cm::string_view const SOURCES;
extern cm::string_view const CXX_MODULES;
enum class FileSetLookup
{
// Search for file sets attached to the target
Target,
// Search also file sets inherited from link libraries
Dependencies
};
struct FileSetDescriptor
{
FileSetDescriptor(cm::string_view type, FileSetLookup lookup)
: Type(type)
, Lookup(lookup)
{
}
cm::string_view const Type;
FileSetLookup const Lookup;
};
cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type);
std::vector<cm::string_view> const& GetKnownTypes();
bool IsKnownType(cm::string_view type);
+257 -2
View File
@@ -12,10 +12,16 @@
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmAlgorithms.h"
#include "cmEvaluatedTargetProperty.h"
#include "cmFileSet.h"
#include "cmGenExContext.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
@@ -27,6 +33,7 @@
class cmLinkItem;
namespace {
class FileSetPropertyEntry : public cm::TargetPropertyEntry
{
public:
@@ -92,12 +99,105 @@ private:
mutable std::string Value;
};
void CreatePropertyGeneratorExpressions(
cmake& cmakeInstance, cmBTStringRange entries,
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>& items)
{
for (auto const& entry : entries) {
items.emplace_back(
cm::TargetPropertyEntry::Create(cmakeInstance, entry, false));
}
}
enum class OptionsParse
{
None,
Shell
};
std::vector<BT<std::string>> ProcessOptions(
cm::EvaluatedTargetPropertyEntries const& entries,
OptionsParse parse = OptionsParse::None)
{
std::vector<BT<std::string>> options;
std::unordered_set<std::string> uniqueOptions;
for (cm::EvaluatedTargetPropertyEntry const& entry : entries.Entries) {
for (std::string const& opt : entry.Values) {
if (uniqueOptions.insert(opt).second) {
if (parse == OptionsParse::Shell &&
cmHasLiteralPrefix(opt, "SHELL:")) {
std::vector<std::string> tmp;
cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp);
for (std::string& o : tmp) {
options.emplace_back(std::move(o), entry.Backtrace);
}
} else {
options.emplace_back(opt, entry.Backtrace);
}
}
}
}
return options;
}
std::vector<BT<std::string>> ProcessIncludes(
cmGeneratorTarget const* target, std::string const& fileSetName,
cm::string_view property, cm::EvaluatedTargetPropertyEntries& entries)
{
std::vector<BT<std::string>> includes;
std::unordered_set<std::string> uniqueIncludes;
for (cm::EvaluatedTargetPropertyEntry& entry : entries.Entries) {
for (std::string& include : entry.Values) {
if (!cmValue::IsOff(include)) {
cmSystemTools::ConvertToUnixSlashes(include);
}
if (!cmSystemTools::FileIsFullPath(include)) {
target->GetLocalGenerator()->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("File set \"", fileSetName, "\" from the target \"",
target->GetName(), "\" contains relative path in its ",
property, ":\n \"", include, "\""));
return includes;
}
if (uniqueIncludes.insert(include).second) {
includes.emplace_back(include, entry.Backtrace);
}
}
}
return includes;
}
}
//
// Class cmGeneratorFileSet
//
cmGeneratorFileSet::cmGeneratorFileSet(cmFileSet const* fileSet)
: FileSet(fileSet)
cmGeneratorFileSet::cmGeneratorFileSet(cmGeneratorTarget const* target,
cmFileSet const* fileSet)
: Target(target)
, FileSet(fileSet)
{
auto& cmake = *target->GetLocalGenerator()->GetCMakeInstance();
CreatePropertyGeneratorExpressions(cmake, fileSet->GetIncludeDirectories(),
this->IncludeDirectories);
CreatePropertyGeneratorExpressions(cmake,
fileSet->GetInterfaceIncludeDirectories(),
this->InterfaceIncludeDirectories);
CreatePropertyGeneratorExpressions(cmake, fileSet->GetCompileOptions(),
this->CompileOptions);
CreatePropertyGeneratorExpressions(cmake,
fileSet->GetInterfaceCompileOptions(),
this->InterfaceCompileOptions);
CreatePropertyGeneratorExpressions(cmake, fileSet->GetCompileDefinitions(),
this->CompileDefinitions);
CreatePropertyGeneratorExpressions(cmake,
fileSet->GetInterfaceCompileDefinitions(),
this->InterfaceCompileDefinitions);
}
std::string const& cmGeneratorFileSet::GetName() const
@@ -131,6 +231,161 @@ cmValue cmGeneratorFileSet::GetProperty(std::string const& prop) const
return this->FileSet->GetProperty(prop);
}
std::vector<BT<std::string>> cmGeneratorFileSet::GetIncludeDirectories(
std::string const& config, std::string const& lang) const
{
ConfigAndLanguage cacheKey(config, lang);
{
auto it = this->IncludeDirectoriesCache.find(cacheKey);
if (it != this->IncludeDirectoriesCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, lang);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "INCLUDE_DIRECTORIES", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->IncludeDirectories);
auto includes = ProcessIncludes(this->Target, this->GetName(),
"INCLUDE_DIRECTORIES"_s, entries);
this->IncludeDirectoriesCache.emplace(cacheKey, includes);
return includes;
}
std::vector<BT<std::string>>
cmGeneratorFileSet::GetInterfaceIncludeDirectories(
std::string const& config, std::string const& lang) const
{
ConfigAndLanguage cacheKey(config, lang);
{
auto it = this->InterfaceIncludeDirectoriesCache.find(cacheKey);
if (it != this->InterfaceIncludeDirectoriesCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config, lang);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "INCLUDE_DIRECTORIES", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->InterfaceIncludeDirectories);
auto includes = ProcessIncludes(this->Target, this->GetName(),
"INTERFACE_INCLUDE_DIRECTORIES"_s, entries);
this->InterfaceIncludeDirectoriesCache.emplace(cacheKey, includes);
return includes;
}
std::vector<BT<std::string>> cmGeneratorFileSet::GetCompileOptions(
std::string const& config, std::string const& language) const
{
ConfigAndLanguage cacheKey(config, language);
{
auto it = this->CompileOptionsCache.find(cacheKey);
if (it != this->CompileOptionsCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config,
language);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "COMPILE_OPTIONS", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->CompileOptions);
auto options = ProcessOptions(entries, OptionsParse::Shell);
this->CompileOptionsCache.emplace(cacheKey, options);
return options;
}
std::vector<BT<std::string>> cmGeneratorFileSet::GetInterfaceCompileOptions(
std::string const& config, std::string const& language) const
{
ConfigAndLanguage cacheKey(config, language);
{
auto it = this->InterfaceCompileOptionsCache.find(cacheKey);
if (it != this->InterfaceCompileOptionsCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config,
language);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "COMPILE_OPTIONS", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->InterfaceCompileOptions);
auto options = ProcessOptions(entries, OptionsParse::Shell);
this->InterfaceCompileOptionsCache.emplace(cacheKey, options);
return options;
}
std::vector<BT<std::string>> cmGeneratorFileSet::GetCompileDefinitions(
std::string const& config, std::string const& language) const
{
ConfigAndLanguage cacheKey(config, language);
{
auto it = this->CompileDefinitionsCache.find(cacheKey);
if (it != this->CompileDefinitionsCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config,
language);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "COMPILE_DEFINITIONS", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->CompileDefinitions);
auto defines = ProcessOptions(entries);
this->CompileDefinitionsCache.emplace(cacheKey, defines);
return defines;
}
std::vector<BT<std::string>>
cmGeneratorFileSet::GetInterfaceCompileDefinitions(
std::string const& config, std::string const& language) const
{
ConfigAndLanguage cacheKey(config, language);
{
auto it = this->InterfaceCompileDefinitionsCache.find(cacheKey);
if (it != this->InterfaceCompileDefinitionsCache.end()) {
return it->second;
}
}
cm::GenEx::Context context(this->Target->GetLocalGenerator(), config,
language);
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, "COMPILE_DEFINITIONS", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries entries =
cm::EvaluateTargetPropertyEntries(this->Target, context, &dagChecker,
this->InterfaceCompileDefinitions);
auto defines = ProcessOptions(entries);
this->InterfaceCompileDefinitionsCache.emplace(cacheKey, defines);
return defines;
}
std::vector<BT<std::string>> const& cmGeneratorFileSet::GetDirectoryEntries()
const
{
+43 -4
View File
@@ -12,6 +12,7 @@
#include "cmFileSetMetadata.h"
#include "cmGeneratorExpression.h"
#include "cmListFileCache.h"
#include "cmTargetPropertyEntry.h"
#include "cmValue.h"
@@ -21,9 +22,6 @@ struct Context;
}
}
template <typename T>
class BT;
struct cmGeneratorExpressionDAGChecker;
class cmFileSet;
@@ -34,7 +32,7 @@ class cmGeneratorFileSet
public:
using TargetPropertyEntry = cm::TargetPropertyEntry;
cmGeneratorFileSet(cmFileSet const*);
cmGeneratorFileSet(cmGeneratorTarget const*, cmFileSet const*);
~cmGeneratorFileSet() = default;
cmGeneratorFileSet(cmGeneratorFileSet&&) = default;
@@ -49,10 +47,31 @@ public:
bool IsForInterface() const;
bool CanBeIncluded() const;
cmGeneratorTarget const* GetTarget() const { return this->Target; }
bool BelongsTo(cmGeneratorTarget const* target) const
{
return this->Target == target;
}
cmFileSet const* GetFileSet() const { return this->FileSet; }
cmValue GetProperty(std::string const& prop) const;
std::vector<BT<std::string>> GetIncludeDirectories(
std::string const& config, std::string const& lang) const;
std::vector<BT<std::string>> GetInterfaceIncludeDirectories(
std::string const& config, std::string const& lang) const;
std::vector<BT<std::string>> GetCompileOptions(
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>> GetInterfaceCompileOptions(
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>> GetCompileDefinitions(
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>> GetInterfaceCompileDefinitions(
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>> const& GetDirectoryEntries() const;
std::vector<BT<std::string>> const& GetFileEntries() const;
@@ -92,9 +111,29 @@ public:
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
private:
cmGeneratorTarget const* Target;
cmFileSet const* FileSet;
mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
CompiledDirectoryEntries;
mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
CompiledFileEntries;
using TargetPropertyEntries =
std::vector<std::unique_ptr<TargetPropertyEntry>>;
TargetPropertyEntries IncludeDirectories;
TargetPropertyEntries InterfaceIncludeDirectories;
TargetPropertyEntries CompileOptions;
TargetPropertyEntries InterfaceCompileOptions;
TargetPropertyEntries CompileDefinitions;
TargetPropertyEntries InterfaceCompileDefinitions;
using ConfigAndLanguage = std::pair<std::string, std::string>;
using ConfigAndLanguageToBTStrings =
std::map<ConfigAndLanguage, std::vector<BT<std::string>>>;
mutable ConfigAndLanguageToBTStrings IncludeDirectoriesCache;
mutable ConfigAndLanguageToBTStrings InterfaceIncludeDirectoriesCache;
mutable ConfigAndLanguageToBTStrings CompileOptionsCache;
mutable ConfigAndLanguageToBTStrings InterfaceCompileOptionsCache;
mutable ConfigAndLanguageToBTStrings CompileDefinitionsCache;
mutable ConfigAndLanguageToBTStrings InterfaceCompileDefinitionsCache;
};
+259 -22
View File
@@ -5,18 +5,31 @@
#include <algorithm>
#include <iterator>
#include <map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cm/memory>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmFileSetMetadata.h"
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorExpressionNode.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmSourceFile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target,
cmLocalGenerator* lg)
@@ -24,9 +37,10 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target,
, LocalGenerator(lg)
{
for (auto const& name : target->Target->GetAllPrivateFileSets()) {
auto entry = this->FileSets.emplace(
name,
cm::make_unique<cmGeneratorFileSet>(target->Target->GetFileSet(name)));
auto entry =
this->FileSets.emplace(name,
cm::make_unique<cmGeneratorFileSet>(
target, target->Target->GetFileSet(name)));
auto const* fileSet = entry.first->second.get();
this->AllFileSets.push_back(fileSet);
@@ -36,9 +50,10 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target,
auto it = this->FileSets.find(name);
cmGeneratorFileSet const* fileSet = nullptr;
if (it == this->FileSets.end()) {
auto entry = this->FileSets.emplace(
name,
cm::make_unique<cmGeneratorFileSet>(target->Target->GetFileSet(name)));
auto entry =
this->FileSets.emplace(name,
cm::make_unique<cmGeneratorFileSet>(
target, target->Target->GetFileSet(name)));
fileSet = entry.first->second.get();
this->AllFileSets.push_back(fileSet);
} else {
@@ -102,15 +117,28 @@ cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSet(
cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
std::string const& config, std::string const& path) const
{
using Lookup = cm::FileSetMetadata::FileSetLookup;
this->BuildInfoCache(config);
auto const& info = this->Configs[config];
auto const it = info.FileSetCache.find(path);
if (it == info.FileSetCache.end()) {
return nullptr;
if (it != info.FileSetCache.end()) {
return it->second;
}
return it->second;
// 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{ ""_s, Lookup::Target })
.Lookup == Lookup::Dependencies) {
return it2->second;
}
return nullptr;
}
cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
std::string const& config, cmSourceFile const* sf) const
@@ -120,6 +148,7 @@ cmGeneratorFileSet const* cmGeneratorFileSets::GetFileSetForSource(
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
cmGeneratorFileSets::GetSources(
std::function<bool(cmGeneratorFileSet const*)> include,
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
@@ -127,7 +156,7 @@ cmGeneratorFileSets::GetSources(
for (auto const& entry : this->FileSets) {
auto const* fileSet = entry.second.get();
if (fileSet->IsForSelf()) {
if (include(fileSet)) {
auto sources = fileSet->GetSources(context, target, dagChecker);
std::move(sources.begin(), sources.end(), std::back_inserter(entries));
}
@@ -136,23 +165,216 @@ cmGeneratorFileSets::GetSources(
return entries;
}
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
cmGeneratorFileSets::GetSources(
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
return this->GetSources(
[](cmGeneratorFileSet const* fileSet) -> bool {
return fileSet->IsForSelf();
},
context, target, dagChecker);
}
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
cmGeneratorFileSets::GetSources(
std::string type, cm::GenEx::Context const& context,
cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
std::vector<std::unique_ptr<TargetPropertyEntry>> entries;
return this->GetSources(
[&type](cmGeneratorFileSet const* fileSet) -> bool {
return fileSet->IsForSelf() && fileSet->GetType() == type;
},
context, target, dagChecker);
}
for (auto const& entry : this->FileSets) {
auto const* fileSet = entry.second.get();
if (!fileSet->IsForSelf() && fileSet->GetType() == type) {
auto sources = fileSet->GetSources(context, target, dagChecker);
std::move(sources.begin(), sources.end(), std::back_inserter(entries));
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
cmGeneratorFileSets::GetInterfaceSources(
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
return this->GetSources(
[](cmGeneratorFileSet const* fileSet) -> bool {
return fileSet->IsForInterface();
},
context, target, dagChecker);
}
std::vector<std::unique_ptr<cm::TargetPropertyEntry>>
cmGeneratorFileSets::GetInterfaceSources(
std::string type, cm::GenEx::Context const& context,
cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
return this->GetSources(
[&type](cmGeneratorFileSet const* fileSet) -> bool {
return fileSet->IsForInterface() && fileSet->GetType() == type;
},
context, target, dagChecker);
}
bool cmGeneratorFileSets::MaybeHaveInterfaceProperty(
cm::string_view type, std::string const& prop,
cm::GenEx::Evaluation* eval) const
{
std::string const key =
cmStrCat(type, "::", prop, '@', eval->Context.Config);
auto i = this->MaybeInterfacePropertyExists.find(key);
if (i == this->MaybeInterfacePropertyExists.end()) {
// Insert an entry now in case there is a cycle.
i = this->MaybeInterfacePropertyExists.emplace(key, false).first;
bool& maybeInterfaceProp = i->second;
for (auto const* fileSet : this->GetInterfaceFileSets(type)) {
// If this file set itself has a non-empty property value, we are done.
maybeInterfaceProp = !fileSet->GetProperty(prop).IsEmpty();
if (maybeInterfaceProp) {
break;
}
}
// Otherwise, recurse to interface dependencies.
if (!maybeInterfaceProp) {
cmGeneratorTarget const* headTarget =
eval->HeadTarget ? eval->HeadTarget : this->Target;
if (cmLinkInterfaceLibraries const* iface =
this->Target->GetLinkInterfaceLibraries(
eval->Context.Config, headTarget,
cmGeneratorTarget::UseTo::Compile)) {
if (iface->HadHeadSensitiveCondition) {
// With a different head target we may get to a library with
// this interface property.
maybeInterfaceProp = true;
} else {
// The transitive interface libraries do not depend on the
// head target, so we can follow them.
for (cmLinkItem const& lib : iface->Libraries) {
if (lib.Target &&
lib.Target->GetGeneratorFileSets()->MaybeHaveInterfaceProperty(
type, prop, eval)) {
maybeInterfaceProp = true;
break;
}
}
}
}
}
}
return i->second;
}
std::string cmGeneratorFileSets::EvaluateInterfaceProperty(
cm::string_view type, std::string const& prop, cm::GenEx::Evaluation* eval,
cmGeneratorExpressionDAGChecker* dagCheckerParent) const
{
// If the property does not appear transitively at all, we are done.
if (!this->MaybeHaveInterfaceProperty(type, prop, eval)) {
return std::string{};
}
cmList result;
cmGeneratorExpressionDAGChecker dagChecker{
this->Target, prop, nullptr,
dagCheckerParent, eval->Context, eval->Backtrace,
};
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.ReportError(
eval,
cmStrCat("$<FILE_SET_PROPERTY:*,TARGET:", this->Target->GetName(), ',',
prop, '>'));
return std::string{};
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
// No error. We just skip cyclic references.
case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
// No error. We have already seen this transitive property.
return std::string{};
case cmGeneratorExpressionDAGChecker::DAG:
break;
}
cmGeneratorTarget const* headTarget =
eval->HeadTarget ? eval->HeadTarget : this->Target;
for (auto const* fileSet : this->GetInterfaceFileSets(type)) {
if (cmValue p = fileSet->GetProperty(prop)) {
result.append(cmGeneratorExpressionNode::EvaluateDependentExpression(
*p, eval, headTarget, &dagChecker, this->Target));
}
}
return entries;
if (cmLinkInterfaceLibraries const* iface =
this->Target->GetLinkInterfaceLibraries(
eval->Context.Config, headTarget,
cmGeneratorTarget::UseTo::Compile)) {
eval->HadContextSensitiveCondition = eval->HadContextSensitiveCondition ||
iface->HadContextSensitiveCondition;
for (cmLinkItem const& lib : iface->Libraries) {
// Broken code can have a target in its own link interface.
// Don't follow such link interface entries so as not to create a
// self-referencing loop.
if (lib.Target && lib.Target != this->Target) {
// Pretend $<FILE_SET__PROPERTY:fileSet,TARGET:lib.Target,prop>
// appeared in the above property and hand-evaluate it as if it were
// compiled.
// Create a context as cmCompiledGeneratorExpression::Evaluate does.
cm::GenEx::Evaluation libEval(
eval->Context, eval->Quiet, headTarget, this->Target,
eval->EvaluateForBuildsystem, eval->Backtrace);
std::string libResult = cmGeneratorExpression::StripEmptyListElements(
lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty(
type, prop, &libEval, &dagChecker));
if (!libResult.empty()) {
result.append(libResult);
}
eval->HadContextSensitiveCondition =
eval->HadContextSensitiveCondition ||
libEval.HadContextSensitiveCondition;
eval->HadHeadSensitiveCondition =
eval->HadHeadSensitiveCondition || libEval.HadHeadSensitiveCondition;
}
}
}
return result.to_string();
}
namespace {
void GetInterfaceFiles(cmGeneratorTarget const* target,
cm::GenEx::Context const& context,
std::unordered_set<cmGeneratorTarget const*>& targets,
std::map<std::string, cmGeneratorFileSet const*>& cache)
{
namespace Metadata = cm::FileSetMetadata;
for (auto const& type : Metadata::GetKnownTypes()) {
auto fileSetDescriptor = Metadata::GetFileSetDescriptor(type);
if (fileSetDescriptor &&
fileSetDescriptor->Lookup == Metadata::FileSetLookup::Dependencies) {
for (auto const* fileSet : target->GetInterfaceFileSets(type)) {
auto files = fileSet->GetFiles(context, target);
for (auto const& it : files.first) {
for (auto const& filename : it.second) {
auto collapsedFile = cmSystemTools::CollapseFullPath(filename);
cache[collapsedFile] = fileSet;
}
}
}
}
}
if (cmLinkInterfaceLibraries const* iface =
target->GetLinkInterfaceLibraries(context.Config, target,
cmGeneratorTarget::UseTo::Compile)) {
for (cmLinkItem const& lib : iface->Libraries) {
if (lib.Target && lib.Target != target &&
targets.insert(lib.Target).second) {
GetInterfaceFiles(lib.Target, context, targets, cache);
}
}
}
}
}
void cmGeneratorFileSets::BuildInfoCache(std::string const& config) const
@@ -163,16 +385,31 @@ void cmGeneratorFileSets::BuildInfoCache(std::string const& config) const
return;
}
for (auto const& item : this->FileSets) {
cm::GenEx::Context context(this->LocalGenerator, config);
auto const* file_set = item.second.get();
cm::GenEx::Context context(this->LocalGenerator, config);
auto files = file_set->GetFiles(context, this->Target);
for (auto const& item : this->FileSets) {
auto const* fileSet = item.second.get();
auto files = fileSet->GetFiles(context, this->Target);
for (auto const& it : files.first) {
for (auto const& filename : it.second) {
auto collapsedFile = cmSystemTools::CollapseFullPath(filename);
info.FileSetCache[collapsedFile] = file_set;
info.FileSetCache[collapsedFile] = fileSet;
}
}
}
// retrieve all files inherited from dependent targets
std::unordered_set<cmGeneratorTarget const*> targets;
if (cmLinkImplementationLibraries const* impl =
this->Target->GetLinkImplementationLibraries(
config, cmGeneratorTarget::UseTo::Compile)) {
for (cmLinkItem const& lib : impl->Libraries) {
if (lib.Target) {
GetInterfaceFiles(lib.Target, context, targets,
info.InterfaceFileSetCache);
}
}
}
+25
View File
@@ -4,6 +4,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <functional>
#include <map>
#include <memory>
#include <string>
@@ -17,6 +18,7 @@
namespace cm {
namespace GenEx {
struct Context;
struct Evaluation;
}
}
@@ -64,7 +66,24 @@ public:
cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources(
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources(
std::string type, cm::GenEx::Context const& context,
cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
std::string EvaluateInterfaceProperty(
cm::string_view type, std::string const& prop, cm::GenEx::Evaluation* eval,
cmGeneratorExpressionDAGChecker* dagCheckerParent) const;
private:
std::vector<std::unique_ptr<cm::TargetPropertyEntry>> GetSources(
std::function<bool(cmGeneratorFileSet const*)> include,
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cmGeneratorExpressionDAGChecker* dagChecker) const;
// file sets indexed by name
std::map<std::string, std::unique_ptr<cmGeneratorFileSet>> FileSets;
std::vector<cmGeneratorFileSet const*> AllFileSets;
@@ -75,10 +94,16 @@ private:
std::unordered_map<cm::string_view, std::vector<cmGeneratorFileSet const*>>
InterfaceFileSets;
mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists;
bool MaybeHaveInterfaceProperty(cm::string_view type,
std::string const& prop,
cm::GenEx::Evaluation* eval) const;
struct InfoByConfig
{
bool BuiltCache = false;
std::map<std::string, cmGeneratorFileSet const*> FileSetCache;
std::map<std::string, cmGeneratorFileSet const*> InterfaceFileSetCache;
};
mutable std::map<std::string, InfoByConfig> Configs;
+5 -10
View File
@@ -92,12 +92,11 @@ static void CreatePropertyGeneratorExpressions(
cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
: Target(t)
, Makefile(t->GetMakefile())
, LocalGenerator(lg)
, GlobalGenerator(lg->GetGlobalGenerator())
, FileSets(cm::make_unique<cmGeneratorFileSets>(this, lg))
{
this->Makefile = this->Target->GetMakefile();
this->LocalGenerator = lg;
this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator();
this->GlobalGenerator->ComputeTargetObjectDirectory(this);
CreatePropertyGeneratorExpressions(*lg->GetCMakeInstance(),
@@ -5884,12 +5883,8 @@ bool cmGeneratorTarget::HaveFortranSources() const
bool cmGeneratorTarget::HaveCxx20ModuleSources() const
{
auto const& fileSets = this->GetAllFileSets();
return std::any_of(fileSets.begin(), fileSets.end(),
[](cmGeneratorFileSet const* file_set) -> bool {
auto const& fs_type = file_set->GetType();
return fs_type == cm::FileSetMetadata::CXX_MODULES;
});
return !this->GetFileSets(cm::FileSetMetadata::CXX_MODULES).empty() ||
!this->GetInterfaceFileSets(cm::FileSetMetadata::CXX_MODULES).empty();
}
cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
+16 -2
View File
@@ -244,8 +244,12 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
}
// Collect this target's file sets.
cmGeneratorExpressionDAGChecker fsDagChecker{
this, "SOURCES", nullptr, nullptr, context,
};
cm::EvaluatedTargetPropertyEntries fileSetEntries;
AddFileSetEntries(this, this->FileSets.get(), context, &dagChecker,
AddFileSetEntries(this, this->FileSets.get(), context, &fsDagChecker,
fileSetEntries);
auto processFileSetEntry = [this, &config](cmSourceFile* sf) {
auto const* fileSet = this->GetFileSetForSource(config, sf);
@@ -273,9 +277,19 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
processSources(this, config, fileSetEntries, files, uniqueSrcs,
debugSources, processFileSetEntry);
// Collect file sets INTERFACE_SOURCES of all direct link-dependencies.
cm::EvaluatedTargetPropertyEntries linkInterfaceFileSetsEntries;
cm::AddInterfaceFileSetsEntries(this, cm::FileSetMetadata::SOURCES,
"INTERFACE_SOURCES", context, &fsDagChecker,
linkInterfaceFileSetsEntries);
bool contextDependentInterfaceFileSets =
processSources(this, config, linkInterfaceFileSetsEntries, files,
uniqueSrcs, debugSources);
// Determine if sources are context-dependent or not.
if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
!contextDependentObjects && !contextDependentFileSets) {
!contextDependentObjects && !contextDependentFileSets &&
!contextDependentInterfaceFileSets) {
this->SourcesAreContextDependent = Tribool::False;
} else {
this->SourcesAreContextDependent = Tribool::True;
+43
View File
@@ -10,15 +10,19 @@
#include <vector>
#include <cm/optional>
#include <cmext/string_view>
#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGhsMultiGenerator.h"
#include "cmLinkLineComputer.h" // IWYU pragma: keep
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
@@ -503,6 +507,34 @@ void cmGhsMultiTargetGenerator::WriteSourceProperty(
}
}
void cmGhsMultiTargetGenerator::WriteFileSetProperty(
std::ostream& fout, cmGeneratorFileSet const* fileSet,
std::string const& lang, cm::string_view propName, cm::string_view propFlag)
{
if (!fileSet) {
return;
}
std::vector<BT<std::string>> entries;
if (propName == "COMPILE_OPTIONS"_s) {
entries = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileOptions(this->ConfigName, lang)
: fileSet->GetInterfaceCompileOptions(this->ConfigName, lang);
} else if (propName == "COMPILE_DEFINITIONS"_s) {
entries = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileDefinitions(this->ConfigName, lang)
: fileSet->GetInterfaceCompileDefinitions(this->ConfigName, lang);
} else if (propName == "INCLUDE_DIRECTORIES"_s) {
entries = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetIncludeDirectories(this->ConfigName, lang)
: fileSet->GetInterfaceIncludeDirectories(this->ConfigName, lang);
}
for (auto const& entry : entries) {
fout << " " << propFlag << entry.Value << '\n';
}
}
void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
{
/* vector of all sources for this target */
@@ -642,9 +674,20 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
*fout << comment << fname << WriteObjectLangOverride(si) << '\n';
if (compile) {
// lookup for the associated file set, if any.
auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
this->ConfigName, si);
this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(),
"INCLUDE_DIRECTORIES"_s, "");
this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I");
this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D");
this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(),
"COMPILE_DEFINITIONS"_s, "");
this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", "");
this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(),
"COMPILE_OPTIONS"_s, "");
/* to avoid clutter in the GUI only print out the objectName if it
* has been renamed */
+7
View File
@@ -8,6 +8,8 @@
#include <string>
#include <vector>
#include <cm/string_view>
#include "cmGhsMultiGpj.h"
class cmCustomCommand;
@@ -17,6 +19,7 @@ class cmGlobalGhsMultiGenerator;
class cmLocalGhsMultiGenerator;
class cmMakefile;
class cmSourceFile;
class cmGeneratorFileSet;
class cmGhsMultiTargetGenerator
{
@@ -65,6 +68,10 @@ private:
void WriteSourceProperty(std::ostream& fout, cmSourceFile const* sf,
std::string const& propName,
std::string const& propFlag);
void WriteFileSetProperty(std::ostream& fout,
cmGeneratorFileSet const* fileSet,
std::string const& lang, cm::string_view propName,
cm::string_view propFlag);
static std::string WriteObjectLangOverride(cmSourceFile const* sourceFile);
bool DetermineIfIntegrityApp();
+36 -1
View File
@@ -28,6 +28,8 @@
#include "cmCustomCommandTypes.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGeneratorFactory.h"
@@ -1091,6 +1093,12 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
break;
}
// lookup for the associated file set, if any.
//// sources are independent of the config but needed here
auto const& config = this->CurrentConfigurationTypes[0];
auto const* fileSet =
gtgt->GetGeneratorFileSets()->GetFileSetForSource(config, sf);
// Explicitly add the explicit language flag before any other flag
// so user flags can override it.
gtgt->AddExplicitLanguageFlags(flags, *sf);
@@ -1104,6 +1112,15 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
lg->AppendCompileOptions(
flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
}
// Add flags from file set properties.
if (fileSet) {
auto options = fileSet->BelongsTo(gtgt)
? fileSet->GetCompileOptions(config, lang)
: fileSet->GetInterfaceCompileOptions(config, lang);
if (!options.empty()) {
lg->AppendCompileOptions(flags, cm::remove_BT(options));
}
}
// Add per-source definitions.
BuildObjectListOrString flagsBuild(this, false);
@@ -1114,6 +1131,15 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS).c_str(),
true);
}
// Add file set preprocessor definitions
if (fileSet) {
auto fsDefines = fileSet->BelongsTo(gtgt)
? fileSet->GetCompileDefinitions(config, lang)
: fileSet->GetInterfaceCompileDefinitions(config, lang);
if (!fsDefines.empty()) {
this->AppendDefines(flagsBuild, cm::remove_BT(fsDefines), true);
}
}
if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
@@ -1126,8 +1152,17 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
flags += flagsBuild.GetString();
}
// Add per-source include directories.
std::vector<std::string> includes;
// Add include directories from file set properties.
if (fileSet) {
auto fsIncludes = fileSet->BelongsTo(gtgt)
? fileSet->GetIncludeDirectories(config, lang)
: fileSet->GetInterfaceIncludeDirectories(config, lang);
if (!fsIncludes.empty()) {
lg->AppendIncludeDirectories(includes, cm::remove_BT(fsIncludes), *sf);
}
}
// Add per-source include directories.
std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
if (cmValue cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
lg->AppendIncludeDirectories(
+1 -1
View File
@@ -13,7 +13,6 @@
#include <cm/optional>
#include <cm/string_view>
#include <cmext/type_traits>
#include "cmList.h"
#include "cmStack.h"
@@ -220,6 +219,7 @@ template <typename T>
std::vector<T> remove_BT(std::vector<BT<T>> const& container)
{
std::vector<T> result;
result.reserve(container.size());
for (auto const& entry : container) {
result.emplace_back(entry.Value);
}
+41 -1
View File
@@ -24,6 +24,8 @@
#include "cmCustomCommandLines.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -1496,6 +1498,9 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo(
}
cmGeneratorExpressionInterpreter genexInterpreter(lg, config, gt, lang);
// lookup for the associated file set, if any.
auto const* fileSet =
gt->GetGeneratorFileSets()->GetFileSetForSource(config, &sf);
bool needfc = false;
if (!objectName.empty()) {
@@ -1514,6 +1519,15 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo(
genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
needfc = true;
}
// Add flags from file set properties.
if (fileSet) {
auto options = fileSet->BelongsTo(gt)
? fileSet->GetCompileOptions(config, lang)
: fileSet->GetInterfaceCompileOptions(config, lang);
if (!options.empty()) {
lg->AppendCompileOptions(fc.CompileFlags, cm::remove_BT(options));
}
}
// Add precompile headers compile options.
std::string const pchSource = gt->GetPchSource(config, lang);
if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
@@ -1570,10 +1584,36 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo(
genexInterpreter.Evaluate(*ccdefs, COMPILE_DEFINITIONS);
needfc = true;
}
// Add file set preprocessor definitions
if (fileSet) {
auto defines = fileSet->BelongsTo(gt)
? fileSet->GetCompileDefinitions(config, lang)
: fileSet->GetInterfaceCompileDefinitions(config, lang);
if (!defines.empty()) {
if (!fc.CompileDefs.empty()) {
fc.CompileDefs += ';';
}
fc.CompileDefs += cmList::to_string(defines);
needfc = true;
}
}
// Add file set include directories definitions
if (fileSet) {
auto includes = fileSet->BelongsTo(gt)
? fileSet->GetIncludeDirectories(config, lang)
: fileSet->GetInterfaceIncludeDirectories(config, lang);
if (!includes.empty()) {
fc.IncludeDirs = cmList::to_string(includes);
needfc = true;
}
}
std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
if (cmValue cincs = sf.GetProperty(INCLUDE_DIRECTORIES)) {
fc.IncludeDirs = genexInterpreter.Evaluate(*cincs, INCLUDE_DIRECTORIES);
if (!fc.IncludeDirs.empty()) {
fc.IncludeDirs += ';';
}
fc.IncludeDirs += genexInterpreter.Evaluate(*cincs, INCLUDE_DIRECTORIES);
needfc = true;
}
+66 -28
View File
@@ -29,11 +29,13 @@
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalUnixMakefileGenerator3.h"
#include "cmLinkLineComputer.h" // IWYU pragma: keep
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalCommonGenerator.h"
#include "cmLocalGenerator.h"
#include "cmLocalUnixMakefileGenerator3.h"
@@ -55,7 +57,6 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
#include "cmake.h"
@@ -346,18 +347,6 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
}
}
std::map<std::string, std::string> file_set_map;
auto const* tgt = this->GeneratorTarget->Target;
for (auto const* file_set : this->GeneratorTarget->GetAllFileSets()) {
auto files = file_set->GetFiles(context, this->GeneratorTarget).first;
for (auto const& it : files) {
for (auto const& filename : it.second) {
file_set_map[filename] = file_set->GetType();
}
}
}
std::vector<cmSourceFile const*> objectSources;
this->GeneratorTarget->GetObjectSources(objectSources,
this->GetConfigName());
@@ -372,20 +361,17 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
}
for (cmSourceFile const* sf : objectSources) {
auto const& path = sf->GetFullPath();
auto const it = file_set_map.find(path);
if (it != file_set_map.end()) {
auto const& file_set_type = it->second;
if (file_set_type == cm::FileSetMetadata::CXX_MODULES) {
if (sf->GetLanguage() != "CXX"_s) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"Target \"", tgt->GetName(), "\" contains the source\n ", path,
"\nin a file set of type \"", file_set_type,
R"(" but the source is not classified as a "CXX" source.)"));
}
}
cmGeneratorFileSet const* fileSet =
this->GeneratorTarget->GetFileSetForSource(this->GetConfigName(), sf);
if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES &&
sf->GetLanguage() != "CXX"_s) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Target \"", this->GeneratorTarget->GetName(),
"\" contains the source\n ", sf->GetFullPath(),
"\nin a file set of type \"",
cm::FileSetMetadata::CXX_MODULES,
R"(" but the source is not classified as a "CXX" source.)"));
}
}
}
@@ -765,6 +751,11 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
ispcHeaderRelative, cmOutputConverter::SHELL);
}
// lookup for the associated file set, if any.
auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
config, &source);
// Add flags from source file properties.
std::string const COMPILE_FLAGS("COMPILE_FLAGS");
if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) {
@@ -786,6 +777,22 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
<< "\n";
}
// Add flags from file set properties.
if (fileSet) {
auto options = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileOptions(config, lang)
: fileSet->GetInterfaceCompileOptions(config, lang);
if (!options.empty()) {
this->LocalGenerator->AppendCompileOptions(flags,
cm::remove_BT(options));
*this->FlagFileStream << "# Custom options (File Set '"
<< fileSet->GetName() << "'): " << relativeObj
<< "_OPTIONS = " << cmList::to_string(options)
<< "\n"
<< "\n";
}
}
// Add precompile headers compile options.
if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
std::string pchOptions;
@@ -807,9 +814,25 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
<< "\n";
}
// Add include directories from source file properties.
// Add include directories from file set properties.
std::vector<std::string> includes;
if (fileSet) {
auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetIncludeDirectories(config, lang)
: fileSet->GetInterfaceIncludeDirectories(config, lang);
if (!fsIncludes.empty()) {
this->LocalGenerator->AppendIncludeDirectories(
includes, cm::remove_BT(fsIncludes), source);
*this->FlagFileStream
<< "# Custom include directories (File Set '" << fileSet->GetName()
<< "'): " << relativeObj
<< "_INCLUDE_DIRECTORIES = " << cmList::to_string(fsIncludes) << "\n"
<< "\n";
}
}
// Add include directories from source file properties.
std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
std::string const& evaluatedIncludes =
@@ -846,6 +869,21 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
<< "\n";
}
// Add file set preprocessor definitions
if (fileSet) {
auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileDefinitions(config, lang)
: fileSet->GetInterfaceCompileDefinitions(config, lang);
if (!fsDefines.empty()) {
this->LocalGenerator->AppendDefines(defines, fsDefines);
*this->FlagFileStream << "# Custom defines (File Set '"
<< fileSet->GetName() << "'): " << relativeObj
<< "_DEFINES = " << cmList::to_string(fsDefines)
<< "\n"
<< "\n";
}
}
// Get the output paths for source and object files.
std::string const sourceFile = this->LocalGenerator->ConvertToOutputFormat(
source.GetFullPath(), cmOutputConverter::SHELL);
+37
View File
@@ -31,6 +31,7 @@
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalCommonGenerator.h"
@@ -238,6 +239,18 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
}
if (auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
config, source)) {
auto options = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileOptions(config, language)
: fileSet->GetInterfaceCompileOptions(config, language);
if (!options.empty()) {
this->LocalGenerator->AppendCompileOptions(flags,
cm::remove_BT(options));
}
}
// Add precompile headers compile options.
if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
std::string pchOptions;
@@ -331,6 +344,17 @@ std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source,
genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
}
if (auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
config, source)) {
auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetCompileDefinitions(config, language)
: fileSet->GetInterfaceCompileDefinitions(config, language);
if (!fsDefines.empty()) {
this->LocalGenerator->AppendDefines(defines, fsDefines);
}
}
std::string definesString = this->GetDefines(language, config);
this->LocalGenerator->JoinDefines(defines, definesString, language);
@@ -342,6 +366,19 @@ std::string cmNinjaTargetGenerator::ComputeIncludes(
std::string const& config)
{
std::vector<std::string> includes;
if (auto const* fileSet =
this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(
config, source)) {
auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget)
? fileSet->GetIncludeDirectories(config, language)
: fileSet->GetInterfaceIncludeDirectories(config, language);
if (!fsIncludes.empty()) {
this->LocalGenerator->AppendIncludeDirectories(
includes, cm::remove_BT(fsIncludes), *source);
}
}
cmGeneratorExpressionInterpreter genexInterpreter(
this->LocalGenerator, config, this->GeneratorTarget, language);
+6
View File
@@ -714,6 +714,12 @@ cmTargetInternals::cmTargetInternals()
"Header"_s, "The default header set"_s, "Header set"_s,
FileSetEntries{ "HEADER_SETS"_s },
FileSetEntries{ "INTERFACE_HEADER_SETS"_s } } },
{ cm::FileSetMetadata::SOURCES,
{ cm::FileSetMetadata::SOURCES, "SOURCE_DIRS"_s,
"SOURCE_SET"_s, "SOURCE_DIRS_"_s, "SOURCE_SET_"_s,
"Source"_s, "The default source set"_s, "Source set"_s,
FileSetEntries{ "SOURCE_SETS"_s },
FileSetEntries{ "INTERFACE_SOURCE_SETS"_s } } },
{ cm::FileSetMetadata::CXX_MODULES,
{ cm::FileSetMetadata::CXX_MODULES, "CXX_MODULE_DIRS"_s,
"CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s,
+12 -2
View File
@@ -284,7 +284,7 @@ bool TargetSourcesImpl::HandleOneFileSet(
this->SetError(
cmStrCat(R"(File set TYPE ")", cm::FileSetMetadata::CXX_MODULES,
R"(" may not have "PUBLIC" )"
R"(or "PRIVATE" visibility on INTERFACE libraries.)"));
R"(or "PRIVATE" scope on INTERFACE libraries.)"));
return false;
}
}
@@ -297,11 +297,21 @@ bool TargetSourcesImpl::HandleOneFileSet(
if (type == cm::FileSetMetadata::CXX_MODULES) {
this->SetError(cmStrCat(R"(File set TYPE ")",
cm::FileSetMetadata::CXX_MODULES,
R"(" may not have "INTERFACE" visibility)"));
R"(" may not have "INTERFACE" scope)"));
return false;
}
}
if (cm::FileSetMetadata::VisibilityIsForSelf(visibility) &&
this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
type == cm::FileSetMetadata::SOURCES) {
this->SetError(
cmStrCat(R"(File set TYPE ")", cm::FileSetMetadata::SOURCES,
R"(" may not have "PUBLIC" )"
R"(or "PRIVATE" scope on INTERFACE libraries.)"));
return false;
}
if (args.BaseDirs.empty()) {
args.BaseDirs.emplace_back(this->Makefile->GetCurrentSourceDirectory());
}
+48 -9
View File
@@ -31,6 +31,7 @@
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorFileSet.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorOptions.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -2883,14 +2884,36 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
configDefines += *ccdefs;
}
// lookup for the associated file set, if any.
auto const* fileSet =
this->GeneratorTarget->GetFileSetForSource(config, source);
bool fileSetBelongsToTarget = false;
std::vector<std::string> fsOptions;
std::vector<std::string> fsDefines;
std::vector<std::string> fsIncludes;
if (fileSet) {
fileSetBelongsToTarget = fileSet->BelongsTo(this->GeneratorTarget);
fsOptions =
cm::remove_BT(fileSetBelongsToTarget
? fileSet->GetCompileOptions(config, lang)
: fileSet->GetInterfaceCompileOptions(config, lang));
fsDefines = cm::remove_BT(
fileSetBelongsToTarget
? fileSet->GetCompileDefinitions(config, lang)
: fileSet->GetInterfaceCompileDefinitions(config, lang));
fsIncludes = cm::remove_BT(
fileSetBelongsToTarget
? fileSet->GetIncludeDirectories(config, lang)
: fileSet->GetInterfaceIncludeDirectories(config, lang));
}
bool const shouldScanForModules = lang == "CXX"_s &&
this->GeneratorTarget->NeedDyndepForSource(lang, config, source);
auto const* fs =
this->GeneratorTarget->GetFileSetForSource(config, source);
char const* compileAsPerConfig = compileAs;
if (fs && fs->GetType() == cm::FileSetMetadata::CXX_MODULES) {
if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) {
if (lang == "CXX"_s) {
if (fs->GetType() == cm::FileSetMetadata::CXX_MODULES) {
if (fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) {
isCppModule = true;
if (shouldScanForModules &&
this->GlobalGenerator->IsScanDependenciesSupported()) {
@@ -2909,7 +2932,7 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
cmStrCat(
"Target \"", this->GeneratorTarget->Target->GetName(),
"\" contains the source\n ", source->GetFullPath(),
"\nin a file set of type \"", fs->GetType(),
"\nin a file set of type \"", fileSet->GetType(),
R"(" but the source is not classified as a "CXX" source.)"));
}
}
@@ -2935,9 +2958,10 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
// if we have flags or defines for this config then
// use them
if (!flags.empty() || !options.empty() || !configDefines.empty() ||
!includes.empty() || compileAsPerConfig || noWinRT ||
!options.empty() || needsPCHFlags ||
if (!flags.empty() || !options.empty() || !fsOptions.empty() ||
!configDefines.empty() || !fsDefines.empty() || !includes.empty() ||
!fsIncludes.empty() || compileAsPerConfig || noWinRT ||
needsPCHFlags ||
(shouldScanForModules !=
this->ScanSourceForModuleDependencies[config])) {
cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
@@ -3010,8 +3034,8 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
}
}
std::string expandedOptions;
if (!options.empty()) {
std::string expandedOptions;
if (configDependentOptions) {
this->LocalGenerator->AppendCompileOptions(
expandedOptions,
@@ -3019,6 +3043,12 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
} else {
this->LocalGenerator->AppendCompileOptions(expandedOptions, options);
}
}
// Add flags from file set properties.
if (!fsOptions.empty()) {
this->LocalGenerator->AppendCompileOptions(expandedOptions, fsOptions);
}
if (!expandedOptions.empty()) {
clOptions.Parse(expandedOptions);
}
if (clOptions.HasFlag("DisableSpecificWarnings")) {
@@ -3034,7 +3064,16 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
} else {
clOptions.AddDefines(configDefines);
}
// Add defines from file set properties.
if (!fsDefines.empty()) {
clOptions.AddDefines(fsDefines);
}
std::vector<std::string> includeList;
// Add include directories from file set properties.
if (!fsIncludes.empty()) {
this->LocalGenerator->AppendIncludeDirectories(includeList, fsIncludes,
*source);
}
if (configDependentIncludes) {
this->LocalGenerator->AppendIncludeDirectories(
includeList,
+2
View File
@@ -623,6 +623,8 @@ 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(FileSet-SOURCES -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DMSVC_VERSION=${MSVC_VERSION})
add_RunCMake_test(ToolchainFile)
if(NOT CMake_TEST_EXTERNAL_CMAKE)
@@ -1,5 +1,4 @@
CMake Error at FileSetModulesInterfaceOnInterface\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE"
visibility
target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
@@ -1,5 +1,4 @@
CMake Error at FileSetModulesInterfaceOnStatic\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE"
visibility
target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
@@ -1,5 +1,5 @@
^CMake Error at FileSetModulesPrivateOnInterface\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or
"PRIVATE" visibility on INTERFACE libraries\.
"PRIVATE" scope on INTERFACE libraries\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -1,5 +1,5 @@
^CMake Error at FileSetModulesPublicOnInterface\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or
"PRIVATE" visibility on INTERFACE libraries\.
"PRIVATE" scope on INTERFACE libraries\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 4.0)
project(${RunCMake_TEST} LANGUAGES NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
@@ -0,0 +1,9 @@
if(RunCMake_GENERATOR MATCHES "Xcode")
if (NOT actual_stdout MATCHES "-DOPT_SRC +-DOPT_FS")
set (RunCMake_TEST_FAILED "Wrong compile options order.")
endif()
else()
if (NOT actual_stdout MATCHES "(/|-)D *OPT_TGT.+(/|-)D *OPT_SRC.+(/|-)D *OPT_FS")
set (RunCMake_TEST_FAILED "Wrong compile options order.")
endif()
endif()
@@ -0,0 +1,15 @@
enable_language(C)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS OFF)
# ensure no temp file will be used
string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}")
string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}")
add_library(lib1 STATIC)
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib1.c)
set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_FS)
set_property(TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_TGT)
set_property(SOURCE lib1.c PROPERTY COMPILE_OPTIONS -DOPT_SRC)
@@ -0,0 +1,10 @@
enable_language(C)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/custom.c"
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c"
"${CMAKE_CURRENT_BINARY_DIR}/custom.c")
add_library(lib1 STATIC)
target_sources(lib1 PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}"
FILES "${CMAKE_CURRENT_BINARY_DIR}/custom.c")
@@ -0,0 +1 @@
1
@@ -0,0 +1,4 @@
CMake Error at FileNoExist\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
.+/Tests/RunCMake/FileSet-SOURCES/noexist\.c
@@ -0,0 +1,4 @@
enable_language(C)
add_library(lib1 STATIC lib1.c)
target_sources(lib1 PRIVATE FILE_SET SOURCES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES noexist.c)
@@ -0,0 +1,19 @@
enable_language(C)
add_library(lib1 STATIC)
target_sources(lib1 PRIVATE lib1.c)
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib2.c)
set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_DEFINITIONS LIB1_A)
set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1")
set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_A)
set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2")
add_executable(main main.c)
target_link_libraries(main PRIVATE lib1)
target_compile_definitions(main PRIVATE CONSUMER)
add_library(lib2 SHARED)
target_link_libraries(lib2 PRIVATE lib1)
target_compile_definitions(lib2 PRIVATE CONSUMER)
@@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "(/|-)D *OPT_FS" OR actual_stdout MATCHES "(/|-)D *OPT_INTERFACE_FS")
set (RunCMake_TEST_FAILED "Wrong compile options specified.")
endif()
@@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "(/|-)D *OPT_INTERFACE_FS" OR actual_stdout MATCHES "(/|-)D *OPT_FS")
set (RunCMake_TEST_FAILED "Wrong compile options specified.")
endif()
@@ -0,0 +1,17 @@
enable_language(C)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS OFF)
# ensure no temp file will be used
string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}")
string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}")
add_library(lib1 STATIC)
target_sources(lib1 PRIVATE lib1.c)
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib4.c)
set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_OPTIONS -DOPT_FS)
set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_OPTIONS -DOPT_INTERFACE_FS)
add_executable(main main.c)
target_link_libraries(main PRIVATE lib1)
@@ -0,0 +1,17 @@
enable_language(C)
add_library(lib1 STATIC)
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib2.c)
set_property(FILE_SET a TARGET lib1 PROPERTY COMPILE_DEFINITIONS LIB1_A)
set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1")
set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_COMPILE_DEFINITIONS INTERFACE_LIB1_A)
set_property(FILE_SET a TARGET lib1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2")
add_library(lib2 STATIC lib1.c)
target_link_libraries(lib2 INTERFACE lib1)
add_executable(main main.c)
target_link_libraries(main PRIVATE lib2)
target_compile_definitions(main PRIVATE CONSUMER)
@@ -0,0 +1,11 @@
enable_language(C)
add_library(lib1 STATIC)
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES FILES lib3.c)
set_property(FILE_SET a TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir")
if(NOT CMAKE_GENERATOR MATCHES "Xcode")
set_property(TARGET lib1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir1")
endif()
set_property(SOURCE lib3.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/subdir2")
@@ -0,0 +1,43 @@
include(RunCMake)
function(run_and_build test)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(config_options "-DCMAKE_CONFIGURATION_TYPES=Debug")
else()
set(config_options -DCMAKE_BUILD_TYPE=Debug)
endif()
set(RunCMake_TEST_OPTIONS ${config_options})
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build")
run_cmake_with_options(${test})
set(RunCMake_TEST_NO_CLEAN 1)
if(ARGN)
foreach(target IN LISTS ARGN)
run_cmake_command(${test}-${target} ${CMAKE_COMMAND} --build . --config Debug --target ${target} --verbose)
endforeach()
else()
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug --verbose)
endif()
endfunction()
function(run_and_check test)
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
run_and_build(${test} ${ARGN})
endfunction()
run_cmake(FileNoExist)
run_cmake(TargetProperties)
run_and_build(FileSetProperties)
run_and_build(CustomCommandInput)
run_and_build(IncludeDirectoriesOrder)
run_and_build(FileSetTransitivity)
run_and_check(CompileOptionsOrder)
# Some environments are excluded because they are not able to honor verbose mode
if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode"
OR (RunCMake_GENERATOR MATCHES "Visual Studio" AND MSVC_VERSION GREATER_EQUAL "1600"))
AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
run_and_check(FileSetProperties2 lib1 main)
endif()
@@ -0,0 +1,61 @@
enable_language(C)
function(assert_prop_undef tgt prop)
unset(actual_value)
get_property(actual_value TARGET ${tgt} PROPERTY ${prop})
if(DEFINED actual_value)
message(SEND_ERROR "${prop} should be undefined, actual value:\n ${actual_value}")
endif()
endfunction()
function(assert_prop_eq tgt prop value)
unset(actual_value)
get_property(actual_value TARGET ${tgt} PROPERTY ${prop})
if(NOT actual_value STREQUAL value)
message(SEND_ERROR "Expected value of ${prop}:\n ${value}\nActual value:\n ${actual_value}")
endif()
endfunction()
add_library(lib1 STATIC)
assert_prop_eq(lib1 SOURCE_SETS "")
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "")
target_sources(lib1 PUBLIC FILE_SET a TYPE SOURCES BASE_DIRS "." FILES lib1.c lib2.c)
assert_prop_eq(lib1 SOURCE_SETS "a")
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a")
assert_prop_eq(lib1 SOURCE_DIRS_a "${CMAKE_CURRENT_SOURCE_DIR}/.")
assert_prop_eq(lib1 SOURCE_SET_a "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c;${CMAKE_CURRENT_SOURCE_DIR}/lib2.c")
target_sources(lib1 PUBLIC FILE_SET a FILES lib3.c)
assert_prop_eq(lib1 SOURCE_SETS "a")
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a")
assert_prop_eq(lib1 SOURCE_DIRS_a "${CMAKE_CURRENT_SOURCE_DIR}/.")
assert_prop_eq(lib1 SOURCE_SET_a "${CMAKE_CURRENT_SOURCE_DIR}/lib1.c;${CMAKE_CURRENT_SOURCE_DIR}/lib2.c;${CMAKE_CURRENT_SOURCE_DIR}/lib3.c")
target_sources(lib1 PRIVATE FILE_SET b TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/dir" FILES dir/dir.c)
assert_prop_eq(lib1 SOURCE_SETS "a;b")
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a")
assert_prop_eq(lib1 SOURCE_DIRS_b "${CMAKE_CURRENT_SOURCE_DIR}/dir")
assert_prop_eq(lib1 SOURCE_SET_b "${CMAKE_CURRENT_SOURCE_DIR}/dir/dir.c")
target_sources(lib1 INTERFACE FILE_SET c TYPE SOURCES FILE_SET d TYPE SOURCES)
assert_prop_eq(lib1 SOURCE_SETS "a;b")
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d")
assert_prop_eq(lib1 SOURCE_DIRS_c "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET_c "")
assert_prop_eq(lib1 SOURCE_DIRS_d "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET_d "")
target_sources(lib1 PUBLIC FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES lib4.c)
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d;SOURCES")
assert_prop_eq(lib1 SOURCE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c")
assert_prop_eq(lib1 SOURCE_DIRS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c")
target_sources(lib1 PUBLIC FILE_SET SOURCES FILES lib5.c)
assert_prop_eq(lib1 INTERFACE_SOURCE_SETS "a;c;d;SOURCES")
assert_prop_eq(lib1 SOURCE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c;${CMAKE_CURRENT_SOURCE_DIR}/lib5.c")
assert_prop_eq(lib1 SOURCE_DIRS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}")
assert_prop_eq(lib1 SOURCE_SET_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib4.c;${CMAKE_CURRENT_SOURCE_DIR}/lib5.c")
+14
View File
@@ -0,0 +1,14 @@
#if defined(INTERFACE_LIB1_A)
# error "INTERFACE_LIB1_A defined"
#endif
#if defined(LIB1_A)
# error "LIB1_A defined"
#endif
#if defined(CONSUMER)
# error "CONSUMER defined"
#endif
void f1(void)
{
}
+24
View File
@@ -0,0 +1,24 @@
#if defined(CONSUMER)
# if defined(LIB1_A)
# error "LIB1_A defined"
# endif
# if !defined(INTERFACE_LIB1_A)
# error "INTERFACE_LIB1_A not defined"
# endif
# include "h2.h"
#else
# if !defined(LIB1_A)
# error "LIB1_A not defined"
# endif
# if defined(INTERFACE_LIB1_A)
# error "INTERFACE_LIB1_A defined"
# endif
# include "h1.h"
#endif
void f2(void)
{
}
+7
View File
@@ -0,0 +1,7 @@
#include "h3.h"
void f3(void)
{
}
+4
View File
@@ -0,0 +1,4 @@
void f2(void)
{
}
+9
View File
@@ -0,0 +1,9 @@
extern void f2(void);
int main(void)
{
f2();
return 0;
}
@@ -0,0 +1,2 @@
#error "TARGET INCLUDE_DIRECTORIES"
@@ -0,0 +1,2 @@
#error "SOURCE INCLUDE_DIRECTORIES"
+4 -2
View File
@@ -1,3 +1,5 @@
enable_language(C)
macro(test_target_property PROP)
add_custom_target(CustomTarget)
set_property(TARGET CustomTarget PROPERTY ${PROP} x)
@@ -28,8 +30,8 @@ macro(test_directory_property PROP)
endmacro()
macro(test_file_set_property PROP)
add_library(foo INTERFACE)
target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS)
add_library(foo STATIC foo.c)
target_sources(foo PRIVATE FILE_SET foo TYPE HEADERS)
set_property(FILE_SET foo TARGET foo PROPERTY ${PROP} x)
set_property(FILE_SET foo TARGET foo PROPERTY ${PROP})
set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP})
View File
@@ -1,5 +1,5 @@
^CMake Error at FileSetDefaultWrongType\.cmake:[0-9]+ \(target_sources\):
target_sources FILE_SET names starting with a capital letter are reserved
for built-in file sets and may only be "HEADERS", "CXX_MODULES"
for built-in file sets and may only be "HEADERS", "SOURCES", "CXX_MODULES"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,12 @@
CMake Error at FileSetIncompatibleScopes\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "CXX_MODULES" may not have "PUBLIC" or
"PRIVATE" scope on INTERFACE libraries\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)
CMake Error at FileSetIncompatibleScopes\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE "SOURCES" may not have "PUBLIC" or "PRIVATE"
scope on INTERFACE libraries\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)
@@ -0,0 +1,5 @@
enable_language(C)
add_library(lib1 INTERFACE)
target_sources(lib1 PUBLIC FILE_SET CXX_MODULES FILES lib1.c)
target_sources(lib1 PUBLIC FILE_SET SOURCES FILES lib2.c)
@@ -1,4 +1,5 @@
^CMake Error at FileSetWrongType\.cmake:[0-9]+ \(target_sources\):
target_sources File set TYPE may only be "HEADERS", "CXX_MODULES"
target_sources File set TYPE may only be "HEADERS", "SOURCES",
"CXX_MODULES"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -26,6 +26,7 @@ run_cmake(FileSetProperties)
run_cmake(FileSetNoType)
run_cmake(FileSetWrongType)
run_cmake(FileSetDefaultWrongType)
run_cmake(FileSetIncompatibleScopes)
run_cmake(FileSetChangeScope)
run_cmake(FileSetChangeType)
run_cmake(FileSetWrongBaseDirs)