From b9ee79b8a13abb957a176ff0b5eab1e5d33efc50 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 9 May 2024 13:38:35 -0400 Subject: GenEx: Add support for custom transitive compile properties Teach the `$` generator expression to check for a new `TRANSITIVE_COMPILE_PROPERTIES` property in the target's link closure to enable transitive evaluation of named properties through the link closure, excluding entries guarded by `$`. Issue: #20416 --- Help/manual/cmake-buildsystem.7.rst | 47 ++++++++++ Help/manual/cmake-generator-expressions.7.rst | 43 ++++++++++ Help/manual/cmake-properties.7.rst | 1 + Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst | 17 ++++ Help/release/dev/custom-transitive-properties.rst | 7 ++ Source/cmExportBuildFileGenerator.cxx | 2 + Source/cmExportFileGenerator.cxx | 19 ++++ Source/cmExportFileGenerator.h | 4 + Source/cmExportInstallFileGenerator.cxx | 2 + Source/cmGeneratorExpressionDAGChecker.cxx | 5 +- Source/cmGeneratorExpressionNode.cxx | 12 +-- Source/cmGeneratorTarget.cxx | 2 + Source/cmGeneratorTarget.h | 38 +++++++- Source/cmGeneratorTarget_Link.cxx | 17 ++++ Source/cmGeneratorTarget_TransitiveProperty.cxx | 90 ++++++++++++++++++- Tests/CMakeLists.txt | 1 + Tests/CustomTransitiveProperties/CMakeLists.txt | 100 ++++++++++++++++++++++ Tests/CustomTransitiveProperties/check.cmake | 33 +++++++ Tests/CustomTransitiveProperties/main.c | 68 +++++++++++++++ Tests/CustomTransitiveProperties/object1.c | 36 ++++++++ Tests/CustomTransitiveProperties/static1.c | 36 ++++++++ Tests/ExportImport/Export/CMakeLists.txt | 25 ++++++ Tests/ExportImport/Export/testLib10.c | 4 + Tests/ExportImport/Export/testLib11.c | 6 ++ Tests/ExportImport/Import/A/CMakeLists.txt | 10 +++ Tests/ExportImport/Import/A/imp_testLib10.c | 10 +++ Tests/ExportImport/Import/A/imp_testLib11.c | 18 ++++ 27 files changed, 643 insertions(+), 10 deletions(-) create mode 100644 Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst create mode 100644 Help/release/dev/custom-transitive-properties.rst create mode 100644 Tests/CustomTransitiveProperties/CMakeLists.txt create mode 100644 Tests/CustomTransitiveProperties/check.cmake create mode 100644 Tests/CustomTransitiveProperties/main.c create mode 100644 Tests/CustomTransitiveProperties/object1.c create mode 100644 Tests/CustomTransitiveProperties/static1.c create mode 100644 Tests/ExportImport/Export/testLib10.c create mode 100644 Tests/ExportImport/Export/testLib11.c create mode 100644 Tests/ExportImport/Import/A/imp_testLib10.c create mode 100644 Tests/ExportImport/Import/A/imp_testLib11.c diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst index 93d55c7..4dc2306 100644 --- a/Help/manual/cmake-buildsystem.7.rst +++ b/Help/manual/cmake-buildsystem.7.rst @@ -602,6 +602,53 @@ linking consumers. List of files on which linking the target's consumers depends, for those that are executables, shared libraries, or module libraries. +.. _`Custom Transitive Properties`: + +Custom Transitive Properties +---------------------------- + +.. versionadded:: 3.30 + +The :genex:`TARGET_PROPERTY` generator expression evaluates the above +`build specification `_ and +`usage requirement `_ properties +as builtin transitive properties. It also supports custom transitive +properties defined by the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` +property on the target and its link dependencies. + +For example: + +.. code-block:: cmake + + add_library(example INTERFACE) + set_target_properties(example PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_C" + + INTERFACE_CUSTOM_C "EXAMPLE_CUSTOM_C" + ) + + add_library(mylib STATIC mylib.c) + target_link_libraries(mylib PRIVATE example) + set_target_properties(mylib PROPERTIES + CUSTOM_C "MYLIB_PRIVATE_CUSTOM_C" + INTERFACE_CUSTOM_C "MYLIB_IFACE_CUSTOM_C" + ) + + add_executable(myexe myexe.c) + target_link_libraries(myexe PRIVATE mylib) + set_target_properties(myexe PROPERTIES + CUSTOM_C "MYEXE_CUSTOM_C" + ) + + add_custom_target(print ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -E echo + # Prints "MYLIB_PRIVATE_CUSTOM_C;EXAMPLE_CUSTOM_C" + "$" + + # Prints "MYEXE_CUSTOM_C" + "$" + ) + .. _`Compatible Interface Properties`: Compatible Interface Properties diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index d9b10b8..d0d751b 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -1810,6 +1810,49 @@ The expressions have special evaluation rules for some properties: Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive. +:ref:`Custom Transitive Properties` + .. versionadded:: 3.30 + + These are processed during evaluation as follows: + + * Evaluation of :genex:`$` for some property + ``PROP``, named without an ``INTERFACE_`` prefix, + checks the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + property on target ``tgt``, + on targets named by its :prop_tgt:`LINK_LIBRARIES`, and on the + transitive closure of targets named by the linked targets' + :prop_tgt:`INTERFACE_LINK_LIBRARIES`. + + If ``PROP`` is listed by one of those properties, then it evaluates as + a :ref:`semicolon-separated list ` representing + the union of the value on the target itself with the values of the + corresponding ``INTERFACE_PROP`` on targets named by the target's + :prop_tgt:`LINK_LIBRARIES`: + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the linked targets' :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + excluding entries guarded by the :genex:`LINK_ONLY` generator expression. + + * Evaluation of :genex:`$` for some + property ``INTERFACE_PROP``, named with an ``INTERFACE_`` prefix, + checks the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + property on target ``tgt``, + and on the transitive closure of targets named by its + :prop_tgt:`INTERFACE_LINK_LIBRARIES`. + + If the corresponding ``PROP`` is listed by one of those properties, + then ``INTERFACE_PROP`` evaluates as a + :ref:`semicolon-separated list ` representing the + union of the value on the target itself with the value of the same + property on targets named by the target's + :prop_tgt:`INTERFACE_LINK_LIBRARIES`: + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + excluding entries guarded by the :genex:`LINK_ONLY` generator expression. + :ref:`Compatible Interface Properties` These evaluate as a single value combined from the target itself, from targets named by the target's :prop_tgt:`LINK_LIBRARIES`, and diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 7e640df..48e1d54 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -401,6 +401,7 @@ Properties on Targets /prop_tgt/Swift_MODULE_NAME /prop_tgt/SYSTEM /prop_tgt/TEST_LAUNCHER + /prop_tgt/TRANSITIVE_COMPILE_PROPERTIES /prop_tgt/TYPE /prop_tgt/UNITY_BUILD /prop_tgt/UNITY_BUILD_BATCH_SIZE diff --git a/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst b/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst new file mode 100644 index 0000000..a7777c3 --- /dev/null +++ b/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst @@ -0,0 +1,17 @@ +TRANSITIVE_COMPILE_PROPERTIES +----------------------------- + +.. versionadded:: 3.30 + +Properties that the :genex:`TARGET_PROPERTY` generator expression, on the +target and its dependents, evaluates as the union of values collected from +the transitive closure of link dependencies, excluding entries guarded by +:genex:`LINK_ONLY`. + +The value is a :ref:`semicolon-separated list ` +of :ref:`custom transitive property ` names. +Any leading ``INTERFACE_`` prefix is ignored, e.g., ``INTERFACE_PROP`` is +treated as just ``PROP``. + +See documentation of the :genex:`TARGET_PROPERTY` generator expression +for details of custom transitive property evaluation. diff --git a/Help/release/dev/custom-transitive-properties.rst b/Help/release/dev/custom-transitive-properties.rst new file mode 100644 index 0000000..aafa2f2 --- /dev/null +++ b/Help/release/dev/custom-transitive-properties.rst @@ -0,0 +1,7 @@ +custom-transitive-properties +---------------------------- + +* The :genex:`TARGET_PROPERTY` generator expression learned to evaluate + :ref:`custom transitive properties ` + defined by a new :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + target property. diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 2345d64..d877d76 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -152,6 +152,8 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) gte, cmGeneratorExpression::BuildInterface, properties); } this->PopulateCompatibleInterfaceProperties(gte, properties); + this->PopulateCustomTransitiveInterfaceProperties( + gte, cmGeneratorExpression::BuildInterface, properties); this->GenerateInterfaceProperties(gte, os, properties); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 9bd7f49..a7d0d7e 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -604,6 +604,25 @@ void cmExportFileGenerator::PopulateCompatibleInterfaceProperties( } } +void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + this->PopulateInterfaceProperty("TRANSITIVE_COMPILE_PROPERTIES", target, + properties); + std::set ifaceProperties; + for (std::string const& config : this->Configurations) { + for (auto const& i : target->GetCustomTransitiveProperties( + config, cmGeneratorTarget::PropertyFor::Interface)) { + ifaceProperties.emplace(i.second.InterfaceName); + } + } + for (std::string const& ip : ifaceProperties) { + this->PopulateInterfaceProperty(ip, target, preprocessRule, properties); + } +} + void cmExportFileGenerator::GenerateInterfaceProperties( const cmGeneratorTarget* target, std::ostream& os, const ImportPropertyMap& properties) diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f619576..f275a12 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -145,6 +145,10 @@ protected: ImportPropertyMap& properties); void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, ImportPropertyMap& properties); + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); virtual void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties); diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 5c95ecd..f5f22ef 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -160,6 +160,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) properties); this->PopulateCompatibleInterfaceProperties(gt, properties); + this->PopulateCustomTransitiveInterfaceProperties( + gt, cmGeneratorExpression::InstallInterface, properties); this->GenerateInterfaceProperties(gt, os, properties); diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 1ae422e..aad25f0 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -40,12 +40,13 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( , Content(content) , Backtrace(std::move(backtrace)) { - static_cast(contextConfig); if (parent) { this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty; } else { this->TopIsTransitiveProperty = - this->Target->IsTransitiveProperty(this->Property, contextLG) + this->Target + ->IsTransitiveProperty(this->Property, contextLG, contextConfig, + this->EvaluatingLinkLibraries()) .has_value(); } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index e07f26d..01cd18d 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -2873,19 +2873,22 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode return target->GetLinkerLanguage(context->Config); } + bool const evaluatingLinkLibraries = + dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries(); + std::string interfacePropertyName; bool isInterfaceProperty = false; cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile; if (cm::optional transitiveProp = - target->IsTransitiveProperty(propertyName, context->LG)) { + target->IsTransitiveProperty(propertyName, context->LG, + context->Config, + evaluatingLinkLibraries)) { interfacePropertyName = std::string(transitiveProp->InterfaceName); isInterfaceProperty = transitiveProp->InterfaceName == propertyName; usage = transitiveProp->Usage; } - bool evaluatingLinkLibraries = false; - if (dagCheckerParent) { // This $ node has been reached while evaluating // another target property value. Check that the outermost evaluation @@ -2894,8 +2897,7 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode dagCheckerParent->EvaluatingPICExpression() || dagCheckerParent->EvaluatingLinkerLauncher()) { // No check required. - } else if (dagCheckerParent->EvaluatingLinkLibraries()) { - evaluatingLinkLibraries = true; + } else if (evaluatingLinkLibraries) { if (!interfacePropertyName.empty()) { reportError( context, content->GetOriginalExpression(), diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 55f34f8..aa3e36f 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -518,6 +518,8 @@ void cmGeneratorTarget::ClearSourcesCache() this->IncludeDirectoriesCache.clear(); this->CompileOptionsCache.clear(); this->CompileDefinitionsCache.clear(); + this->CustomTransitiveBuildPropertiesMap.clear(); + this->CustomTransitiveInterfacePropertiesMap.clear(); this->PrecompileHeadersCache.clear(); this->LinkOptionsCache.clear(); this->LinkDirectoriesCache.clear(); diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 3090a5b..8f27a91 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -907,7 +907,8 @@ public: BuiltinTransitiveProperties; cm::optional IsTransitiveProperty( - cm::string_view prop, cmLocalGenerator const* lg) const; + cm::string_view prop, cmLocalGenerator const* lg, + std::string const& config, bool evaluatingLinkLibraries) const; bool HaveInstallTreeRPATH(const std::string& config) const; @@ -989,6 +990,30 @@ public: bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config); + class CustomTransitiveProperty : public TransitiveProperty + { + std::unique_ptr InterfaceNameBuf; + CustomTransitiveProperty(std::unique_ptr interfaceNameBuf, + UseTo usage); + + public: + CustomTransitiveProperty(std::string interfaceName, UseTo usage); + }; + struct CustomTransitiveProperties + : public std::map + { + void Add(cmValue props, UseTo usage); + }; + + enum class PropertyFor + { + Build, + Interface, + }; + + CustomTransitiveProperties const& GetCustomTransitiveProperties( + std::string const& config, PropertyFor propertyFor) const; + private: void AddSourceCommon(const std::string& src, bool before = false); @@ -1056,6 +1081,11 @@ private: std::string const& base, std::string const& suffix, std::string const& name, cmValue version) const; + mutable std::map + CustomTransitiveBuildPropertiesMap; + mutable std::map + CustomTransitiveInterfacePropertiesMap; + struct CompatibleInterfacesBase { std::set PropsBool; @@ -1306,6 +1336,12 @@ private: void ComputeLinkInterfaceRuntimeLibraries( const std::string& config, cmOptionalLinkInterface& iface) const; + // If this method is made public, or call sites are added outside of + // methods computing cached members, add dedicated caching members. + std::vector GetLinkInterfaceClosure( + std::string const& config, cmGeneratorTarget const* headTarget, + UseTo usage) const; + public: const std::vector& GetLinkImplementationClosure( const std::string& config, UseTo usage) const; diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx index 7c6f7b5..0799429 100644 --- a/Source/cmGeneratorTarget_Link.cxx +++ b/Source/cmGeneratorTarget_Link.cxx @@ -282,6 +282,23 @@ static void processILibs(const std::string& config, } } +std::vector +cmGeneratorTarget::GetLinkInterfaceClosure(std::string const& config, + cmGeneratorTarget const* headTarget, + UseTo usage) const +{ + cmGlobalGenerator* gg = this->GetLocalGenerator()->GetGlobalGenerator(); + std::vector tgts; + std::set emitted; + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(config, headTarget, usage)) { + for (cmLinkItem const& lib : iface->Libraries) { + processILibs(config, headTarget, lib, gg, tgts, emitted, usage); + } + } + return tgts; +} + const std::vector& cmGeneratorTarget::GetLinkImplementationClosure(const std::string& config, UseTo usage) const diff --git a/Source/cmGeneratorTarget_TransitiveProperty.cxx b/Source/cmGeneratorTarget_TransitiveProperty.cxx index 2cd3665..d63d11c 100644 --- a/Source/cmGeneratorTarget_TransitiveProperty.cxx +++ b/Source/cmGeneratorTarget_TransitiveProperty.cxx @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorExpressionNode.h" #include "cmLinkItem.h" +#include "cmList.h" #include "cmLocalGenerator.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" @@ -176,11 +178,16 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty( cm::optional cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop, - cmLocalGenerator const* lg) const + cmLocalGenerator const* lg, + std::string const& config, + bool evaluatingLinkLibraries) const { cm::optional result; static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; - if (cmHasPrefix(prop, kINTERFACE_)) { + PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_) + ? PropertyFor::Interface + : PropertyFor::Build; + if (propertyFor == PropertyFor::Interface) { prop = prop.substr(kINTERFACE_.length()); } auto i = BuiltinTransitiveProperties.find(prop); @@ -202,6 +209,85 @@ cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop, result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile }; } + } else if (!evaluatingLinkLibraries) { + // Honor TRANSITIVE_COMPILE_PROPERTIES + // from the link closure when we are not evaluating the closure itself. + CustomTransitiveProperties const& ctp = + this->GetCustomTransitiveProperties(config, propertyFor); + auto ci = ctp.find(std::string(prop)); + if (ci != ctp.end()) { + result = ci->second; + } } return result; } + +cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( + std::string interfaceName, UseTo usage) + : CustomTransitiveProperty( + cm::make_unique(std::move(interfaceName)), usage) +{ +} +cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( + std::unique_ptr interfaceNameBuf, UseTo usage) + : TransitiveProperty{ *interfaceNameBuf, usage } + , InterfaceNameBuf(std::move(interfaceNameBuf)) +{ +} + +void cmGeneratorTarget::CustomTransitiveProperties::Add(cmValue props, + UseTo usage) +{ + if (props) { + cmList propsList(*props); + for (std::string p : propsList) { + std::string ip; + static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; + if (cmHasPrefix(p, kINTERFACE_)) { + ip = std::move(p); + p = ip.substr(kINTERFACE_.length()); + } else { + ip = cmStrCat(kINTERFACE_, p); + } + this->emplace(std::move(p), + CustomTransitiveProperty(std::move(ip), usage)); + } + } +} + +cmGeneratorTarget::CustomTransitiveProperties const& +cmGeneratorTarget::GetCustomTransitiveProperties(std::string const& config, + PropertyFor propertyFor) const +{ + std::map& ctpm = + propertyFor == PropertyFor::Build + ? this->CustomTransitiveBuildPropertiesMap + : this->CustomTransitiveInterfacePropertiesMap; + auto i = ctpm.find(config); + if (i == ctpm.end()) { + CustomTransitiveProperties ctp; + auto addTransitiveProperties = [this, &config, propertyFor, + &ctp](std::string const& tp, UseTo usage) { + // Add transitive properties named by the target itself. + ctp.Add(this->GetProperty(tp), usage); + // Add transitive properties named by the target's link dependencies. + if (propertyFor == PropertyFor::Build) { + for (cmGeneratorTarget const* gt : + this->GetLinkImplementationClosure(config, usage)) { + ctp.Add(gt->GetProperty(tp), usage); + } + } else { + // The set of custom transitive INTERFACE_ properties does not + // depend on the consumer. Use the target as its own head. + cmGeneratorTarget const* headTarget = this; + for (cmGeneratorTarget const* gt : + this->GetLinkInterfaceClosure(config, headTarget, usage)) { + ctp.Add(gt->GetProperty(tp), usage); + } + } + }; + addTransitiveProperties("TRANSITIVE_COMPILE_PROPERTIES", UseTo::Compile); + i = ctpm.emplace(config, std::move(ctp)).first; + } + return i->second; +} diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 9ff38bf..853380b 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -527,6 +527,7 @@ if(BUILD_TESTING) set_property(TEST CompileOptions APPEND PROPERTY LABELS "Fortran") ADD_TEST_MACRO(CompatibleInterface CompatibleInterface) + ADD_TEST_MACRO(CustomTransitiveProperties CustomTransitiveProperties) ADD_TEST_MACRO(AliasTarget AliasTarget) ADD_TEST_MACRO(StagingPrefix StagingPrefix) ADD_TEST_MACRO(ImportedSameName ImportedSameName) diff --git a/Tests/CustomTransitiveProperties/CMakeLists.txt b/Tests/CustomTransitiveProperties/CMakeLists.txt new file mode 100644 index 0000000..219ec12 --- /dev/null +++ b/Tests/CustomTransitiveProperties/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.29) +cmake_policy(SET CMP0166 NEW) +project(CustomTransitiveProperties C) + +add_library(iface1 INTERFACE) +set_target_properties(iface1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_A" # LINK_ONLY not pierced + + INTERFACE_CUSTOM_A "CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_$>" + INTERFACE_CUSTOM_B "CUSTOM_B_IFACE1" + INTERFACE_CUSTOM_C "CUSTOM_C_IFACE1" + ) + +add_library(iface2 INTERFACE) +set_target_properties(iface2 PROPERTIES + INTERFACE_CUSTOM_A "CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_$" + ) +target_link_libraries(iface2 INTERFACE iface1) + +# Test that the INTERFACE prefix is removed. +set(unnecessary_INTERFACE_ "INTERFACE_") + +add_library(static1 STATIC static1.c) +target_link_libraries(static1 PRIVATE iface2) +set_target_properties(static1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_B" # LINK_ONLY not pierced + + CUSTOM_A "CUSTOM_A_STATIC1" + CUSTOM_B "CUSTOM_B_STATIC1" + INTERFACE_CUSTOM_A "CUSTOM_A_STATIC1_IFACE" + INTERFACE_CUSTOM_B "CUSTOM_B_STATIC1_IFACE" + ) +target_compile_definitions(static1 PRIVATE + $ + $ + ) + +add_library(object1 OBJECT object1.c) +target_link_libraries(object1 PRIVATE iface2) +set_target_properties(object1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_C" # LINK_ONLY not pierced + + CUSTOM_A "CUSTOM_A_OBJECT1" + CUSTOM_C "CUSTOM_C_OBJECT1" + INTERFACE_CUSTOM_A "CUSTOM_A_OBJECT1_IFACE" + INTERFACE_CUSTOM_C "CUSTOM_C_OBJECT1_IFACE" + ) +target_compile_definitions(object1 PRIVATE + $ + $ + ) + +add_executable(CustomTransitiveProperties main.c) +target_link_libraries(CustomTransitiveProperties PRIVATE static1 object1) +set_target_properties(CustomTransitiveProperties PROPERTIES + CUSTOM_A "CUSTOM_A_MAIN" + CUSTOM_B "CUSTOM_B_MAIN" + CUSTOM_C "CUSTOM_C_MAIN" + ) + +# Test TRANSITIVE_*_PROPERTY evaluation within usage requirements. +target_compile_definitions(CustomTransitiveProperties PRIVATE + $ + $ + $ + ) + +# Test TRANSITIVE_*_PROPERTY evaluation outside of usage requirements. +set(out "${CMAKE_CURRENT_BINARY_DIR}/out-$.txt") +file(GENERATE OUTPUT "${out}" CONTENT "# file(GENERATE) produced: +iface1 CUSTOM_A: '$' +iface1 INTERFACE_CUSTOM_A: '$' +iface2 CUSTOM_A: '$' +iface2 INTERFACE_CUSTOM_A: '$' +static1 CUSTOM_A: '$' +static1 INTERFACE_CUSTOM_A: '$' +static1 CUSTOM_B: '$' +static1 INTERFACE_CUSTOM_B: '$' +object1 CUSTOM_A: '$' +object1 INTERFACE_CUSTOM_A: '$' +object1 CUSTOM_C: '$' +object1 INTERFACE_CUSTOM_C: '$' +main CUSTOM_A: '$' +main INTERFACE_CUSTOM_A: '$' +main CUSTOM_B: '$' +main INTERFACE_CUSTOM_B: '$' +main CUSTOM_C: '$' +main INTERFACE_CUSTOM_C: '$' +") +add_custom_target(check ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -Dconfig=$ -Dout=${out} -P${CMAKE_CURRENT_SOURCE_DIR}/check.cmake + COMMAND CustomTransitiveProperties + "$" "CUSTOM_A_STATIC1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_STATIC1" + "$" "CUSTOM_B_STATIC1;CUSTOM_B_IFACE1" + "$" "CUSTOM_A_OBJECT1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_OBJECT1" + "$" "CUSTOM_C_OBJECT1;CUSTOM_C_IFACE1" + "$" "CUSTOM_A_MAIN" + "$" "CUSTOM_B_MAIN;CUSTOM_B_STATIC1_IFACE" + "$" "CUSTOM_C_MAIN;CUSTOM_C_OBJECT1_IFACE" + ) diff --git a/Tests/CustomTransitiveProperties/check.cmake b/Tests/CustomTransitiveProperties/check.cmake new file mode 100644 index 0000000..2d024ce --- /dev/null +++ b/Tests/CustomTransitiveProperties/check.cmake @@ -0,0 +1,33 @@ +set(expect [[ +# file\(GENERATE\) produced: +iface1 CUSTOM_A: '' +iface1 INTERFACE_CUSTOM_A: 'CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_IFACE1' +iface2 CUSTOM_A: '' +iface2 INTERFACE_CUSTOM_A: 'CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_INTERFACE_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_IFACE2' +static1 CUSTOM_A: 'CUSTOM_A_STATIC1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_STATIC1' +static1 INTERFACE_CUSTOM_A: 'CUSTOM_A_STATIC1_IFACE' +static1 CUSTOM_B: 'CUSTOM_B_STATIC1;CUSTOM_B_IFACE1' +static1 INTERFACE_CUSTOM_B: 'CUSTOM_B_STATIC1_IFACE' +object1 CUSTOM_A: 'CUSTOM_A_OBJECT1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_OBJECT1' +object1 INTERFACE_CUSTOM_A: 'CUSTOM_A_OBJECT1_IFACE' +object1 CUSTOM_C: 'CUSTOM_C_OBJECT1;CUSTOM_C_IFACE1' +object1 INTERFACE_CUSTOM_C: 'CUSTOM_C_OBJECT1_IFACE' +main CUSTOM_A: 'CUSTOM_A_MAIN' +main INTERFACE_CUSTOM_A: '' +main CUSTOM_B: 'CUSTOM_B_MAIN;CUSTOM_B_STATIC1_IFACE' +main INTERFACE_CUSTOM_B: '' +main CUSTOM_C: 'CUSTOM_C_MAIN;CUSTOM_C_OBJECT1_IFACE' +main INTERFACE_CUSTOM_C: '' +]]) +string(REGEX REPLACE "\r\n" "\n" expect "${expect}") +string(REGEX REPLACE "\n+$" "" expect "${expect}") + +file(READ "${out}" 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/CustomTransitiveProperties/main.c b/Tests/CustomTransitiveProperties/main.c new file mode 100644 index 0000000..9ce600c --- /dev/null +++ b/Tests/CustomTransitiveProperties/main.c @@ -0,0 +1,68 @@ +#include +#include + +#ifdef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly defined" +#endif + +#ifdef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly defined" +#endif + +#ifdef CUSTOM_A_STATIC1_IFACE +# error "CUSTOM_A_STATIC1_IFACE incorrectly defined" +#endif + +#ifdef CUSTOM_A_OBJECT1_IFACE +# error "CUSTOM_A_OBJECT1_IFACE incorrectly defined" +#endif + +#ifndef CUSTOM_A_MAIN +# error "CUSTOM_A_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_B_IFACE1 +# error "CUSTOM_B_IFACE1 incorrectly defined" +#endif + +#ifndef CUSTOM_B_STATIC1_IFACE +# error "CUSTOM_B_STATIC1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_B_MAIN +# error "CUSTOM_B_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_C_IFACE1 +# error "CUSTOM_C_IFACE1 incorrectly defined" +#endif + +#ifndef CUSTOM_C_OBJECT1_IFACE +# error "CUSTOM_C_OBJECT1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_C_MAIN +# error "CUSTOM_C_MAIN incorrectly not defined" +#endif + +extern int static1(void); +extern int object1(void); + +int check_args(int argc, char** argv) +{ + int result = 0; + int i; + for (i = 2; i < argc; i += 2) { + if (strcmp(argv[i - 1], argv[i]) != 0) { + fprintf(stderr, "Argument %d expected '%s' but got '%s'.\n", i, argv[i], + argv[i - 1]); + result = 1; + } + } + return result; +} + +int main(int argc, char** argv) +{ + return static1() + object1() + check_args(argc, argv); +} diff --git a/Tests/CustomTransitiveProperties/object1.c b/Tests/CustomTransitiveProperties/object1.c new file mode 100644 index 0000000..dcc5224 --- /dev/null +++ b/Tests/CustomTransitiveProperties/object1.c @@ -0,0 +1,36 @@ +#ifndef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_OBJECT1 +# error "CUSTOM_A_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_NAME_OBJECT1 +# error "CUSTOM_A_TARGET_NAME_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY +# error "CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_C_IFACE1 +# error "CUSTOM_C_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_C_OBJECT1 +# error "CUSTOM_C_OBJECT1 incorrectly not defined" +#endif + +#ifdef CUSTOM_C_OBJECT1_IFACE +# error "CUSTOM_C_OBJECT1_IFACE incorrectly defined" +#endif + +int object1(void) +{ + return 0; +} diff --git a/Tests/CustomTransitiveProperties/static1.c b/Tests/CustomTransitiveProperties/static1.c new file mode 100644 index 0000000..857595b --- /dev/null +++ b/Tests/CustomTransitiveProperties/static1.c @@ -0,0 +1,36 @@ +#ifndef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_STATIC1 +# error "CUSTOM_A_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_NAME_STATIC1 +# error "CUSTOM_A_TARGET_NAME_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY +# error "CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_B_IFACE1 +# error "CUSTOM_B_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_B_STATIC1 +# error "CUSTOM_B_STATIC1 incorrectly not defined" +#endif + +#ifdef CUSTOM_B_STATIC1_IFACE +# error "CUSTOM_B_STATIC1_IFACE incorrectly defined" +#endif + +int static1(void) +{ + return 0; +} diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 0b7f739..20e7c08 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -116,6 +116,29 @@ target_link_libraries(testLib9 INTERFACE testLib9ObjIface PUBLIC testLib9ObjPub target_link_libraries(testLib9 PUBLIC Foo::Foo) cmake_policy(POP) +block() + cmake_policy(SET CMP0022 NEW) + add_library(testLib10 STATIC testLib10.c) + set_target_properties(testLib10 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_C" + INTERFACE_CUSTOM_C "TESTLIB10_INTERFACE_CUSTOM_C" + ) + target_compile_definitions(testLib10 INTERFACE + "$" + ) + add_library(testLib11 STATIC testLib11.c) + target_link_libraries(testLib11 PRIVATE testLib10) + set_target_properties(testLib11 PROPERTIES + INTERFACE_CUSTOM_C "TESTLIB11_INTERFACE_CUSTOM_C" + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_D" + INTERFACE_CUSTOM_D "TESTLIB11_INTERFACE_CUSTOM_D" + ) + target_compile_definitions(testLib11 INTERFACE + "$" + "$" + ) +endblock() + # Test using the target_link_libraries command to set the # LINK_INTERFACE_LIBRARIES* properties. We construct two libraries # providing the same two symbols. In each library one of the symbols @@ -574,6 +597,7 @@ install( testExe2lib testLib4lib testLib4libdbg testLib4libopt testLib6 testLib7 testLib8 testLib9 + testLib10 testLib11 testLibDeprecation testLibCycleA testLibCycleB testLibNoSONAME @@ -653,6 +677,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3 export(TARGETS testExe2 testLib4 testLib5 testLib6 testLib7 testExe3 testExe4 testExe2lib testLib8 testLib9 testLib9ObjPub testLib9ObjPriv testLib9ObjIface + testLib10 testLib11 testLibDeprecation testLib4lib testLib4libdbg testLib4libopt testLibCycleA testLibCycleB diff --git a/Tests/ExportImport/Export/testLib10.c b/Tests/ExportImport/Export/testLib10.c new file mode 100644 index 0000000..d5ecb7f --- /dev/null +++ b/Tests/ExportImport/Export/testLib10.c @@ -0,0 +1,4 @@ +int testLib10(void) +{ + return 0; +} diff --git a/Tests/ExportImport/Export/testLib11.c b/Tests/ExportImport/Export/testLib11.c new file mode 100644 index 0000000..b288b29 --- /dev/null +++ b/Tests/ExportImport/Export/testLib11.c @@ -0,0 +1,6 @@ +int testLib10(void); + +int testLib11(void) +{ + return testLib10(); +} diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 2a57633..632825d 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -328,6 +328,16 @@ foreach(vis Pub Priv Iface) endif() endforeach() +# Create executables to verify custom transitive properties. +add_executable(imp_testLib10 imp_testLib10.c) +target_link_libraries(imp_testLib10 PRIVATE exp_testLib10) +add_executable(imp_testLib10b imp_testLib10.c) +target_link_libraries(imp_testLib10b PRIVATE bld_testLib10) +add_executable(imp_testLib11 imp_testLib11.c) +target_link_libraries(imp_testLib11 PRIVATE exp_testLib11) +add_executable(imp_testLib11b imp_testLib11.c) +target_link_libraries(imp_testLib11b PRIVATE bld_testLib11) + #----------------------------------------------------------------------------- # Test that handling imported targets, including transitive dependencies, # works in CheckFunctionExists (...and hopefully all other try_compile() checks diff --git a/Tests/ExportImport/Import/A/imp_testLib10.c b/Tests/ExportImport/Import/A/imp_testLib10.c new file mode 100644 index 0000000..bac772c --- /dev/null +++ b/Tests/ExportImport/Import/A/imp_testLib10.c @@ -0,0 +1,10 @@ +#ifndef TESTLIB10_INTERFACE_CUSTOM_C +# error "TESTLIB10_INTERFACE_CUSTOM_C incorrectly not defined!" +#endif + +int testLib10(void); + +int main(void) +{ + return testLib10(); +} diff --git a/Tests/ExportImport/Import/A/imp_testLib11.c b/Tests/ExportImport/Import/A/imp_testLib11.c new file mode 100644 index 0000000..a17d7e7 --- /dev/null +++ b/Tests/ExportImport/Import/A/imp_testLib11.c @@ -0,0 +1,18 @@ +#ifdef TESTLIB10_INTERFACE_CUSTOM_C +# error "TESTLIB10_INTERFACE_CUSTOM_C incorrectly defined!" +#endif + +#ifdef TESTLIB11_INTERFACE_CUSTOM_C +# error "TESTLIB11_INTERFACE_CUSTOM_C incorrectly defined!" +#endif + +#ifndef TESTLIB11_INTERFACE_CUSTOM_D +# error "TESTLIB11_INTERFACE_CUSTOM_D incorrectly not defined!" +#endif + +int testLib11(void); + +int main(void) +{ + return testLib11(); +} -- cgit v0.12