diff options
80 files changed, 1442 insertions, 120 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index ddb917a..f4efd3c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -266,6 +266,8 @@ Properties on Targets /prop_tgt/INTERFACE_LINK_DEPENDS /prop_tgt/INTERFACE_LINK_DIRECTORIES /prop_tgt/INTERFACE_LINK_LIBRARIES + /prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT + /prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE /prop_tgt/INTERFACE_LINK_OPTIONS /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE /prop_tgt/INTERFACE_PRECOMPILE_HEADERS diff --git a/Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst index bf7f72f..53f5838 100644 --- a/Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst +++ b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst @@ -12,6 +12,13 @@ other target also. This property is overridden by the :prop_tgt:`LINK_INTERFACE_LIBRARIES_<CONFIG>` property if policy :policy:`CMP0022` is ``OLD`` or unset. +The value of this property is used by the generators when constructing +the link rule for a dependent target. A dependent target's direct +link dependencies, specified by its :prop_tgt:`LINK_LIBRARIES` target +property, are linked first, followed by indirect dependencies from the +transitive closure of the direct dependencies' +``INTERFACE_LINK_LIBRARIES`` properties. See policy :policy:`CMP0022`. + Contents of ``INTERFACE_LINK_LIBRARIES`` may use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual for available expressions. See the :manual:`cmake-buildsystem(7)` @@ -19,6 +26,12 @@ manual for more on defining buildsystem properties. .. include:: LINK_LIBRARIES_INDIRECTION.txt +``INTERFACE_LINK_LIBRARIES`` adds transitive link dependencies for a +target's dependents. In advanced use cases, one may update the +direct link dependencies of a target's dependents by using the +:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and +:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties. + Creating Relocatable Packages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.rst b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.rst new file mode 100644 index 0000000..1a6ebd1 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.rst @@ -0,0 +1,221 @@ +INTERFACE_LINK_LIBRARIES_DIRECT +------------------------------- + +List of libraries that consumers of this library should treat +as direct link dependencies. + +This target property may be set to *include* items in a dependent +target's final set of direct link dependencies. See the +:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target property +to exclude items. + +The initial set of a dependent target's direct link dependencies is +specified by its :prop_tgt:`LINK_LIBRARIES` target property. Indirect +link dependencies are specified by the transitive closure of the direct +link dependencies' :prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. +Any link dependency may specify additional direct link dependencies +using the ``INTERFACE_LINK_LIBRARIES_DIRECT`` target property. +The set of direct link dependencies is then filtered to exclude items named +by any dependency's :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` +target property. + +.. |INTERFACE_PROPERTY_LINK_DIRECT| replace:: ``INTERFACE_LINK_LIBRARIES_DIRECT`` +.. include:: INTERFACE_LINK_LIBRARIES_DIRECT.txt + +Direct Link Dependencies as Usage Requirements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``INTERFACE_PROPERTY_LINK_DIRECT`` and +``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` target properties +are :ref:`usage requirements <Target Usage Requirements>`. +Their effects propagate to dependent targets transitively, and can +therefore affect the direct link dependencies of every target in a +chain of dependent libraries. Whenever some library target ``X`` links +to another library target ``Y`` whose direct or transitive usage +requirements contain ``INTERFACE_PROPERTY_LINK_DIRECT`` or +``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, the properties may affect +``X``'s list of direct link dependencies: + +* If ``X`` is a shared library or executable, its dependencies are linked. + They also affect the usage requirements with which ``X``'s sources are + compiled. + +* If ``X`` is a static library or object library, it does not actually + link, so its dependencies at most affect the usage requirements with + which ``X``'s sources are compiled. + +The properties may also affect the list of direct link dependencies +on ``X``'s dependents: + +* If ``X`` links ``Y`` publicly: + + .. code-block:: cmake + + target_link_libraries(X PUBLIC Y) + + then ``Y`` is placed in ``X``'s :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + so ``Y``'s usage requirements, including ``INTERFACE_PROPERTY_LINK_DIRECT`` + and ``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, are propagated + to ``X``'s dependents. + +* If ``X`` links ``Y`` privately: + + .. code-block:: cmake + + target_link_libraries(X PRIVATE Y) + + then ``Y`` is not placed in ``X``'s :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + so ``Y``'s usage requirements, even ``INTERFACE_PROPERTY_LINK_DIRECT`` + and ``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, are not propagated + to ``X``'s dependents. + (If ``X`` is a static library or object library, then ``$<LINK_ONLY:Y>`` + is placed in ``X``'s :prop_tgt:`INTERFACE_LINK_LIBRARIES`, but the + :genex:`LINK_ONLY` generator expression block ``Y``'s usage requirements.) + +* In either case, the content of ``X``'s :prop_tgt:`INTERFACE_LINK_LIBRARIES` + is not affected by ``Y``'s ``INTERFACE_PROPERTY_LINK_DIRECT`` or + ``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``. + +One may limit the effects of ``INTERFACE_PROPERTY_LINK_DIRECT`` and +``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE`` to a subset of dependent +targets by using the :genex:`TARGET_PROPERTY` generator expression. +For example, to limit the effects to executable targets, use an +entry of the form:: + + "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:...>" + +Similarly, to limit the effects to specific targets, use an entry +of the form:: + + "$<$<BOOL:$<TARGET_PROPERTY:USE_IT>>:...>" + +This entry will only affect targets that set their ``USE_IT`` +target property to a true value. + +Direct Link Dependency Ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The list of direct link dependencies for a target is computed from an +initial ordered list in its :prop_tgt:`LINK_LIBRARIES` target property. +For each item, additional direct link dependencies are discovered from +its direct and transitive ``INTERFACE_LINK_LIBRARIES_DIRECT`` usage +requirements. Each discovered item is injected before the item that +specified it. However, a discovered item is added at most once, +and only if it did not appear anywhere in the initial list. +This gives :prop_tgt:`LINK_LIBRARIES` control over ordering of +those direct link dependencies that it explicitly specifies. + +Once all direct link dependencies have been collected, items named by +all of their :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` +usage requirements are removed from the final list. This does not +affect the order of the items that remain. + +Example: Static Plugins +^^^^^^^^^^^^^^^^^^^^^^^ + +Consider a static library ``Foo`` that provides a static plugin +``FooPlugin`` to consuming application executables, where the +implementation of the plugin depends on ``Foo`` and other things. +In this case, the application should link to ``FooPlugin`` directly, +before ``Foo``. However, the application author only knows about ``Foo``. +We can express this as follows: + +.. code-block:: cmake + + # Core library used by other components. + add_library(Core STATIC core.cpp) + + # Foo is a static library for use by applications. + # Implementation of Foo depends on Core. + add_library(Foo STATIC foo.cpp foo_plugin_helper.cpp) + target_link_libraries(Foo PRIVATE Core) + + # Extra parts of Foo for use by its static plugins. + # Implementation of Foo's extra parts depends on both Core and Foo. + add_library(FooExtras STATIC foo_extras.cpp) + target_link_libraries(FooExtras PRIVATE Core Foo) + + # The Foo library has an associated static plugin + # that should be linked into the final executable. + # Implementation of the plugin depends on Core, Foo, and FooExtras. + add_library(FooPlugin STATIC foo_plugin.cpp) + target_link_libraries(FooPlugin PRIVATE Core Foo FooExtras) + + # An app that links Foo should link Foo's plugin directly. + set_property(TARGET Foo PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT FooPlugin) + + # An app does not need to link Foo directly because the plugin links it. + set_property(TARGET Foo PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE Foo) + +An application ``app`` only needs to specify that it links to ``Foo``: + +.. code-block:: cmake + + add_executable(app main.cpp) + target_link_libraries(app PRIVATE Foo) + +The ``INTERFACE_LINK_LIBRARIES_DIRECT`` target property on ``Foo`` tells +CMake to pretend that ``app`` also links directly to ``FooPlugin``. +The ``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` target property on ``Foo`` +tells CMake to pretend that ``app`` did *not* link directly to ``Foo``. +Instead, ``Foo`` will be linked as a dependency of ``FooPlugin``. The +final link line for ``app`` will link the libraries in the following +order: + +* ``FooPlugin`` as a direct link dependency of ``app`` + (via ``Foo``'s usage requiremens). +* ``FooExtras`` as a dependency of ``FooPlugin``. +* ``Foo`` as a dependency of ``FooPlugin`` and ``FooExtras``. +* ``Core`` as a dependency of ``FooPlugin``, ``FooExtras``, and ``Foo``. + +Note that without the ``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` target +property, ``Foo`` would be linked twice: once as a direct dependency +of ``app``, and once as a dependency of ``FooPlugin``. + +Example: Opt-In Static Plugins +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the above `Example: Static Plugins`_, the ``app`` executable specifies +that it links directly to ``Foo``. In a real application, there might +be an intermediate library: + +.. code-block:: cmake + + add_library(app_impl STATIC app_impl.cpp) + target_link_libraries(app_impl PUBLIC Foo) + + add_executable(app main.cpp) + target_link_libraries(app PRIVATE app_impl) + +In this case we do not want ``Foo``'s ``INTERFACE_LINK_LIBRARIES_DIRECT`` +and ``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` target properties to affect +the direct dependencies of ``app_impl``. To avoid this, we can revise +the property values to make their effects opt-in: + +.. code-block:: cmake + + # An app that links Foo should link Foo's plugin directly. + set_property(TARGET Foo PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT + "$<$<BOOL:$<TARGET_PROPERTY:FOO_STATIC_PLUGINS>>:FooPlugin>" + ) + + # An app does not need to link Foo directly because the plugin links it. + set_property(TARGET Foo PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE + "$<$<BOOL:$<TARGET_PROPERTY:FOO_STATIC_PLUGINS>>:Foo>" + ) + +Now, the ``app`` executable can opt-in to get ``Foo``'s plugin(s): + +.. code-block:: cmake + + set_property(TARGET app PROPERTY FOO_STATIC_PLUGINS 1) + +The final link line for ``app`` will now link the libraries in the following +order: + +* ``FooPlugin`` as a direct link dependency of ``app`` + (via ``Foo``'s usage requiremens). +* ``app_impl`` as a direct link dependency of ``app``. +* ``FooExtras`` as a dependency of ``FooPlugin``. +* ``Foo`` as a dependency of ``app_impl``, ``FooPlugin``, and ``FooExtras``. +* ``Core`` as a dependency of ``FooPlugin``, ``FooExtras``, and ``Foo``. diff --git a/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.txt b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.txt new file mode 100644 index 0000000..077af42 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT.txt @@ -0,0 +1,9 @@ +The value of |INTERFACE_PROPERTY_LINK_DIRECT| may use +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +.. note:: + + The |INTERFACE_PROPERTY_LINK_DIRECT| target property is intended for + advanced use cases such as injection of static plugins into a consuming + executable. It should not be used as a substitute for organizing + normal calls to :command:`target_link_libraries`. diff --git a/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE.rst b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE.rst new file mode 100644 index 0000000..ecab8a0 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE.rst @@ -0,0 +1,32 @@ +INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE +--------------------------------------- + +List of libraries that consumers of this library should *not* treat +as direct link dependencies. + +This target property may be set to *exclude* items from a dependent +target's final set of direct link dependencies. This property is +processed after the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` +target property of all other dependencies of the dependent target, so +exclusion from direct link dependence takes priority over inclusion. + +The initial set of a dependent target's direct link dependencies is +specified by its :prop_tgt:`LINK_LIBRARIES` target property. Indirect +link dependencies are specified by the transitive closure of the direct +link dependencies' :prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. +Any link dependency may specify additional direct link dependencies +using the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target property. +The set of direct link dependencies is then filtered to exclude items named +by any dependency's ``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` target +property. + +Excluding an item from a dependent target's direct link dependencies +does not mean the dependent target won't link the item. The item +may still be linked as an indirect link dependency via the +:prop_tgt:`INTERFACE_LINK_LIBRARIES` property on other dependencies. + +.. |INTERFACE_PROPERTY_LINK_DIRECT| replace:: ``INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE`` +.. include:: INTERFACE_LINK_LIBRARIES_DIRECT.txt + +See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target property +documentation for more details and examples. diff --git a/Help/prop_tgt/LINK_LIBRARIES.rst b/Help/prop_tgt/LINK_LIBRARIES.rst index d88e798..ae5334a 100644 --- a/Help/prop_tgt/LINK_LIBRARIES.rst +++ b/Help/prop_tgt/LINK_LIBRARIES.rst @@ -8,8 +8,11 @@ used for linking. In addition to accepting values from the :command:`target_link_libraries` command, values may be set directly on any target using the :command:`set_property` command. -The value of this property is used by the generators to set the link -libraries for the compiler. +The value of this property is used by the generators to construct the +link rule for the target. The direct link dependencies are linked first, +followed by indirect dependencies from the transitive closure of the +direct dependencies' :prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. +See policy :policy:`CMP0022`. Contents of ``LINK_LIBRARIES`` may use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual @@ -17,3 +20,8 @@ for available expressions. See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. .. include:: LINK_LIBRARIES_INDIRECTION.txt + +In advanced use cases, the list of direct link dependencies specified +by this property may be updated by usage requirements from dependencies. +See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and +:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties. diff --git a/Help/release/dev/link-interface-direct.rst b/Help/release/dev/link-interface-direct.rst new file mode 100644 index 0000000..2e9a59e --- /dev/null +++ b/Help/release/dev/link-interface-direct.rst @@ -0,0 +1,7 @@ +link-interface-direct +--------------------- + +* The :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and + :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties + were added to express usage requirements affecting a consumer's + direct link dependencies. diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 896240c..412d104 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportFileGenerator.h" +#include <array> #include <cassert> #include <cstring> #include <sstream> @@ -175,18 +176,24 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( if (!target->IsLinkable()) { return false; } - cmValue input = target->GetProperty("INTERFACE_LINK_LIBRARIES"); - if (input) { - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions( - prepro, target, missingTargets, ReplaceFreeTargets); - properties["INTERFACE_LINK_LIBRARIES"] = prepro; - return true; + static const std::array<std::string, 3> linkIfaceProps = { + { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT", + "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" } + }; + bool hadINTERFACE_LINK_LIBRARIES = false; + for (std::string const& linkIfaceProp : linkIfaceProps) { + if (cmValue input = target->GetProperty(linkIfaceProp)) { + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions( + prepro, target, missingTargets, ReplaceFreeTargets); + properties[linkIfaceProp] = prepro; + hadINTERFACE_LINK_LIBRARIES = true; + } } } - return false; + return hadINTERFACE_LINK_LIBRARIES; } static bool isSubDirectory(std::string const& a, std::string const& b) diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index db9b05b..e98aa05 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -111,6 +111,8 @@ void cmExportTryCompileFileGenerator::PopulateProperties( std::vector<std::string> props = target->GetPropertyKeys(); // Include special properties that might be relevant here. props.emplace_back("INTERFACE_LINK_LIBRARIES"); + props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT"); + props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"); for (std::string const& p : props) { cmValue v = target->GetProperty(p); if (!v) { diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index a1fce55..d4b02a5 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -188,11 +188,13 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries( return top->Target == tgt && prop == "LINK_LIBRARIES"_s; } - return prop == "LINK_LIBRARIES"_s || prop == "LINK_INTERFACE_LIBRARIES"_s || + return prop == "LINK_LIBRARIES"_s || prop == "INTERFACE_LINK_LIBRARIES"_s || + prop == "INTERFACE_LINK_LIBRARIES_DIRECT"_s || + prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s || + prop == "LINK_INTERFACE_LIBRARIES"_s || prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s || cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") || - cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_") || - prop == "INTERFACE_LINK_LIBRARIES"_s; + cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_"); } cmGeneratorExpressionDAGChecker const* cmGeneratorExpressionDAGChecker::Top() diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 45e3b64..c4f1a13 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -57,6 +57,11 @@ using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor; const cmsys::RegularExpression FrameworkRegularExpression( "^(.*/)?([^/]*)\\.framework/(.*)$"); +const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT = + "INTERFACE_LINK_LIBRARIES_DIRECT"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE = + "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"; } template <> @@ -749,6 +754,12 @@ void cmGeneratorTarget::ClearSourcesCache() this->LinkImplMap.clear(); } +void cmGeneratorTarget::ClearLinkInterfaceCache() +{ + this->LinkInterfaceMap.clear(); + this->LinkInterfaceUsageRequirementsOnlyMap.clear(); +} + void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before) { this->SourceEntries.insert( @@ -3645,7 +3656,7 @@ void processIncludeDirectories(cmGeneratorTarget const* tgt, cmLinkImplItem const& item = entry.LinkImplItem; std::string const& targetName = item.AsStr(); bool const fromImported = item.Target && item.Target->IsImported(); - bool const checkCMP0027 = item.FromGenex; + bool const checkCMP0027 = item.CheckCMP0027; std::string usedIncludes; for (std::string& entryInclude : entry.Values) { @@ -6638,7 +6649,7 @@ bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( std::string const& n, cmListFileBacktrace const& bt, - LookupLinkItemScope* scope) const + LookupLinkItemScope* scope, LookupSelf lookupSelf) const { cm::optional<cmLinkItem> maybeItem; if (this->IsLinkLookupScope(n, scope->LG)) { @@ -6646,20 +6657,22 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( } std::string name = this->CheckCMP0004(n); - if (name == this->GetName() || name.empty()) { + if (name.empty() || + (lookupSelf == LookupSelf::No && name == this->GetName())) { return maybeItem; } maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG); return maybeItem; } -void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, - cmBTStringRange entries, - std::string const& config, - cmGeneratorTarget const* headTarget, - LinkInterfaceFor interfaceFor, - cmLinkInterface& iface) const +void cmGeneratorTarget::ExpandLinkItems( + std::string const& prop, cmBTStringRange entries, std::string const& config, + cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor, + LinkInterfaceField field, cmLinkInterface& iface) const { + if (entries.empty()) { + return; + } // Keep this logic in sync with ComputeLinkImplementationLibraries. cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr); // The $<LINK_ONLY> expression may be in a link interface to specify @@ -6678,10 +6691,20 @@ void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this, headTarget->LinkerLanguage)); for (std::string const& lib : libs) { - if (cm::optional<cmLinkItem> maybeItem = - this->LookupLinkItem(lib, cge->GetBacktrace(), &scope)) { + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + lib, cge->GetBacktrace(), &scope, + field == LinkInterfaceField::Libraries ? LookupSelf::No + : LookupSelf::Yes)) { cmLinkItem item = std::move(*maybeItem); + if (field == LinkInterfaceField::HeadInclude) { + iface.HeadInclude.emplace_back(std::move(item)); + continue; + } + if (field == LinkInterfaceField::HeadExclude) { + iface.HeadExclude.emplace_back(std::move(item)); + continue; + } if (!item.Target) { // Report explicitly linked object files separately. std::string const& maybeObj = item.AsStr(); @@ -6736,17 +6759,16 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( // Lookup any existing link interface for this configuration. cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); - if (secondPass) { - hm.erase(head); - } - // If the link interface does not depend on the head target - // then return the one we computed first. + // then re-use the one from the head we computed first. if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - return &hm.begin()->second; + head = hm.begin()->first; } cmOptionalLinkInterface& iface = hm[head]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } if (!iface.LibrariesDone) { iface.LibrariesDone = true; this->ComputeLinkInterfaceLibraries(config, iface, head, @@ -6865,9 +6887,9 @@ const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( : this->GetHeadToLinkInterfaceMap(config)); // If the link interface does not depend on the head target - // then return the one we computed first. + // then re-use the one from the head we computed first. if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - return &hm.begin()->second; + head = hm.begin()->first; } cmOptionalLinkInterface& iface = hm[head]; @@ -7146,59 +7168,66 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries( // An explicit list of interface libraries may be set for shared // libraries and executables that export symbols. - cmValue explicitLibraries = nullptr; - std::string linkIfaceProp; + bool haveExplicitLibraries = false; + cmValue explicitLibrariesCMP0022OLD; + std::string linkIfacePropCMP0022OLD; bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && this->GetPolicyStatusCMP0022() != cmPolicies::WARN); if (cmp0022NEW) { // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. - linkIfaceProp = "INTERFACE_LINK_LIBRARIES"; - explicitLibraries = this->GetProperty(linkIfaceProp); - } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->IsExecutableWithExports()) { + haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() || + !this->Target->GetLinkInterfaceDirectEntries().empty() || + !this->Target->GetLinkInterfaceDirectExcludeEntries().empty(); + } else { // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a // shared lib or executable. - - // Lookup the per-configuration property. - linkIfaceProp = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); - explicitLibraries = this->GetProperty(linkIfaceProp); - - // If not set, try the generic property. - if (!explicitLibraries) { - linkIfaceProp = "LINK_INTERFACE_LIBRARIES"; - explicitLibraries = this->GetProperty(linkIfaceProp); + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->IsExecutableWithExports()) { + // Lookup the per-configuration property. + linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); + explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD); + + // If not set, try the generic property. + if (!explicitLibrariesCMP0022OLD) { + linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES"; + explicitLibrariesCMP0022OLD = + this->GetProperty(linkIfacePropCMP0022OLD); + } } - } - if (explicitLibraries && - this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022) { - // Compare the explicitly set old link interface properties to the - // preferred new link interface property one and warn if different. - cmValue newExplicitLibraries = - this->GetProperty("INTERFACE_LINK_LIBRARIES"); - if (newExplicitLibraries && - (*newExplicitLibraries != *explicitLibraries)) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property which differs from its " << - linkIfaceProp << " properties." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << *newExplicitLibraries << "\n" << - linkIfaceProp << ":\n" - " " << *explicitLibraries << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); - this->PolicyWarnedCMP0022 = true; + if (explicitLibrariesCMP0022OLD && + this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022) { + // Compare the explicitly set old link interface properties to the + // preferred new link interface property one and warn if different. + cmValue newExplicitLibraries = + this->GetProperty("INTERFACE_LINK_LIBRARIES"); + if (newExplicitLibraries && + (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property which differs from its " << + linkIfacePropCMP0022OLD << " properties." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << *newExplicitLibraries << "\n" << + linkIfacePropCMP0022OLD << ":\n" + " " << *explicitLibrariesCMP0022OLD << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } } + + haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD); } // There is no implicit link interface for executables or modules // so if none was explicitly set then there is no link interface. - if (!explicitLibraries && + if (!haveExplicitLibraries && (this->GetType() == cmStateEnums::EXECUTABLE || (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { return; @@ -7208,22 +7237,29 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries( // If CMP0022 is NEW then the plain tll signature sets the // INTERFACE_LINK_LIBRARIES property. Even if the project // clears it, the link interface is still explicit. - iface.Explicit = cmp0022NEW || explicitLibraries; - - if (explicitLibraries) { - // The interface libraries have been explicitly set. - if (cmp0022NEW) { - // The explicitLibraries came from INTERFACE_LINK_LIBRARIES. - // Use its special representation directly to get backtraces. - this->ExpandLinkItems(linkIfaceProp, - this->Target->GetLinkInterfaceEntries(), config, - headTarget, interfaceFor, iface); - } else { - std::vector<BT<std::string>> entries; - entries.emplace_back(*explicitLibraries); - this->ExpandLinkItems(linkIfaceProp, cmMakeRange(entries), config, - headTarget, interfaceFor, iface); - } + iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD; + + if (cmp0022NEW) { + // The interface libraries are specified by INTERFACE_LINK_LIBRARIES. + // Use its special representation directly to get backtraces. + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + this->Target->GetLinkInterfaceDirectEntries(), + config, headTarget, interfaceFor, + LinkInterfaceField::HeadInclude, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + this->Target->GetLinkInterfaceDirectExcludeEntries(), + config, headTarget, interfaceFor, + LinkInterfaceField::HeadExclude, iface); + } else if (explicitLibrariesCMP0022OLD) { + // The interface libraries have been explicitly set in pre-CMP0022 style. + std::vector<BT<std::string>> entries; + entries.emplace_back(*explicitLibrariesCMP0022OLD); + this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries), + config, headTarget, interfaceFor, + LinkInterfaceField::Libraries, iface); } // If the link interface is explicit, do not fall back to the link impl. @@ -7241,13 +7277,10 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries( // Compare the link implementation fallback link interface to the // preferred new link interface property and warn if different. cmLinkInterface ifaceNew; - static const std::string newProp = "INTERFACE_LINK_LIBRARIES"; - if (cmValue newExplicitLibraries = this->GetProperty(newProp)) { - std::vector<BT<std::string>> entries; - entries.emplace_back(*newExplicitLibraries); - this->ExpandLinkItems(linkIfaceProp, cmMakeRange(entries), config, - headTarget, interfaceFor, ifaceNew); - } + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES, + this->Target->GetLinkInterfaceEntries(), config, + headTarget, interfaceFor, + LinkInterfaceField::Libraries, ifaceNew); if (ifaceNew.Libraries != iface.Libraries) { std::string oldLibraries = cmJoin(impl->Libraries, ";"); std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); @@ -7372,29 +7405,37 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) : this->GetHeadToLinkInterfaceMap(config)); - if (secondPass) { - hm.erase(headTarget); - } - // If the link interface does not depend on the head target - // then return the one we computed first. + // then re-use the one from the head we computed first. if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - return &hm.begin()->second; + headTarget = hm.begin()->first; } cmOptionalLinkInterface& iface = hm[headTarget]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } if (!iface.AllDone) { iface.AllDone = true; iface.LibrariesDone = true; iface.Multiplicity = info->Multiplicity; cmExpandList(info->Languages, iface.Languages); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + cmMakeRange(info->LibrariesHeadInclude), config, + headTarget, interfaceFor, + LinkInterfaceField::HeadInclude, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + cmMakeRange(info->LibrariesHeadExclude), config, + headTarget, interfaceFor, + LinkInterfaceField::HeadExclude, iface); this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), - config, headTarget, interfaceFor, iface); + config, headTarget, interfaceFor, + LinkInterfaceField::Libraries, iface); std::vector<std::string> deps = cmExpandedList(info->SharedDeps); LookupLinkItemScope scope{ this->LocalGenerator }; for (std::string const& dep : deps) { - if (cm::optional<cmLinkItem> maybeItem = - this->LookupLinkItem(dep, cmListFileBacktrace(), &scope)) { + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + dep, cmListFileBacktrace(), &scope, LookupSelf::No)) { iface.SharedDeps.emplace_back(std::move(*maybeItem)); } } @@ -7482,6 +7523,14 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config, } } } + for (BT<std::string> const& entry : + this->Target->GetLinkInterfaceDirectEntries()) { + info.LibrariesHeadInclude.emplace_back(entry); + } + for (BT<std::string> const& entry : + this->Target->GetLinkInterfaceDirectExcludeEntries()) { + info.LibrariesHeadExclude.emplace_back(entry); + } if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { if (loc) { info.LibName = *loc; @@ -7896,9 +7945,9 @@ cmGeneratorTarget::GetLinkImplementationLibrariesInternal( this->LinkImplMap[cmSystemTools::UpperCase(config)]; // If the link implementation does not depend on the head target - // then return the one we computed first. + // then re-use the one from the head we computed first. if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - return &hm.begin()->second; + head = hm.begin()->first; } cmOptionalLinkImplementation& impl = hm[head]; @@ -7915,6 +7964,112 @@ bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( return cm::contains(this->LinkImplicitNullProperties, p); } +namespace { +class TransitiveLinkImpl +{ + cmGeneratorTarget const* Self; + std::string const& Config; + cmLinkImplementation& Impl; + + std::set<cmLinkItem> Emitted; + std::set<cmLinkItem> Excluded; + std::unordered_set<cmGeneratorTarget const*> Followed; + + void Follow(cmGeneratorTarget const* target); + +public: + TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config, + cmLinkImplementation& impl) + : Self(self) + , Config(config) + , Impl(impl) + { + } + + void Compute(); +}; + +void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target) +{ + if (!target || !this->Followed.insert(target).second || + target->GetPolicyStatusCMP0022() == cmPolicies::OLD || + target->GetPolicyStatusCMP0022() == cmPolicies::WARN) { + return; + } + + // Get this target's usage requirements. + cmLinkInterfaceLibraries const* iface = target->GetLinkInterfaceLibraries( + this->Config, this->Self, LinkInterfaceFor::Usage); + if (!iface) { + return; + } + if (iface->HadContextSensitiveCondition) { + this->Impl.HadContextSensitiveCondition = true; + } + + // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements. + for (cmLinkItem const& item : iface->HeadInclude) { + // Inject direct dependencies from the item's usage requirements + // before the item itself. + this->Follow(item.Target); + + // Add the item itself, but at most once. + if (this->Emitted.insert(item).second) { + this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false); + } + } + + // Follow transitive dependencies. + for (cmLinkItem const& item : iface->Libraries) { + this->Follow(item.Target); + } + + // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements. + for (cmLinkItem const& item : iface->HeadExclude) { + this->Excluded.insert(item); + } +} + +void TransitiveLinkImpl::Compute() +{ + // Save the original items and start with an empty list. + std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries); + + // Avoid injecting any original items as usage requirements. + // This gives LINK_LIBRARIES final control over the order + // if it explicitly lists everything. + this->Emitted.insert(original.cbegin(), original.cend()); + + // Process each original item. + for (cmLinkImplItem& item : original) { + // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT' + // usage requirements before the item itself. + this->Follow(item.Target); + + // Add the item itself. + this->Impl.Libraries.emplace_back(std::move(item)); + } + + // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements found through any dependency above. + this->Impl.Libraries.erase( + std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(), + [this](cmLinkImplItem const& item) { + return this->Excluded.find(item) != this->Excluded.end(); + }), + this->Impl.Libraries.end()); +} + +void ComputeLinkImplTransitive(cmGeneratorTarget const* self, + std::string const& config, + cmLinkImplementation& impl) +{ + TransitiveLinkImpl transitiveLinkImpl(self, config, impl); + transitiveLinkImpl.Compute(); +} +} + void cmGeneratorTarget::ComputeLinkImplementationLibraries( const std::string& config, cmOptionalLinkImplementation& impl, cmGeneratorTarget const* head) const @@ -7935,7 +8090,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( std::string const& evaluated = cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, this->LinkerLanguage); - bool const fromGenex = evaluated != entry.Value; + bool const checkCMP0027 = evaluated != entry.Value; cmExpandList(evaluated, llibs); if (cge->GetHadHeadSensitiveCondition()) { impl.HadHeadSensitiveCondition = true; @@ -8009,7 +8164,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( } } - impl.Libraries.emplace_back(std::move(item), fromGenex); + impl.Libraries.emplace_back(std::move(item), checkCMP0027); } std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); @@ -8021,6 +8176,11 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); } + // Update the list of direct link dependencies from usage requirements. + if (head == this) { + ComputeLinkImplTransitive(this, config, impl); + } + // Get the list of configurations considered to be DEBUG. std::vector<std::string> debugConfigs = this->Makefile->GetCMakeInstance()->GetDebugConfigs(); diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index a58dc93..45639c0 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -667,6 +667,9 @@ public: */ void ClearSourcesCache(); + // Do not use. This is only for a specific call site with a FIXME comment. + void ClearLinkInterfaceCache(); + void AddSource(const std::string& src, bool before = false); void AddTracedSources(std::vector<std::string> const& srcs); @@ -1001,8 +1004,10 @@ private: std::string ImportLibrary; std::string LibName; std::string Languages; - std::vector<BT<std::string>> Libraries; std::string LibrariesProp; + std::vector<BT<std::string>> Libraries; + std::vector<BT<std::string>> LibrariesHeadInclude; + std::vector<BT<std::string>> LibrariesHeadExclude; std::string SharedDeps; }; @@ -1063,19 +1068,31 @@ private: bool IsLinkLookupScope(std::string const& n, cmLocalGenerator const*& lg) const; + enum class LinkInterfaceField + { + Libraries, + HeadExclude, + HeadInclude, + }; void ExpandLinkItems(std::string const& prop, cmBTStringRange entries, std::string const& config, const cmGeneratorTarget* headTarget, - LinkInterfaceFor interfaceFor, + LinkInterfaceFor interfaceFor, LinkInterfaceField field, cmLinkInterface& iface) const; struct LookupLinkItemScope { cmLocalGenerator const* LG; }; + enum class LookupSelf + { + No, + Yes, + }; cm::optional<cmLinkItem> LookupLinkItem(std::string const& n, cmListFileBacktrace const& bt, - LookupLinkItemScope* scope) const; + LookupLinkItemScope* scope, + LookupSelf lookupSelf) const; std::vector<BT<std::string>> GetSourceFilePaths( std::string const& config) const; diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx index 62e7ef4..2dc40ff 100644 --- a/Source/cmLinkItem.cxx +++ b/Source/cmLinkItem.cxx @@ -68,8 +68,8 @@ cmLinkImplItem::cmLinkImplItem() { } -cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool fromGenex) +cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool checkCMP0027) : cmLinkItem(std::move(item)) - , FromGenex(fromGenex) + , CheckCMP0027(checkCMP0027) { } diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h index 0863edd..262728b 100644 --- a/Source/cmLinkItem.h +++ b/Source/cmLinkItem.h @@ -40,8 +40,8 @@ class cmLinkImplItem : public cmLinkItem { public: cmLinkImplItem(); - cmLinkImplItem(cmLinkItem item, bool fromGenex); - bool FromGenex = false; + cmLinkImplItem(cmLinkItem item, bool checkCMP0027); + bool CheckCMP0027 = false; }; /** The link implementation specifies the direct library @@ -70,6 +70,12 @@ struct cmLinkInterfaceLibraries // Object files listed in the interface. std::vector<cmLinkItem> Objects; + // Items to be included as if directly linked by the head target. + std::vector<cmLinkItem> HeadInclude; + + // Items to be excluded from direct linking by the head target. + std::vector<cmLinkItem> HeadExclude; + // Whether the list depends on a genex referencing the head target. bool HadHeadSensitiveCondition = false; diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 50e6cdc..2adb232 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -2615,10 +2615,15 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) true); } else if (reuseTarget->GetType() == cmStateEnums::OBJECT_LIBRARY) { + // FIXME: This can propagate more than one level, unlike + // the rest of the object files in an object library. + // Find another way to do this. target->Target->AppendProperty( "INTERFACE_LINK_LIBRARIES", cmStrCat("$<$<CONFIG:", config, ">:$<LINK_ONLY:", pchSourceObj, ">>")); + // We updated the link interface, so ensure it is recomputed. + target->ClearLinkInterfaceCache(); } } } else { diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ad19e03..87fce92 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -202,6 +202,8 @@ public: std::vector<BT<std::string>> LinkDirectoriesEntries; std::vector<BT<std::string>> LinkImplementationPropertyEntries; std::vector<BT<std::string>> LinkInterfacePropertyEntries; + std::vector<BT<std::string>> LinkInterfaceDirectPropertyEntries; + std::vector<BT<std::string>> LinkInterfaceDirectExcludePropertyEntries; std::vector<BT<std::string>> HeaderSetsEntries; std::vector<BT<std::string>> InterfaceHeaderSetsEntries; std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>> @@ -1138,6 +1140,16 @@ cmBTStringRange cmTarget::GetLinkInterfaceEntries() const return cmMakeRange(this->impl->LinkInterfacePropertyEntries); } +cmBTStringRange cmTarget::GetLinkInterfaceDirectEntries() const +{ + return cmMakeRange(this->impl->LinkInterfaceDirectPropertyEntries); +} + +cmBTStringRange cmTarget::GetLinkInterfaceDirectExcludeEntries() const +{ + return cmMakeRange(this->impl->LinkInterfaceDirectExcludePropertyEntries); +} + cmBTStringRange cmTarget::GetHeaderSetsEntries() const { return cmMakeRange(this->impl->HeaderSetsEntries); @@ -1182,6 +1194,8 @@ MAKE_PROP(HEADER_SET); MAKE_PROP(HEADER_SETS); MAKE_PROP(INTERFACE_HEADER_SETS); MAKE_PROP(INTERFACE_LINK_LIBRARIES); +MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT); +MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE); #undef MAKE_PROP } @@ -1313,6 +1327,19 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value) cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt); } + } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) { + this->impl->LinkInterfaceDirectPropertyEntries.clear(); + if (value) { + cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt); + } + } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) { + this->impl->LinkInterfaceDirectExcludePropertyEntries.clear(); + if (value) { + cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value, + lfbt); + } } else if (prop == propSOURCES) { this->impl->SourceEntries.clear(); if (value) { @@ -1571,6 +1598,17 @@ void cmTarget::AppendProperty(const std::string& prop, cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt); } + } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt); + } + } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value, + lfbt); + } } else if (prop == "SOURCES") { cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); this->impl->SourceEntries.emplace_back(value, lfbt); @@ -1881,6 +1919,8 @@ cmValue cmTarget::GetProperty(const std::string& prop) const propHEADER_SETS, propINTERFACE_HEADER_SETS, propINTERFACE_LINK_LIBRARIES, + propINTERFACE_LINK_LIBRARIES_DIRECT, + propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, }; if (specialProps.count(prop)) { if (prop == propC_STANDARD || prop == propCXX_STANDARD || @@ -1910,6 +1950,25 @@ cmValue cmTarget::GetProperty(const std::string& prop) const output = cmJoin(this->impl->LinkInterfacePropertyEntries, ";"); return cmValue(output); } + if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) { + if (this->impl->LinkInterfaceDirectPropertyEntries.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->impl->LinkInterfaceDirectPropertyEntries, ";"); + return cmValue(output); + } + if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) { + if (this->impl->LinkInterfaceDirectExcludePropertyEntries.empty()) { + return nullptr; + } + + static std::string output; + output = + cmJoin(this->impl->LinkInterfaceDirectExcludePropertyEntries, ";"); + return cmValue(output); + } // the type property returns what type the target is if (prop == propTYPE) { return cmValue(cmState::GetTargetTypeName(this->GetType())); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 1173f49..18e39c7 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -266,6 +266,8 @@ public: cmBTStringRange GetLinkImplementationEntries() const; cmBTStringRange GetLinkInterfaceEntries() const; + cmBTStringRange GetLinkInterfaceDirectEntries() const; + cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; cmBTStringRange GetHeaderSetsEntries() const; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index b1f473b..da6219e 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -3691,6 +3691,8 @@ if(BUILD_TESTING) --test-command InterfaceLinkLibraries) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/InterfaceLinkLibraries") + ADD_TEST_MACRO(InterfaceLinkLibrariesDirect) + if(NOT CMake_TEST_EXTERNAL_CMAKE) add_subdirectory(CMakeTests) endif() diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index a79efd0..c9e41f5 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -163,6 +163,16 @@ install( cmake_policy(PUSH) cmake_policy(SET CMP0022 NEW) + +# Test control over direct linking. +include(../../InterfaceLinkLibrariesDirect/testStaticLibPlugin.cmake) +include(../../InterfaceLinkLibrariesDirect/testSharedLibWithHelper.cmake) +include(../../InterfaceLinkLibrariesDirect/testExeWithPluginHelper.cmake) +if(NOT maybe_OBJECTS_DESTINATION) + target_compile_definitions(testSharedLibHelperObj INTERFACE testSharedLibHelperObj_NO_OBJECT) + target_compile_definitions(testExePluginHelperObj INTERFACE testExePluginHelperObj_NO_OBJECT) +endif() + # Test exporting dependent libraries into different exports add_library(testLibRequired testLibRequired.c) add_library(testLibDepends testLibDepends.c) @@ -544,6 +554,9 @@ install( testLibDeprecation testLibCycleA testLibCycleB testLibNoSONAME + testStaticLibWithPlugin testStaticLibPluginExtra testStaticLibPlugin + testSharedLibWithHelper testSharedLibHelperObj + testExeWithPluginHelper testExePluginHelperObj testMod1 testMod2 cmp0022NEW cmp0022OLD TopDirLib SubDirLinkA @@ -619,6 +632,9 @@ export(TARGETS testExe2 testLib4 testLib5 testLib6 testLib7 testExe3 testExe4 te testLib4lib testLib4libdbg testLib4libopt testLibCycleA testLibCycleB testLibNoSONAME + testStaticLibWithPlugin testStaticLibPluginExtra testStaticLibPlugin + testSharedLibWithHelper testSharedLibHelperObj + testExeWithPluginHelper testExePluginHelperObj testMod1 testMod2 testLibPerConfigDest NAMESPACE bld_ diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index d6cc8d0..272c7a9 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -68,16 +68,23 @@ target_link_libraries(imp_testExe1 exp_testLib7 exp_testLibCycleA exp_testLibPerConfigDest + exp_testStaticLibWithPlugin ) add_library(imp_testInterfaceInclude1 STATIC imp_testInterfaceInclude1.c) target_include_directories(imp_testInterfaceInclude1 SYSTEM PRIVATE testInterfaceIncludeSystem) target_link_libraries(imp_testInterfaceInclude1 PRIVATE exp_testInterfaceIncludeUser) +add_executable(imp_UseSharedLibWithHelper1 ../../../InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c) +target_link_libraries(imp_UseSharedLibWithHelper1 PRIVATE exp_testSharedLibWithHelper testSharedLibHelperExclude) + # Try building a plugin to an executable imported from the install tree. add_library(imp_mod1 MODULE imp_mod1.c) target_link_libraries(imp_mod1 exp_testExe2) +add_library(imp_ExePlugin1 MODULE ../../../InterfaceLinkLibrariesDirect/ExePlugin.c) +target_link_libraries(imp_ExePlugin1 PRIVATE exp_testExeWithPluginHelper testExePluginHelperExclude) + # Try referencing an executable imported from the build tree. add_custom_command( OUTPUT ${Import_BINARY_DIR}/bld_generated.c @@ -112,6 +119,7 @@ target_link_libraries(imp_testExe1b bld_testLib7 bld_testLibCycleA bld_testLibPerConfigDest + bld_testStaticLibWithPlugin ) add_library(imp_testInterfaceInclude1b STATIC imp_testInterfaceInclude1.c) @@ -183,10 +191,16 @@ target_link_libraries(SubDirLink_bld PRIVATE bld_TopDirLib bld_SubDirLinkA) add_executable(SubDirLink_exp SubDirLink.c) target_link_libraries(SubDirLink_exp PRIVATE exp_TopDirLib exp_SubDirLinkA) +add_executable(imp_UseSharedLibWithHelper1b ../../../InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c) +target_link_libraries(imp_UseSharedLibWithHelper1b PRIVATE bld_testSharedLibWithHelper testSharedLibHelperExclude) + # Try building a plugin to an executable imported from the build tree. add_library(imp_mod1b MODULE imp_mod1.c) target_link_libraries(imp_mod1b bld_testExe2) +add_library(imp_ExePlugin1b MODULE ../../../InterfaceLinkLibrariesDirect/ExePlugin.c) +target_link_libraries(imp_ExePlugin1b PRIVATE bld_testExeWithPluginHelper testExePluginHelperExclude) + # Export/CMakeLists.txt pretends the RelWithDebInfo (as well as Debug) # configuration should link to debug libs. foreach(c DEBUG RELWITHDEBINFO) diff --git a/Tests/ExportImport/Import/A/imp_testExe1.c b/Tests/ExportImport/Import/A/imp_testExe1.c index 8173557..7490a80 100644 --- a/Tests/ExportImport/Import/A/imp_testExe1.c +++ b/Tests/ExportImport/Import/A/imp_testExe1.c @@ -10,6 +10,7 @@ extern int testLib6(void); extern int testLib7(void); extern int testLibCycleA1(void); extern int testLibPerConfigDest(void); +extern int testStaticLibPlugin(void); /* Switch a symbol between debug and optimized builds to make sure the proper library is found from the testLib4 link interface. */ @@ -24,6 +25,7 @@ int main() { return (testLib2() + generated_by_testExe1() + testLib3() + testLib4() + testLib5() + testLib6() + testLib7() + testLibCycleA1() + - testLibPerConfigDest() + generated_by_testExe3() + - generated_by_testExe4() + testLib4lib() + testLib4libcfg()); + testLibPerConfigDest() + testStaticLibPlugin() + + generated_by_testExe3() + generated_by_testExe4() + testLib4lib() + + testLib4libcfg()); } diff --git a/Tests/InterfaceLinkLibrariesDirect/CMakeLists.txt b/Tests/InterfaceLinkLibrariesDirect/CMakeLists.txt new file mode 100644 index 0000000..b06a2fb --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/CMakeLists.txt @@ -0,0 +1,156 @@ +cmake_minimum_required(VERSION 3.21) +project(InterfaceLinkLibrariesDirect C) + +include(testStaticLibPlugin.cmake) +add_executable(InterfaceLinkLibrariesDirect main.c) +target_link_libraries(InterfaceLinkLibrariesDirect PRIVATE testStaticLibWithPlugin) + +include(testSharedLibWithHelper.cmake) +add_executable(UseSharedLibWithHelper UseSharedLibWithHelper.c) +target_link_libraries(UseSharedLibWithHelper PRIVATE testSharedLibWithHelper testSharedLibHelperExclude) + +include(testExeWithPluginHelper.cmake) +add_library(ExePlugin MODULE ExePlugin.c) +target_link_libraries(ExePlugin PRIVATE testExeWithPluginHelper testExePluginHelperExclude) + +#---------------------------------------------------------------------------- + +# Offer usage requirements and symbols to be used through static libs below. +add_library(A STATIC + a_always.c + + # Good symbols that direct_from_A libraries poison if incorrectly used. + a_not_direct_from_A.c + a_not_direct_from_A_for_exe.c + a_not_direct_from_A_optional.c + + # Bad symbols in direct_from_A libraries below to ensure they come first. + a_poison_direct_from_A.c + a_poison_direct_from_A_for_exe.c + a_poison_direct_from_A_optional.c + ) + +# Propagates as usage requirement from A. +add_library(direct_from_A STATIC direct_from_A.c direct_from_A_poison.c) +target_compile_definitions(direct_from_A INTERFACE DEF_DIRECT_FROM_A) +set_property(TARGET A APPEND PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT direct_from_A) + +# Propagates as usage requirement from A, but only for executables. +add_library(direct_from_A_for_exe STATIC direct_from_A_for_exe.c direct_from_A_for_exe_poison.c) +target_compile_definitions(direct_from_A_for_exe INTERFACE DEF_DIRECT_FROM_A_FOR_EXE) +set_property(TARGET A APPEND PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT + "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:direct_from_A_for_exe>") + +# Propagates as usage requirement from A, but only for targets that opt-in. +add_library(direct_from_A_optional STATIC direct_from_A_optional.c direct_from_A_optional_poison.c) +target_compile_definitions(direct_from_A_optional INTERFACE DEF_DIRECT_FROM_A_OPTIONAL) +set_property(TARGET A APPEND PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT + "$<$<BOOL:$<TARGET_PROPERTY:A_LINK_OPTIONAL>>:direct_from_A_optional>") + +# Uses and propagates A's usage requirements. +# Does not use the exe-only or optional usage requirements. +add_library(static_A_public STATIC static_A_public.c) +target_link_libraries(static_A_public PUBLIC A) + +# Uses A's usage requirements, but does not propagate them. +# Does not use the exe-only usage requirement. Does use the optional one. +add_library(static_A_private STATIC static_A_private.c) +target_link_libraries(static_A_private PRIVATE A) +set_property(TARGET static_A_private PROPERTY A_LINK_OPTIONAL 1) + +# Uses A's usage requirements, including an optional one. +add_executable(exe_use_static_A_public exe_use_static_A_public.c) +target_link_libraries(exe_use_static_A_public PRIVATE static_A_public) +set_property(TARGET exe_use_static_A_public PROPERTY A_LINK_OPTIONAL 1) + +# Does not use A's usage requirements. +add_executable(exe_use_static_A_private exe_use_static_A_private.c) +target_link_libraries(exe_use_static_A_private PRIVATE static_A_private) + +#---------------------------------------------------------------------------- + +# Test how original and injected dependencies get ordered. + +# A bunch of static libraries that need to be linked in alphabetic order. +# Each library has an extra source to poison all symbols meant to be +# provided by earlier libraries. This enforces ordering on platforms +# whose linkers re-visit static libraries. +add_library(order_A STATIC order_A.c) +add_library(order_B STATIC order_B.c order_B_poison.c) +add_library(order_C STATIC order_C.c order_C_poison.c) +add_library(order_D STATIC order_D.c order_D_poison.c) +add_library(order_E STATIC order_E.c order_E_poison.c) +add_library(order_F STATIC order_F.c order_F_poison.c) +add_library(order_G STATIC order_G.c order_G_poison.c) +add_library(order_H STATIC order_H.c order_H_poison.c) +add_library(order_I STATIC order_I.c order_I_poison.c) +add_library(order_J STATIC order_J.c order_J_poison.c) + +# An executable to drive linking. +add_executable(order_main order_main.c) + +# In the following diagram, connection by a slash means the top +# target lists the bottom target in a link interface property: +# +# \ => INTERFACE_LINK_LIBRARIES +# / => INTERFACE_LINK_LIBRARIES_DIRECT +# +# The top of each tree represents an entry in the exe's LINK_LIBRARIES. +# CMake should evaluate this graph to generate the proper link order. +# +# D H +# / \ / \ +# B J F I +# / / / / \ +# A C E G J +set_property(TARGET order_main PROPERTY LINK_LIBRARIES order_D order_H) +set_property(TARGET order_D PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_B) +set_property(TARGET order_D PROPERTY INTERFACE_LINK_LIBRARIES order_J) +set_property(TARGET order_B PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_A) +set_property(TARGET order_J PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_C) +set_property(TARGET order_H PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_F) +set_property(TARGET order_H PROPERTY INTERFACE_LINK_LIBRARIES order_I) +set_property(TARGET order_F PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_E) +set_property(TARGET order_I PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT order_G) +set_property(TARGET order_I PROPERTY INTERFACE_LINK_LIBRARIES order_J) + +#---------------------------------------------------------------------------- + +# Test that the original LINK_LIBRARIES cannot be re-ordered by injection +# from usage requirements. + +# A bunch of static libraries that need to be linked in alphabetic order. +# Each library has an extra source to poison all symbols meant to be +# provided by earlier libraries. This enforces ordering on platforms +# whose linkers re-visit static libraries. +add_library(force_A STATIC order_A.c) +add_library(force_B STATIC order_B.c order_B_poison.c) +add_library(force_C STATIC order_C.c order_C_poison.c) +add_library(force_D STATIC order_D.c order_D_poison.c) +add_library(force_E STATIC order_E.c order_E_poison.c) +add_library(force_F STATIC order_F.c order_F_poison.c) +add_library(force_G STATIC order_G.c order_G_poison.c) +add_library(force_H STATIC order_H.c order_H_poison.c) +add_library(force_I STATIC order_I.c order_I_poison.c) +add_library(force_J STATIC order_J.c order_J_poison.c) + +# An executable to drive linking. +add_executable(force_main order_main.c) + +# The executable explicitly lists all the libraries in the right order. +target_link_libraries(force_main PRIVATE force_A force_B force_C force_D force_E force_F force_G force_H) + +# Add legitimate normal dependencies. +set_property(TARGET force_D PROPERTY INTERFACE_LINK_LIBRARIES force_J) +set_property(TARGET force_H PROPERTY INTERFACE_LINK_LIBRARIES force_I) +set_property(TARGET force_I PROPERTY INTERFACE_LINK_LIBRARIES force_J) + +# Add bogus injected direct dependencies to verify that they do not +# change the original order of LINK_LIBRARIES. +set_property(TARGET force_A PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_B) +set_property(TARGET force_B PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_C) +set_property(TARGET force_C PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_D) +set_property(TARGET force_D PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_E) +set_property(TARGET force_E PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_F) +set_property(TARGET force_F PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_G) +set_property(TARGET force_G PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT force_H) diff --git a/Tests/InterfaceLinkLibrariesDirect/ExePlugin.c b/Tests/InterfaceLinkLibrariesDirect/ExePlugin.c new file mode 100644 index 0000000..40a261c --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/ExePlugin.c @@ -0,0 +1,21 @@ +extern int testExePluginHelperObj(int n); + +#ifdef testExePluginHelperObj_NO_OBJECT +int testExePluginHelperObj(int n) +{ + return n; +} +#endif + +#if defined(_WIN32) +__declspec(dllimport) +#endif + int testExePluginAPI(int n); + +#if defined(_WIN32) +__declspec(dllexport) +#endif + int testExePlugin(int n) +{ + return testExePluginAPI(n) + testExePluginHelperObj(n); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c b/Tests/InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c new file mode 100644 index 0000000..832e31f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c @@ -0,0 +1,18 @@ +extern int testSharedLibHelperObj(int n); + +#ifdef testSharedLibHelperObj_NO_OBJECT +int testSharedLibHelperObj(int n) +{ + return n; +} +#endif + +#if defined(_WIN32) +__declspec(dllimport) +#endif + int testSharedLibWithHelper(int n); + +int main(void) +{ + return testSharedLibWithHelper(0) + testSharedLibHelperObj(0); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_always.c b/Tests/InterfaceLinkLibrariesDirect/a_always.c new file mode 100644 index 0000000..007680d --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_always.c @@ -0,0 +1,3 @@ +void a_always(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A.c b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A.c new file mode 100644 index 0000000..732d36e --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A.c @@ -0,0 +1,3 @@ +void not_direct_from_A(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_for_exe.c b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_for_exe.c new file mode 100644 index 0000000..9aed296 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_for_exe.c @@ -0,0 +1,3 @@ +void not_direct_from_A_for_exe(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_optional.c b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_optional.c new file mode 100644 index 0000000..3572e51 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_not_direct_from_A_optional.c @@ -0,0 +1,3 @@ +void not_direct_from_A_optional(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A.c b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A.c new file mode 100644 index 0000000..6d1963d --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A.c @@ -0,0 +1,5 @@ +extern void poison_direct_from_A(void); +void direct_from_A(void) +{ + poison_direct_from_A(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_for_exe.c b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_for_exe.c new file mode 100644 index 0000000..623fc07 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_for_exe.c @@ -0,0 +1,5 @@ +extern void poison_direct_from_A_for_exe(void); +void direct_from_A_for_exe(void) +{ + poison_direct_from_A_for_exe(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_optional.c b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_optional.c new file mode 100644 index 0000000..0f1328e --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/a_poison_direct_from_A_optional.c @@ -0,0 +1,5 @@ +extern void poison_direct_from_A_optional(void); +void direct_from_A_optional(void) +{ + poison_direct_from_A_optional(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A.c new file mode 100644 index 0000000..d6d0df3 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A.c @@ -0,0 +1,3 @@ +void direct_from_A(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe.c new file mode 100644 index 0000000..dfa6db1 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe.c @@ -0,0 +1,3 @@ +void direct_from_A_for_exe(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe_poison.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe_poison.c new file mode 100644 index 0000000..c0ecb0b --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_for_exe_poison.c @@ -0,0 +1,5 @@ +extern void poison_not_direct_from_A_for_exe(void); +void not_direct_from_A_for_exe(void) +{ + poison_not_direct_from_A_for_exe(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional.c new file mode 100644 index 0000000..affdaeb --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional.c @@ -0,0 +1,3 @@ +void direct_from_A_optional(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional_poison.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional_poison.c new file mode 100644 index 0000000..c7c3528 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_optional_poison.c @@ -0,0 +1,5 @@ +extern void poison_not_direct_from_A_optional(void); +void not_direct_from_A_optional(void) +{ + poison_not_direct_from_A_optional(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/direct_from_A_poison.c b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_poison.c new file mode 100644 index 0000000..b03cdf7 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/direct_from_A_poison.c @@ -0,0 +1,5 @@ +extern void poison_not_direct_from_A(void); +void not_direct_from_A(void) +{ + poison_not_direct_from_A(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_private.c b/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_private.c new file mode 100644 index 0000000..024e96e --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_private.c @@ -0,0 +1,23 @@ +#ifdef DEF_DIRECT_FROM_A +# error "DEF_DIRECT_FROM_A incorrectly defined" +#endif +#ifdef DEF_DIRECT_FROM_A_FOR_EXE +# error "DEF_DIRECT_FROM_A_FOR_EXE incorrectly defined" +#endif +#ifdef DEF_DIRECT_FROM_A_OPTIONAL +# error "DEF_DIRECT_FROM_A_OPTIONAL incorrectly defined" +#endif + +extern void static_A_private(void); +extern void not_direct_from_A(void); +extern void not_direct_from_A_for_exe(void); +extern void not_direct_from_A_optional(void); + +int main(void) +{ + static_A_private(); + not_direct_from_A(); + not_direct_from_A_for_exe(); + not_direct_from_A_optional(); + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_public.c b/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_public.c new file mode 100644 index 0000000..b3b0a56 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/exe_use_static_A_public.c @@ -0,0 +1,23 @@ +#ifndef DEF_DIRECT_FROM_A +# error "DEF_DIRECT_FROM_A incorrectly not defined" +#endif +#ifndef DEF_DIRECT_FROM_A_FOR_EXE +# error "DEF_DIRECT_FROM_A_FOR_EXE incorrectly not defined" +#endif +#ifndef DEF_DIRECT_FROM_A_OPTIONAL +# error "DEF_DIRECT_FROM_A_OPTIONAL incorrectly not defined" +#endif + +extern void static_A_public(void); +extern void direct_from_A(void); +extern void direct_from_A_for_exe(void); +extern void direct_from_A_optional(void); + +int main(void) +{ + static_A_public(); + direct_from_A(); + direct_from_A_for_exe(); + direct_from_A_optional(); + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/main.c b/Tests/InterfaceLinkLibrariesDirect/main.c new file mode 100644 index 0000000..53f0e6d --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/main.c @@ -0,0 +1,5 @@ +extern int testStaticLibPlugin(void); +int main(void) +{ + return testStaticLibPlugin(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_A.c b/Tests/InterfaceLinkLibrariesDirect/order_A.c new file mode 100644 index 0000000..2cd4d60 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_A.c @@ -0,0 +1,5 @@ +extern void order_B(void); +void order_A(void) +{ + order_B(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_B.c b/Tests/InterfaceLinkLibrariesDirect/order_B.c new file mode 100644 index 0000000..1787f1d --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_B.c @@ -0,0 +1,5 @@ +extern void order_C(void); +void order_B(void) +{ + order_C(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_B_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_B_poison.c new file mode 100644 index 0000000..bcb7b4b --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_B_poison.c @@ -0,0 +1,6 @@ +extern void order_B_poison(void); + +void order_A(void) +{ + order_B_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_C.c b/Tests/InterfaceLinkLibrariesDirect/order_C.c new file mode 100644 index 0000000..e67e719 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_C.c @@ -0,0 +1,5 @@ +extern void order_D(void); +void order_C(void) +{ + order_D(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_C_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_C_poison.c new file mode 100644 index 0000000..fc31104 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_C_poison.c @@ -0,0 +1,11 @@ +extern void order_C_poison(void); + +void order_A(void) +{ + order_C_poison(); +} + +void order_B(void) +{ + order_C_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_D.c b/Tests/InterfaceLinkLibrariesDirect/order_D.c new file mode 100644 index 0000000..f5bb2d6 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_D.c @@ -0,0 +1,5 @@ +extern void order_E(void); +void order_D(void) +{ + order_E(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_D_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_D_poison.c new file mode 100644 index 0000000..d2d64e6 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_D_poison.c @@ -0,0 +1,16 @@ +extern void order_D_poison(void); + +void order_A(void) +{ + order_D_poison(); +} + +void order_B(void) +{ + order_D_poison(); +} + +void order_C(void) +{ + order_D_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_E.c b/Tests/InterfaceLinkLibrariesDirect/order_E.c new file mode 100644 index 0000000..2a56443 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_E.c @@ -0,0 +1,5 @@ +extern void order_F(void); +void order_E(void) +{ + order_F(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_E_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_E_poison.c new file mode 100644 index 0000000..7d8b53e --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_E_poison.c @@ -0,0 +1,21 @@ +extern void order_E_poison(void); + +void order_A(void) +{ + order_E_poison(); +} + +void order_B(void) +{ + order_E_poison(); +} + +void order_C(void) +{ + order_E_poison(); +} + +void order_D(void) +{ + order_E_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_F.c b/Tests/InterfaceLinkLibrariesDirect/order_F.c new file mode 100644 index 0000000..d242284 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_F.c @@ -0,0 +1,5 @@ +extern void order_G(void); +void order_F(void) +{ + order_G(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_F_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_F_poison.c new file mode 100644 index 0000000..285f247 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_F_poison.c @@ -0,0 +1,26 @@ +extern void order_F_poison(void); + +void order_A(void) +{ + order_F_poison(); +} + +void order_B(void) +{ + order_F_poison(); +} + +void order_C(void) +{ + order_F_poison(); +} + +void order_D(void) +{ + order_F_poison(); +} + +void order_E(void) +{ + order_F_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_G.c b/Tests/InterfaceLinkLibrariesDirect/order_G.c new file mode 100644 index 0000000..ff71038 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_G.c @@ -0,0 +1,5 @@ +extern void order_H(void); +void order_G(void) +{ + order_H(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_G_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_G_poison.c new file mode 100644 index 0000000..3a1fe1d --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_G_poison.c @@ -0,0 +1,31 @@ +extern void order_G_poison(void); + +void order_A(void) +{ + order_G_poison(); +} + +void order_B(void) +{ + order_G_poison(); +} + +void order_C(void) +{ + order_G_poison(); +} + +void order_D(void) +{ + order_G_poison(); +} + +void order_E(void) +{ + order_G_poison(); +} + +void order_F(void) +{ + order_G_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_H.c b/Tests/InterfaceLinkLibrariesDirect/order_H.c new file mode 100644 index 0000000..9c62bb1 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_H.c @@ -0,0 +1,5 @@ +extern void order_I(void); +void order_H(void) +{ + order_I(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_H_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_H_poison.c new file mode 100644 index 0000000..0c6b84f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_H_poison.c @@ -0,0 +1,36 @@ +extern void order_H_poison(void); + +void order_A(void) +{ + order_H_poison(); +} + +void order_B(void) +{ + order_H_poison(); +} + +void order_C(void) +{ + order_H_poison(); +} + +void order_D(void) +{ + order_H_poison(); +} + +void order_E(void) +{ + order_H_poison(); +} + +void order_F(void) +{ + order_H_poison(); +} + +void order_G(void) +{ + order_H_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_I.c b/Tests/InterfaceLinkLibrariesDirect/order_I.c new file mode 100644 index 0000000..96152de --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_I.c @@ -0,0 +1,5 @@ +extern void order_J(void); +void order_I(void) +{ + order_J(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_I_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_I_poison.c new file mode 100644 index 0000000..3fabe1f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_I_poison.c @@ -0,0 +1,41 @@ +extern void order_I_poison(void); + +void order_A(void) +{ + order_I_poison(); +} + +void order_B(void) +{ + order_I_poison(); +} + +void order_C(void) +{ + order_I_poison(); +} + +void order_D(void) +{ + order_I_poison(); +} + +void order_E(void) +{ + order_I_poison(); +} + +void order_F(void) +{ + order_I_poison(); +} + +void order_G(void) +{ + order_I_poison(); +} + +void order_H(void) +{ + order_I_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_J.c b/Tests/InterfaceLinkLibrariesDirect/order_J.c new file mode 100644 index 0000000..49eec47 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_J.c @@ -0,0 +1,3 @@ +void order_J(void) +{ +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_J_poison.c b/Tests/InterfaceLinkLibrariesDirect/order_J_poison.c new file mode 100644 index 0000000..9724fd5 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_J_poison.c @@ -0,0 +1,46 @@ +extern void order_J_poison(void); + +void order_A(void) +{ + order_J_poison(); +} + +void order_B(void) +{ + order_J_poison(); +} + +void order_C(void) +{ + order_J_poison(); +} + +void order_D(void) +{ + order_J_poison(); +} + +void order_E(void) +{ + order_J_poison(); +} + +void order_F(void) +{ + order_J_poison(); +} + +void order_G(void) +{ + order_J_poison(); +} + +void order_H(void) +{ + order_J_poison(); +} + +void order_I(void) +{ + order_J_poison(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/order_main.c b/Tests/InterfaceLinkLibrariesDirect/order_main.c new file mode 100644 index 0000000..eed2453 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/order_main.c @@ -0,0 +1,6 @@ +extern void order_A(void); +int main(void) +{ + order_A(); + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/static_A_private.c b/Tests/InterfaceLinkLibrariesDirect/static_A_private.c new file mode 100644 index 0000000..d98a22c --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/static_A_private.c @@ -0,0 +1,16 @@ +#ifndef DEF_DIRECT_FROM_A +# error "DEF_DIRECT_FROM_A incorrectly not defined" +#endif +#ifdef DEF_DIRECT_FROM_A_FOR_EXE +# error "DEF_DIRECT_FROM_A_FOR_EXE incorrectly defined" +#endif +#ifndef DEF_DIRECT_FROM_A_OPTIONAL +# error "DEF_DIRECT_FROM_A_OPTIONAL incorrectly not defined" +#endif + +extern void a_always(void); + +void static_A_private(void) +{ + a_always(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/static_A_public.c b/Tests/InterfaceLinkLibrariesDirect/static_A_public.c new file mode 100644 index 0000000..ed88ca6 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/static_A_public.c @@ -0,0 +1,16 @@ +#ifndef DEF_DIRECT_FROM_A +# error "DEF_DIRECT_FROM_A incorrectly not defined" +#endif +#ifdef DEF_DIRECT_FROM_A_FOR_EXE +# error "DEF_DIRECT_FROM_A_FOR_EXE incorrectly defined" +#endif +#ifdef DEF_DIRECT_FROM_A_OPTIONAL +# error "DEF_DIRECT_FROM_A_OPTIONAL incorrectly defined" +#endif + +extern void a_always(void); + +void static_A_public(void) +{ + a_always(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testExePluginHelperObj.c b/Tests/InterfaceLinkLibrariesDirect/testExePluginHelperObj.c new file mode 100644 index 0000000..49c495f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testExePluginHelperObj.c @@ -0,0 +1,4 @@ +int testExePluginHelperObj(int n) +{ + return n; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.c b/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.c new file mode 100644 index 0000000..f8787db --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + int testExePluginAPI(int n) +{ + return n; +} + +int main(void) +{ + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.cmake b/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.cmake new file mode 100644 index 0000000..97c5b65 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testExeWithPluginHelper.cmake @@ -0,0 +1,7 @@ +# Logic common to InterfaceLinkLibrariesDirect and ExportImport tests. +set(src ${CMAKE_CURRENT_LIST_DIR}) +add_executable(testExeWithPluginHelper ${src}/testExeWithPluginHelper.c) +add_library(testExePluginHelperObj OBJECT ${src}/testExePluginHelperObj.c) +set_property(TARGET testExeWithPluginHelper PROPERTY ENABLE_EXPORTS 1) +set_property(TARGET testExeWithPluginHelper PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT $<TARGET_NAME:testExePluginHelperObj>) +set_property(TARGET testExeWithPluginHelper PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE $<1:testExePluginHelperExclude>) diff --git a/Tests/InterfaceLinkLibrariesDirect/testSharedLibHelperObj.c b/Tests/InterfaceLinkLibrariesDirect/testSharedLibHelperObj.c new file mode 100644 index 0000000..9d55fcb --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testSharedLibHelperObj.c @@ -0,0 +1,4 @@ +int testSharedLibHelperObj(int n) +{ + return n; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.c b/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.c new file mode 100644 index 0000000..f942b54 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.c @@ -0,0 +1,7 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + int testSharedLibWithHelper(int n) +{ + return n; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.cmake b/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.cmake new file mode 100644 index 0000000..c51751c --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testSharedLibWithHelper.cmake @@ -0,0 +1,6 @@ +# Logic common to InterfaceLinkLibrariesDirect and ExportImport tests. +set(src ${CMAKE_CURRENT_LIST_DIR}) +add_library(testSharedLibWithHelper SHARED ${src}/testSharedLibWithHelper.c) +add_library(testSharedLibHelperObj OBJECT ${src}/testSharedLibHelperObj.c) +set_property(TARGET testSharedLibWithHelper PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT $<TARGET_NAME:testSharedLibHelperObj>) +set_property(TARGET testSharedLibWithHelper PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE $<1:testSharedLibHelperExclude>) diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.c new file mode 100644 index 0000000..17f643f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.c @@ -0,0 +1,6 @@ +extern int testStaticLibWithPlugin1(void); +extern int testStaticLibPluginExtra(void); +int testStaticLibPlugin(void) +{ + return testStaticLibWithPlugin1() + testStaticLibPluginExtra(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.cmake b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.cmake new file mode 100644 index 0000000..907872f --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPlugin.cmake @@ -0,0 +1,14 @@ +# Logic common to InterfaceLinkLibrariesDirect and ExportImport tests. +set(src ${CMAKE_CURRENT_LIST_DIR}) +add_library(testStaticLibWithPlugin STATIC + ${src}/testStaticLibWithPlugin1.c # used by testStaticLibPlugin + ${src}/testStaticLibWithPlugin2.c # used by testStaticLibPluginExtra + ${src}/testStaticLibWithPluginBad1.c # link error if not after testStaticLibPlugin + ${src}/testStaticLibWithPluginBad2.c # link error if not after testStaticLibPluginExtra + ) +add_library(testStaticLibPluginExtra STATIC ${src}/testStaticLibPluginExtra.c) +add_library(testStaticLibPlugin STATIC ${src}/testStaticLibPlugin.c) +target_link_libraries(testStaticLibPlugin PUBLIC testStaticLibWithPlugin testStaticLibPluginExtra) +target_link_libraries(testStaticLibPluginExtra PUBLIC testStaticLibWithPlugin) +set_property(TARGET testStaticLibWithPlugin PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT testStaticLibPlugin) +set_property(TARGET testStaticLibWithPlugin PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE testStaticLibWithPlugin) diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibPluginExtra.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPluginExtra.c new file mode 100644 index 0000000..11fe0f8 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibPluginExtra.c @@ -0,0 +1,5 @@ +extern int testStaticLibWithPlugin2(void); +int testStaticLibPluginExtra(void) +{ + return testStaticLibWithPlugin2(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin1.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin1.c new file mode 100644 index 0000000..5e75dce --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin1.c @@ -0,0 +1,4 @@ +int testStaticLibWithPlugin1(void) +{ + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin2.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin2.c new file mode 100644 index 0000000..74ac1ae --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPlugin2.c @@ -0,0 +1,4 @@ +int testStaticLibWithPlugin2(void) +{ + return 0; +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad1.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad1.c new file mode 100644 index 0000000..b41abc9 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad1.c @@ -0,0 +1,6 @@ +/* Produce an error if if the object compiled from this source is used. */ +extern int testStaticLibWithPlugin_linked_before_testStaticLibPlugin(void); +int testStaticLibPlugin(void) +{ + return testStaticLibWithPlugin_linked_before_testStaticLibPlugin(); +} diff --git a/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad2.c b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad2.c new file mode 100644 index 0000000..43337a5 --- /dev/null +++ b/Tests/InterfaceLinkLibrariesDirect/testStaticLibWithPluginBad2.c @@ -0,0 +1,7 @@ +/* Produce an error if if the object compiled from this source is used. */ +extern int testStaticLibWithPlugin_linked_before_testStaticLibPluginExtra( + void); +int testStaticLibPluginExtra(void) +{ + return testStaticLibWithPlugin_linked_before_testStaticLibPluginExtra(); +} diff --git a/Tests/ObjectLibrary/Transitive/BarMain.c b/Tests/ObjectLibrary/Transitive/BarMain.c new file mode 100644 index 0000000..aec3e48 --- /dev/null +++ b/Tests/ObjectLibrary/Transitive/BarMain.c @@ -0,0 +1,6 @@ +extern int BarObject1(void); + +int main(void) +{ + return BarObject1(); +} diff --git a/Tests/ObjectLibrary/Transitive/BarObject1.c b/Tests/ObjectLibrary/Transitive/BarObject1.c new file mode 100644 index 0000000..2f68386 --- /dev/null +++ b/Tests/ObjectLibrary/Transitive/BarObject1.c @@ -0,0 +1,5 @@ +extern int BarObject2(void); +int BarObject1(void) +{ + return BarObject2(); +} diff --git a/Tests/ObjectLibrary/Transitive/BarObject2.c b/Tests/ObjectLibrary/Transitive/BarObject2.c new file mode 100644 index 0000000..881c64a --- /dev/null +++ b/Tests/ObjectLibrary/Transitive/BarObject2.c @@ -0,0 +1,5 @@ +extern int BarObject3(void); +int BarObject2(void) +{ + return BarObject3(); +} diff --git a/Tests/ObjectLibrary/Transitive/BarObject3.c b/Tests/ObjectLibrary/Transitive/BarObject3.c new file mode 100644 index 0000000..e557dbc --- /dev/null +++ b/Tests/ObjectLibrary/Transitive/BarObject3.c @@ -0,0 +1,4 @@ +int BarObject3(void) +{ + return 0; +} diff --git a/Tests/ObjectLibrary/Transitive/CMakeLists.txt b/Tests/ObjectLibrary/Transitive/CMakeLists.txt index d616cda..17247eb 100644 --- a/Tests/ObjectLibrary/Transitive/CMakeLists.txt +++ b/Tests/ObjectLibrary/Transitive/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_policy(SET CMP0022 NEW) add_library(FooStatic STATIC FooStatic.c) add_library(FooObject1 OBJECT FooObject.c) @@ -10,3 +9,17 @@ add_library(FooObject2 OBJECT FooObject.c) target_link_libraries(FooObject2 INTERFACE FooStatic) add_executable(Transitive2 Transitive.c) target_link_libraries(Transitive2 PRIVATE FooObject2) + +add_library(FooObjectDirect OBJECT FooObject.c) +add_library(FooStaticDirect STATIC FooStatic.c) +set_property(TARGET FooStaticDirect PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT FooObjectDirect) +add_executable(TransitiveDirect Transitive.c) +target_link_libraries(TransitiveDirect PRIVATE FooStaticDirect) + +add_library(BarObject1 OBJECT BarObject1.c) +add_library(BarObject2 OBJECT BarObject2.c) +add_library(BarObject3 OBJECT BarObject3.c) +set_property(TARGET BarObject1 PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT BarObject2) +set_property(TARGET BarObject2 PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT BarObject3) +add_executable(BarMain BarMain.c) +target_link_libraries(BarMain PRIVATE BarObject1) |