diff options
author | Brad King <brad.king@kitware.com> | 2024-05-01 14:05:53 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2024-05-01 14:06:11 (GMT) |
commit | de9faaf0a318d05b694d0edebedfab84e820fe51 (patch) | |
tree | ddfdb25866c37ddbbee6a8b0a474c60fb1c73939 | |
parent | 30b61287adfac023820c2d6ca70d96216fe226b5 (diff) | |
parent | ddb9442f487808b74ed3429832a74385a1214582 (diff) | |
download | CMake-de9faaf0a318d05b694d0edebedfab84e820fe51.zip CMake-de9faaf0a318d05b694d0edebedfab84e820fe51.tar.gz CMake-de9faaf0a318d05b694d0edebedfab84e820fe51.tar.bz2 |
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
32 files changed, 431 insertions, 167 deletions
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 38fa6f9..31f0573 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -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. diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index e46e88e..826790d 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -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 diff --git a/Help/policy/CMP0099.rst b/Help/policy/CMP0099.rst index c0db99d..0a2b786 100644 --- a/Help/policy/CMP0099.rst +++ b/Help/policy/CMP0099.rst @@ -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 diff --git a/Help/policy/CMP0166.rst b/Help/policy/CMP0166.rst new file mode 100644 index 0000000..5c67880 --- /dev/null +++ b/Help/policy/CMP0166.rst @@ -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 diff --git a/Help/release/dev/genex-link-properties.rst b/Help/release/dev/genex-link-properties.rst new file mode 100644 index 0000000..e1e84e0 --- /dev/null +++ b/Help/release/dev/genex-link-properties.rst @@ -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`. diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 677bb48..fbf39e2 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -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); diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index f4b26f3..f6ff71a 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -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); diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 00c9173..4c41ff5 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -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); diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 0b96c3f..0583fd5 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -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, diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index fda7ec3..bb1f4b4 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -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 diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 068ba6b..b230188 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -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; } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 0df086b..b9feb87 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -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; } diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 319526a..c14ff3a 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -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) { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 6228daf..678656a 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -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; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index ada87dd..eb9f031 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -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) \ diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 0ccebfd..34a47cc 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -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) { diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt index 32d92d8..5791993 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt @@ -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) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake index b613ad1..07052eb 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake @@ -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) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake new file mode 100644 index 0000000..65adfaf --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake @@ -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() diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake new file mode 100644 index 0000000..8f8eb96 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake @@ -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>' +") diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake new file mode 100644 index 0000000..7ea95b8 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.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;[^';]*/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) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake new file mode 100644 index 0000000..658dd84 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0166 NEW) +include(TransitiveLink-common.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake new file mode 100644 index 0000000..2d430c2 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.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) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake new file mode 100644 index 0000000..a4a4599 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0166 OLD) +include(TransitiveLink-common.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake new file mode 100644 index 0000000..42e63bc --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-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() diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake new file mode 100644 index 0000000..c120366 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake @@ -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>' +") diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c b/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} |