Merge topic 'genex-link-properties'

ddb9442f48 GenEx: Fix TARGET_PROPERTY evaluation of transitive link properties
862b8e28ad GenEx: Teach TARGET_PROPERTY evaluation to optionally pierce LINK_ONLY
8d1d6a1437 Tests: Cover TARGET_PROPERTY genex evaluation of transitive link properties
abf607c2ec Tests: Cover TARGET_PROPERTY genex evaluation of transitive build properties
7d3d728a72 Help: Clarify CMP0099 documentation and summary text
79a3ae9a0d cmGeneratorExpressionDAGChecker: Simplify transitive property table
e8010b67c7 cmGeneratorExpressionDAGChecker: Make local generator available in constructor
b36fb3f6f1 cmGeneratorExpressionNode: Remove outdated lint suppression

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !9473
This commit is contained in:
Brad King
2024-05-01 14:05:53 +00:00
committed by Kitware Robot
32 changed files with 431 additions and 167 deletions
+13 -4
View File
@@ -1282,7 +1282,7 @@ Compile Context
.. versionadded:: 3.27
Content of ``...``, when collecting
:ref:`usage requirements <Target Usage Requirements>`,
:ref:`transitive build properties <Transitive Build Properties>`,
otherwise it is the empty string. This is intended for use in an
:prop_tgt:`INTERFACE_LINK_LIBRARIES` and :prop_tgt:`LINK_LIBRARIES` target
properties, typically populated via the :command:`target_link_libraries` command.
@@ -1670,8 +1670,8 @@ Link Context
.. versionadded:: 3.1
Content of ``...``, except while collecting
:ref:`usage requirements <Target Usage Requirements>`,
Content of ``...``, except while collecting usage requirements from
:ref:`transitive build properties <Transitive Build Properties>`,
in which case it is the empty string. This is intended for use in an
:prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
via the :command:`target_link_libraries` command, to specify private link
@@ -1788,7 +1788,16 @@ The expressions have special evaluation rules for some properties:
of the value on the target itself with the values of the same properties on
targets named by the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`.
Evaluation is transitive over the closure of the target's
:prop_tgt:`INTERFACE_LINK_LIBRARIES`.
:prop_tgt:`INTERFACE_LINK_LIBRARIES`:
* For :ref:`Transitive Build Properties`, the transitive closure
*excludes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
by the :genex:`LINK_ONLY` generator expression.
* For :ref:`Transitive Link Properties`, the transitive closure is
*includes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
by the :genex:`LINK_ONLY` generator expression.
See policy :policy:`CMP0166`.
Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive.
+2 -1
View File
@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.30
.. toctree::
:maxdepth: 1
CMP0166: TARGET_PROPERTY evaluates link properties transitively over private dependencies of static libraries. </policy/CMP0166>
CMP0165: enable_language() must not be called before project(). </policy/CMP0165>
CMP0164: add_library() rejects SHARED libraries when not supported by the platform. </policy/CMP0164>
CMP0163: The GENERATED source file property is now visible in all directories. </policy/CMP0163>
@@ -214,7 +215,7 @@ Policies Introduced by CMake 3.17
CMP0102: mark_as_advanced() does nothing if a cache entry does not exist. </policy/CMP0102>
CMP0101: target_compile_options honors BEFORE keyword in all scopes. </policy/CMP0101>
CMP0100: Let AUTOMOC and AUTOUIC process .hh header files. </policy/CMP0100>
CMP0099: Link properties are transitive over private dependency on static libraries. </policy/CMP0099>
CMP0099: Link properties are transitive over private dependencies of static libraries. </policy/CMP0099>
CMP0098: FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing. </policy/CMP0098>
Policies Introduced by CMake 3.16
+14 -5
View File
@@ -3,13 +3,16 @@ CMP0099
.. versionadded:: 3.17
Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DEPENDS`
are now transitive over private dependencies of static libraries.
Link properties are transitive over private dependencies of static libraries.
In CMake 3.16 and below the interface link properties attached to libraries
are not propagated for private dependencies of static libraries.
In CMake 3.16 and below, evaluation of target properties
:prop_tgt:`INTERFACE_LINK_OPTIONS`, :prop_tgt:`INTERFACE_LINK_DIRECTORIES`,
and :prop_tgt:`INTERFACE_LINK_DEPENDS` during buildsystem generation does not
follow private dependencies of static libraries, which appear in their
:prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY` generator
expressions.
Only the libraries themselves are propagated to link the dependent binary.
CMake 3.17 and later prefer to propagate all interface link properties.
This policy provides compatibility for projects that have not been updated
to expect the new behavior.
@@ -18,6 +21,12 @@ The ``OLD`` behavior for this policy is to not propagate interface link
properties. The ``NEW`` behavior of this policy is to propagate interface link
properties.
.. versionadded:: 3.30
Policy :policy:`CMP0166` makes :genex:`TARGET_PROPERTY` evaluation of
these three transitive link properties follow private dependencies of
static libraries too.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
.. include:: STANDARD_ADVICE.txt
+40
View File
@@ -0,0 +1,40 @@
CMP0166
-------
.. versionadded:: 3.30
:genex:`TARGET_PROPERTY` evaluates link properties transitively over private
dependencies of static libraries.
In CMake 3.29 and below, the :genex:`TARGET_PROPERTY` generator expression
evaluates properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
as if they were :ref:`Transitive Build Properties` rather than
:ref:`Transitive Link Properties`, even when policy :policy:`CMP0099` is
set to ``NEW``. Private dependencies of static libraries, which appear in
their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY`
generator expressions, are not followed. This is inconsistent with
evaluation of the same target properties during buildsystem generation.
CMake 3.30 and above prefer that :genex:`TARGET_PROPERTY` evaluates
properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
as :ref:`Transitive Link Properties` such that private dependencies of static
libraries, which appear in their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
by :genex:`LINK_ONLY` generator expressions, are followed.
This policy provides compatibility for projects that have not been updated
to expect the new behavior.
The ``OLD`` behavior for this policy is for :genex:`TARGET_PROPERTY` to
evaluate properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS`
as if they were :ref:`Transitive Build Properties` by not following private
dependencies of static libraries. The ``NEW`` behavior for this policy is
to evaluate them as :ref:`Transitive Link Properties` by following private
dependencies of static libraries.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
.. include:: STANDARD_ADVICE.txt
.. include:: DEPRECATED.txt
@@ -0,0 +1,8 @@
genex-link-properties
---------------------
* The :genex:`TARGET_PROPERTY` generator expression now evaluates target
properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and
:prop_tgt:`INTERFACE_LINK_DEPENDS` correctly by following private
dependencies of static libraries. See policy :policy:`CMP0166`.
+2 -1
View File
@@ -501,7 +501,8 @@ std::string cmCommonTargetGenerator::GetLinkerLauncher(
cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
if (cmNonempty(launcherProp)) {
cmGeneratorExpressionDAGChecker dagChecker(this->GeneratorTarget, propName,
nullptr, nullptr);
nullptr, nullptr,
this->LocalCommonGenerator);
std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate(
*launcherProp, this->LocalCommonGenerator, config, this->GeneratorTarget,
&dagChecker, this->GeneratorTarget, lang);
+14 -7
View File
@@ -451,10 +451,14 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
if (cmValue feature = this->Target->GetProperty(key)) {
if (!feature->empty() && key.length() > lloPrefix.length()) {
auto item = key.substr(lloPrefix.length());
cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(),
this->Target,
"LINK_LIBRARY_OVERRIDE",
nullptr, nullptr };
cmGeneratorExpressionDAGChecker dag{
this->Target->GetBacktrace(),
this->Target,
"LINK_LIBRARY_OVERRIDE",
nullptr,
nullptr,
this->Target->GetLocalGenerator()
};
auto overrideFeature = cmGeneratorExpression::Evaluate(
*feature, this->Target->GetLocalGenerator(), config,
this->Target, &dag, this->Target, linkLanguage);
@@ -466,9 +470,12 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
// global override property
if (cmValue linkLibraryOverride =
this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
"LINK_LIBRARY_OVERRIDE", nullptr,
nullptr };
cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(),
target,
"LINK_LIBRARY_OVERRIDE",
nullptr,
nullptr,
target->GetLocalGenerator() };
auto overrideValue = cmGeneratorExpression::Evaluate(
*linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
target, linkLanguage);
+9 -9
View File
@@ -2,9 +2,11 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExportTryCompileFileGenerator.h"
#include <map>
#include <utility>
#include <cm/memory>
#include <cm/string_view>
#include "cmFileSet.h"
#include "cmGeneratorExpression.h"
@@ -44,12 +46,10 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os)
ImportPropertyMap properties;
for (std::string const& lang : this->Languages) {
#define FIND_TARGETS(PROPERTY) \
this->FindTargets("INTERFACE_" #PROPERTY, te, lang, emittedDeps);
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(FIND_TARGETS)
#undef FIND_TARGETS
for (auto i : cmGeneratorTarget::BuiltinTransitiveProperties) {
this->FindTargets(std::string(i.second.InterfaceName), te, lang,
emittedDeps);
}
}
this->PopulateProperties(te, properties, emittedDeps);
@@ -76,10 +76,10 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
// To please constraint checks of DAGChecker, this property must have
// LINK_OPTIONS property as parent
parentDagChecker = cm::make_unique<cmGeneratorExpressionDAGChecker>(
tgt, "LINK_OPTIONS", nullptr, nullptr);
tgt, "LINK_OPTIONS", nullptr, nullptr, tgt->GetLocalGenerator());
}
cmGeneratorExpressionDAGChecker dagChecker(tgt, propName, nullptr,
parentDagChecker.get());
cmGeneratorExpressionDAGChecker dagChecker(
tgt, propName, nullptr, parentDagChecker.get(), tgt->GetLocalGenerator());
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*prop);
+1 -1
View File
@@ -425,7 +425,7 @@ const std::string& cmGeneratorExpressionInterpreter::Evaluate(
cmGeneratorExpressionDAGChecker dagChecker(
this->HeadTarget,
property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr,
nullptr);
nullptr, this->LocalGenerator);
return this->CompiledGeneratorExpression->Evaluate(
this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
+14 -44
View File
@@ -2,10 +2,10 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGeneratorExpressionDAGChecker.h"
#include <cstring>
#include <sstream>
#include <utility>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
@@ -20,16 +20,17 @@
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
cmGeneratorTarget const* target, std::string property,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent)
cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG)
: cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target,
std::move(property), content, parent)
std::move(property), content, parent,
contextLG)
{
}
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
cmListFileBacktrace backtrace, cmGeneratorTarget const* target,
std::string property, const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent)
cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG)
: Parent(parent)
, Top(parent ? parent->Top : this)
, Target(target)
@@ -40,10 +41,9 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
if (parent) {
this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty;
} else {
#define TEST_TRANSITIVE_PROPERTY_METHOD(METHOD) this->METHOD() ||
this->TopIsTransitiveProperty = (CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
TEST_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(*)
#undef TEST_TRANSITIVE_PROPERTY_METHOD
this->TopIsTransitiveProperty =
this->Target->IsTransitiveProperty(this->Property, contextLG)
.has_value();
}
this->CheckResult = this->CheckGraph();
@@ -169,6 +169,12 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const
property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingSources() const
{
return this->Property == "SOURCES"_s ||
this->Property == "INTERFACE_SOURCES"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
{
cm::string_view property(this->Top->Property);
@@ -222,39 +228,3 @@ cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const
{
return this->Top->Target;
}
enum class TransitiveProperty
{
#define DEFINE_ENUM_ENTRY(NAME) NAME,
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)
#undef DEFINE_ENUM_ENTRY
Terminal
};
template <TransitiveProperty>
bool additionalTest(const char* const /*unused*/)
{
return false;
}
template <>
bool additionalTest<TransitiveProperty::COMPILE_DEFINITIONS>(
const char* const prop)
{
return cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_");
}
#define DEFINE_TRANSITIVE_PROPERTY_METHOD(METHOD, PROPERTY) \
bool cmGeneratorExpressionDAGChecker::METHOD() const \
{ \
const char* const prop = this->Property.c_str(); \
if (strcmp(prop, #PROPERTY) == 0 || \
strcmp(prop, "INTERFACE_" #PROPERTY) == 0) { \
return true; \
} \
return additionalTest<TransitiveProperty::PROPERTY>(prop); \
}
CM_FOR_EACH_TRANSITIVE_PROPERTY(DEFINE_TRANSITIVE_PROPERTY_METHOD)
#undef DEFINE_TRANSITIVE_PROPERTY_METHOD
+6 -34
View File
@@ -13,33 +13,7 @@
struct GeneratorExpressionContent;
struct cmGeneratorExpressionContext;
class cmGeneratorTarget;
#define CM_SELECT_BOTH(F, A1, A2) F(A1, A2)
#define CM_SELECT_FIRST(F, A1, A2) F(A1)
#define CM_SELECT_SECOND(F, A1, A2) F(A2)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, SELECT) \
SELECT(F, EvaluatingIncludeDirectories, INCLUDE_DIRECTORIES) \
SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \
SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \
SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \
SELECT(F, EvaluatingAutoMocMacroNames, AUTOMOC_MACRO_NAMES) \
SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \
SELECT(F, EvaluatingSources, SOURCES) \
SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \
SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS) \
SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES) \
SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS) \
SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(F) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_FIRST)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(F) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_SECOND)
class cmLocalGenerator;
struct cmGeneratorExpressionDAGChecker
{
@@ -47,11 +21,13 @@ struct cmGeneratorExpressionDAGChecker
cmGeneratorTarget const* target,
std::string property,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent);
cmGeneratorExpressionDAGChecker* parent,
cmLocalGenerator const* contextLG);
cmGeneratorExpressionDAGChecker(cmGeneratorTarget const* target,
std::string property,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* parent);
cmGeneratorExpressionDAGChecker* parent,
cmLocalGenerator const* contextLG);
enum Result
{
@@ -83,11 +59,7 @@ struct cmGeneratorExpressionDAGChecker
bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr,
ForGenex genex = ForGenex::ANY) const;
#define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;
CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(DECLARE_TRANSITIVE_PROPERTY_METHOD)
#undef DECLARE_TRANSITIVE_PROPERTY_METHOD
bool EvaluatingSources() const;
bool GetTransitivePropertiesOnly() const;
void SetTransitivePropertiesOnly() { this->TransitivePropertiesOnly = true; }
+19 -29
View File
@@ -487,7 +487,8 @@ protected:
if (context->HeadTarget) {
cmGeneratorExpressionDAGChecker dagChecker(
context->Backtrace, context->HeadTarget,
genexOperator + ":" + expression, content, dagCheckerParent);
genexOperator + ":" + expression, content, dagCheckerParent,
context->LG);
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
@@ -2700,7 +2701,8 @@ static const struct DeviceLinkNode : public cmGeneratorExpressionNode
static std::string getLinkedTargetsContent(
cmGeneratorTarget const* target, std::string const& prop,
cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagChecker)
cmGeneratorExpressionDAGChecker* dagChecker,
cmGeneratorTarget::LinkInterfaceFor interfaceFor)
{
std::string result;
if (cmLinkImplementationLibraries const* impl =
@@ -2716,8 +2718,7 @@ static std::string getLinkedTargetsContent(
target, context->EvaluateForBuildsystem, lib.Backtrace,
context->Language);
std::string libResult = lib.Target->EvaluateInterfaceProperty(
prop, &libContext, dagChecker,
cmGeneratorTarget::LinkInterfaceFor::Usage);
prop, &libContext, dagChecker, interfaceFor);
if (!libResult.empty()) {
if (result.empty()) {
result = std::move(libResult);
@@ -2875,25 +2876,15 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
std::string interfacePropertyName;
bool isInterfaceProperty = false;
cmGeneratorTarget::LinkInterfaceFor interfaceFor =
cmGeneratorTarget::LinkInterfaceFor::Usage;
#define POPULATE_INTERFACE_PROPERTY_NAME(prop) \
if (propertyName == #prop) { \
interfacePropertyName = "INTERFACE_" #prop; \
} else if (propertyName == "INTERFACE_" #prop) { \
interfacePropertyName = "INTERFACE_" #prop; \
isInterfaceProperty = true; \
} else
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
// Note that the above macro terminates with an else
/* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
cmPolicies::PolicyStatus polSt =
context->LG->GetPolicyStatus(cmPolicies::CMP0043);
if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
}
if (cm::optional<cmGeneratorTarget::TransitiveProperty> transitiveProp =
target->IsTransitiveProperty(propertyName, context->LG)) {
interfacePropertyName = std::string(transitiveProp->InterfaceName);
isInterfaceProperty = transitiveProp->InterfaceName == propertyName;
interfaceFor = transitiveProp->InterfaceFor;
}
#undef POPULATE_INTERFACE_PROPERTY_NAME
bool evaluatingLinkLibraries = false;
@@ -2916,20 +2907,19 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
return std::string();
}
} else {
assert(dagCheckerParent
->EvaluatingTransitiveProperty()); // NOLINT(clang-tidy)
assert(dagCheckerParent->EvaluatingTransitiveProperty());
}
}
if (isInterfaceProperty) {
return cmGeneratorExpression::StripEmptyListElements(
target->EvaluateInterfaceProperty(
propertyName, context, dagCheckerParent,
cmGeneratorTarget::LinkInterfaceFor::Usage));
target->EvaluateInterfaceProperty(propertyName, context,
dagCheckerParent, interfaceFor));
}
cmGeneratorExpressionDAGChecker dagChecker(
context->Backtrace, target, propertyName, content, dagCheckerParent);
cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, target,
propertyName, content,
dagCheckerParent, context->LG);
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
@@ -3011,7 +3001,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
this->EvaluateDependentExpression(result, context->LG, context, target,
&dagChecker, target));
std::string linkedTargetsContent = getLinkedTargetsContent(
target, interfacePropertyName, context, &dagChecker);
target, interfacePropertyName, context, &dagChecker, interfaceFor);
if (!linkedTargetsContent.empty()) {
result += (result.empty() ? "" : ";") + linkedTargetsContent;
}
+92 -25
View File
@@ -65,6 +65,7 @@
namespace {
using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
using TransitiveProperty = cmGeneratorTarget::TransitiveProperty;
const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
@@ -73,6 +74,33 @@ const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
"INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
}
const std::map<cm::string_view, TransitiveProperty>
cmGeneratorTarget::BuiltinTransitiveProperties = {
{ "AUTOMOC_MACRO_NAMES"_s,
{ "INTERFACE_AUTOMOC_MACRO_NAMES"_s, LinkInterfaceFor::Usage } },
{ "AUTOUIC_OPTIONS"_s,
{ "INTERFACE_AUTOUIC_OPTIONS"_s, LinkInterfaceFor::Usage } },
{ "COMPILE_DEFINITIONS"_s,
{ "INTERFACE_COMPILE_DEFINITIONS"_s, LinkInterfaceFor::Usage } },
{ "COMPILE_FEATURES"_s,
{ "INTERFACE_COMPILE_FEATURES"_s, LinkInterfaceFor::Usage } },
{ "COMPILE_OPTIONS"_s,
{ "INTERFACE_COMPILE_OPTIONS"_s, LinkInterfaceFor::Usage } },
{ "INCLUDE_DIRECTORIES"_s,
{ "INTERFACE_INCLUDE_DIRECTORIES"_s, LinkInterfaceFor::Usage } },
{ "LINK_DEPENDS"_s,
{ "INTERFACE_LINK_DEPENDS"_s, LinkInterfaceFor::Link } },
{ "LINK_DIRECTORIES"_s,
{ "INTERFACE_LINK_DIRECTORIES"_s, LinkInterfaceFor::Link } },
{ "LINK_OPTIONS"_s,
{ "INTERFACE_LINK_OPTIONS"_s, LinkInterfaceFor::Link } },
{ "PRECOMPILE_HEADERS"_s,
{ "INTERFACE_PRECOMPILE_HEADERS"_s, LinkInterfaceFor::Usage } },
{ "SOURCES"_s, { "INTERFACE_SOURCES"_s, LinkInterfaceFor::Usage } },
{ "SYSTEM_INCLUDE_DIRECTORIES"_s,
{ "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, LinkInterfaceFor::Usage } },
};
template <>
cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
cmGeneratorTarget const* tgt, cmMakefile const& /* mf */)
@@ -887,7 +915,7 @@ std::string cmGeneratorTarget::GetLinkerTypeProperty(
auto linkerType = this->GetProperty(propName);
if (!linkerType.IsEmpty()) {
cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
nullptr);
nullptr, this->LocalGenerator);
auto ltype =
cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
config, this, &dagChecker, this, lang);
@@ -1352,7 +1380,8 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory(
if (iter == this->SystemIncludesCache.end()) {
cmGeneratorExpressionDAGChecker dagChecker(
this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr);
this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr,
this->LocalGenerator);
bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED");
@@ -1460,7 +1489,8 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
// a subset of TargetPropertyNode::Evaluate without stringify/parse steps
// but sufficient for transitive interface properties.
cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop,
nullptr, dagCheckerParent);
nullptr, dagCheckerParent,
this->LocalGenerator);
switch (dagChecker.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dagChecker.ReportError(
@@ -1525,6 +1555,38 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
return result;
}
cm::optional<cmGeneratorTarget::TransitiveProperty>
cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop,
cmLocalGenerator const* lg) const
{
cm::optional<TransitiveProperty> result;
static const cm::string_view kINTERFACE_ = "INTERFACE_"_s;
if (cmHasPrefix(prop, kINTERFACE_)) {
prop = prop.substr(kINTERFACE_.length());
}
auto i = BuiltinTransitiveProperties.find(prop);
if (i != BuiltinTransitiveProperties.end()) {
result = i->second;
if (result->InterfaceFor != cmGeneratorTarget::LinkInterfaceFor::Usage) {
cmPolicies::PolicyStatus cmp0166 =
lg->GetPolicyStatus(cmPolicies::CMP0166);
if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) &&
(prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s ||
prop == "LINK_OPTIONS"_s)) {
result->InterfaceFor = cmGeneratorTarget::LinkInterfaceFor::Usage;
}
}
} else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) {
cmPolicies::PolicyStatus cmp0043 =
lg->GetPolicyStatus(cmPolicies::CMP0043);
if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) {
result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s,
LinkInterfaceFor::Usage };
}
}
return result;
}
namespace {
enum class IncludeDirectoryFallBack
@@ -1539,8 +1601,10 @@ std::string AddLangSpecificInterfaceIncludeDirectories(
const std::string& propertyName, IncludeDirectoryFallBack mode,
cmGeneratorExpressionDAGChecker* context)
{
cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
propertyName, nullptr, context };
cmGeneratorExpressionDAGChecker dag{
target->GetBacktrace(), target, propertyName, nullptr, context,
target->GetLocalGenerator()
};
switch (dag.Check()) {
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
dag.ReportError(
@@ -1590,8 +1654,10 @@ void AddLangSpecificImplicitIncludeDirectories(
{
if (const auto* libraries = target->GetLinkImplementationLibraries(
config, LinkInterfaceFor::Usage)) {
cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
propertyName, nullptr, nullptr };
cmGeneratorExpressionDAGChecker dag{
target->GetBacktrace(), target, propertyName, nullptr, nullptr,
target->GetLocalGenerator()
};
for (const cmLinkImplItem& library : libraries->Libraries) {
if (const cmGeneratorTarget* dependency = library.Target) {
@@ -1843,8 +1909,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
this->DebugSourcesDone = true;
}
cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr,
nullptr);
cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr,
this->LocalGenerator);
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
this, config, std::string(), &dagChecker, this->SourceEntries);
@@ -3058,7 +3124,7 @@ void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result,
}
cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr,
nullptr);
nullptr, this->LocalGenerator);
cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator,
config, this, &dagChecker),
result);
@@ -3858,8 +3924,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
std::vector<BT<std::string>> includes;
std::unordered_set<std::string> uniqueIncludes;
cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES",
nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(
this, "INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4123,7 +4189,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileOptions(
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr,
nullptr);
nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4164,7 +4230,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures(
std::unordered_set<std::string> uniqueFeatures;
cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr,
nullptr);
nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4213,8 +4279,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
std::vector<BT<std::string>> list;
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS",
nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(
this, "COMPILE_DEFINITIONS", nullptr, nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4277,8 +4343,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
}
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(
this, "PRECOMPILE_HEADERS", nullptr, nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4667,7 +4733,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr,
nullptr);
nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4835,8 +4901,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions(
std::vector<BT<std::string>> result;
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS",
nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(
this, "STATIC_LIBRARY_OPTIONS", nullptr, nullptr, this->LocalGenerator);
EvaluatedTargetPropertyEntries entries;
if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
@@ -4949,7 +5015,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
std::unordered_set<std::string> uniqueDirectories;
cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
nullptr);
nullptr, this->LocalGenerator);
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
@@ -4993,7 +5059,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
std::vector<BT<std::string>> result;
std::unordered_set<std::string> uniqueOptions;
cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr,
nullptr);
nullptr, this->LocalGenerator);
EvaluatedTargetPropertyEntries entries;
if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
@@ -6979,7 +7045,8 @@ void cmGeneratorTarget::ExpandLinkItems(
return;
}
// Keep this logic in sync with ComputeLinkImplementationLibraries.
cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr,
this->LocalGenerator);
// The $<LINK_ONLY> expression may be in a link interface to specify
// private link dependencies that are otherwise excluded from usage
// requirements.
@@ -8654,7 +8721,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
for (auto const& entry : entryRange) {
// Keep this logic in sync with ExpandLinkItems.
cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
nullptr);
nullptr, this->LocalGenerator);
// The $<LINK_ONLY> expression may be used to specify link dependencies
// that are otherwise excluded from usage requirements.
if (implFor == LinkInterfaceFor::Usage) {
+13
View File
@@ -15,6 +15,7 @@
#include <vector>
#include <cm/optional>
#include <cm/string_view>
#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
@@ -886,6 +887,18 @@ public:
cmGeneratorExpressionDAGChecker* dagCheckerParent,
LinkInterfaceFor interfaceFor) const;
struct TransitiveProperty
{
cm::string_view InterfaceName;
LinkInterfaceFor InterfaceFor;
};
static const std::map<cm::string_view, TransitiveProperty>
BuiltinTransitiveProperties;
cm::optional<TransitiveProperty> IsTransitiveProperty(
cm::string_view prop, cmLocalGenerator const* lg) const;
bool HaveInstallTreeRPATH(const std::string& config) const;
bool GetBuildRPATH(const std::string& config, std::string& rpath) const;
+7 -3
View File
@@ -294,8 +294,8 @@ class cmMakefile;
"FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing.", 3, \
17, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0099, \
"Link properties are transitive over private dependency on static " \
"libraries.", \
"Link properties are transitive over private dependencies of " \
"static libraries.", \
3, 17, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0100, "Let AUTOMOC and AUTOUIC process .hh files.", 3, \
17, 0, cmPolicies::WARN) \
@@ -508,7 +508,11 @@ class cmMakefile;
3, 30, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0165, \
"enable_language() must not be called before project().", 3, 30, 0, \
cmPolicies::WARN)
cmPolicies::WARN) \
SELECT(POLICY, CMP0166, \
"TARGET_PROPERTY evaluates link properties transitively over " \
"private dependencies of static libraries.", \
3, 30, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
+3 -2
View File
@@ -1918,8 +1918,9 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
cmGeneratorExpressionDAGChecker dagChecker(
this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr);
cmGeneratorExpressionDAGChecker dagChecker(this->GenTarget,
"AUTOMOC_MACRO_NAMES", nullptr,
nullptr, this->LocalGen);
EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries;
if (this->MultiConfig) {
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.17)
if(RunCMake_TEST STREQUAL "LOCATION")
cmake_minimum_required(VERSION 2.8.12) # Leave CMP0026 unset.
endif()
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
@@ -13,6 +13,9 @@ run_cmake(LinkImplementationCycle5)
run_cmake(LinkImplementationCycle6)
run_cmake(LOCATION)
run_cmake(SOURCES)
run_cmake(TransitiveBuild)
run_cmake(TransitiveLink-CMP0166-OLD)
run_cmake(TransitiveLink-CMP0166-NEW)
block()
run_cmake(Scope)
@@ -0,0 +1,25 @@
set(expect [[
# file\(GENERATE\) produced:
main INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1'
main SYSTEM_INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/sys1'
main COMPILE_DEFINITIONS: 'DEFM;DEF1'
main COMPILE_FEATURES: 'cxx_std_20;cxx_std_11'
main COMPILE_OPTIONS: '-optM;-opt1'
main PRECOMPILE_HEADERS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h'
main SOURCES: 'main.c;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c'
main AUTOMOC_MACRO_NAMES: 'MOCM;MOC1'
main AUTOUIC_OPTIONS: '-uicM;-uic1'
]])
string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
string(REGEX REPLACE "\n+$" "" expect "${expect}")
file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual)
string(REGEX REPLACE "\r\n" "\n" actual "${actual}")
string(REGEX REPLACE "\n+$" "" actual "${actual}")
if(NOT actual MATCHES "^${expect}$")
string(REPLACE "\n" "\n expect> " expect " expect> ${expect}")
string(REPLACE "\n" "\n actual> " actual " actual> ${actual}")
message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}")
endif()
@@ -0,0 +1,66 @@
enable_language(C)
set(CMAKE_PCH_EXTENSION "") # suppress cmake_pch from SOURCES
add_library(foo1 STATIC empty.c)
target_link_libraries(foo1 PRIVATE foo2 foo3)
target_include_directories(foo1 INTERFACE dir1)
target_compile_definitions(foo1 INTERFACE DEF1)
target_compile_features(foo1 INTERFACE cxx_std_11)
target_compile_options(foo1 INTERFACE -opt1)
target_precompile_headers(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.h")
target_sources(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.c")
set_target_properties(foo1 PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys1"
INTERFACE_AUTOMOC_MACRO_NAMES "MOC1"
INTERFACE_AUTOUIC_OPTIONS "-uic1"
)
add_library(foo2 STATIC empty.c)
target_include_directories(foo2 INTERFACE dir2)
target_compile_definitions(foo2 INTERFACE DEF2)
target_compile_features(foo2 INTERFACE cxx_std_14)
target_compile_options(foo2 INTERFACE -opt2)
target_precompile_headers(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.h")
target_sources(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.c")
set_target_properties(foo2 PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys2"
INTERFACE_AUTOMOC_MACRO_NAMES "MOC2"
INTERFACE_AUTOUIC_OPTIONS "-uic2"
)
add_library(foo3 STATIC empty.c)
target_include_directories(foo3 PRIVATE dir3)
target_compile_definitions(foo3 PRIVATE DEF3)
target_compile_features(foo3 PRIVATE cxx_std_17)
target_compile_options(foo3 PRIVATE -opt3)
target_precompile_headers(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.h")
target_sources(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.c")
set_target_properties(foo3 PROPERTIES
SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys3"
AUTOMOC_MACRO_NAMES "MOC3"
AUTOUIC_OPTIONS "-uic3"
)
add_executable(main main.c)
target_link_libraries(main PRIVATE foo1)
target_include_directories(main PRIVATE dirM)
target_compile_definitions(main PRIVATE DEFM)
target_compile_features(main PRIVATE cxx_std_20)
target_compile_options(main PRIVATE -optM)
target_precompile_headers(main PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty.h")
set_target_properties(main PROPERTIES
AUTOMOC_MACRO_NAMES "MOCM"
AUTOUIC_OPTIONS "-uicM"
)
file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced:
main INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,INCLUDE_DIRECTORIES>'
main SYSTEM_INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,SYSTEM_INCLUDE_DIRECTORIES>'
main COMPILE_DEFINITIONS: '$<TARGET_PROPERTY:main,COMPILE_DEFINITIONS>'
main COMPILE_FEATURES: '$<TARGET_PROPERTY:main,COMPILE_FEATURES>'
main COMPILE_OPTIONS: '$<TARGET_PROPERTY:main,COMPILE_OPTIONS>'
main PRECOMPILE_HEADERS: '$<TARGET_PROPERTY:main,PRECOMPILE_HEADERS>'
main SOURCES: '$<TARGET_PROPERTY:main,SOURCES>'
main AUTOMOC_MACRO_NAMES: '$<TARGET_PROPERTY:main,AUTOMOC_MACRO_NAMES>'
main AUTOUIC_OPTIONS: '$<TARGET_PROPERTY:main,AUTOUIC_OPTIONS>'
")
@@ -0,0 +1,8 @@
set(expect [[
# file\(GENERATE\) produced:
main LINK_LIBRARIES: 'foo1' # not transitive
main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir2'
main LINK_OPTIONS: '-optM;-opt1;-opt2'
main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep2'
]])
include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0166 NEW)
include(TransitiveLink-common.cmake)
@@ -0,0 +1,8 @@
set(expect [[
# file\(GENERATE\) produced:
main LINK_LIBRARIES: 'foo1' # not transitive
main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1'
main LINK_OPTIONS: '-optM;-opt1'
main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/dep1'
]])
include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0166 OLD)
include(TransitiveLink-common.cmake)
@@ -0,0 +1,12 @@
string(REGEX REPLACE "\r\n" "\n" expect "${expect}")
string(REGEX REPLACE "\n+$" "" expect "${expect}")
file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual)
string(REGEX REPLACE "\r\n" "\n" actual "${actual}")
string(REGEX REPLACE "\n+$" "" actual "${actual}")
if(NOT actual MATCHES "^${expect}$")
string(REPLACE "\n" "\n expect> " expect " expect> ${expect}")
string(REPLACE "\n" "\n actual> " actual " actual> ${actual}")
message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}")
endif()
@@ -0,0 +1,42 @@
enable_language(C)
add_library(foo1 STATIC empty.c)
target_link_libraries(foo1 PRIVATE foo2 foo3)
target_link_directories(foo1 INTERFACE dir1)
target_link_options(foo1 INTERFACE -opt1)
set_target_properties(foo1 PROPERTIES
INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep1"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep1" "")
add_library(foo2 STATIC empty.c)
target_link_directories(foo2 INTERFACE dir2)
target_link_options(foo2 INTERFACE -opt2)
set_target_properties(foo2 PROPERTIES
INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep2"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep2" "")
add_library(foo3 STATIC empty.c)
target_link_directories(foo3 PRIVATE dir3)
target_link_options(foo3 PRIVATE -opt3)
set_target_properties(foo3 PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep3"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep3" "")
add_executable(main main.c)
target_link_libraries(main PRIVATE foo1)
target_link_directories(main PRIVATE dirM)
target_link_options(main PRIVATE -optM)
set_target_properties(main PROPERTIES
LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/depM"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/depM" "")
file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced:
main LINK_LIBRARIES: '$<TARGET_PROPERTY:main,LINK_LIBRARIES>' # not transitive
main LINK_DIRECTORIES: '$<TARGET_PROPERTY:main,LINK_DIRECTORIES>'
main LINK_OPTIONS: '$<TARGET_PROPERTY:main,LINK_OPTIONS>'
main LINK_DEPENDS: '$<TARGET_PROPERTY:main,LINK_DEPENDS>'
")
@@ -0,0 +1,4 @@
int main(void)
{
return 0;
}