mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-05 13:20:47 -06:00
INTERFACE_LINK_LIBRARIES_DIRECT: Honor link dependencies through LINK_ONLY
In commit f3ad061858 (Add usage requirements to update direct link
dependencies, 2022-01-12, v3.23.0-rc1~44^2), we evaluated the transitive
closure of `INTERFACE_LINK_LIBRARIES` as a non-linking usage requirement.
That left out `INTERFACE_LINK_LIBRARIES_DIRECT` link dependencies that
appear behind private dependencies of a static library, guarded by the
`$<LINK_ONLY:...>` generator expression. At the time, that decision was
intentional, in order to prevent arbitrary usage requirements from
leaking out of `PRIVATE` dependencies.
Since then, we've revised evaluation of `LINK_LIBRARIES` to distinguish
between collecting link dependencies and other usage requirements. Use
that information when following `INTERFACE_LINK_LIBRARIES` to collect
the matching kind of requirements from `INTERFACE_LINK_LIBRARIES_DIRECT`.
Fixes: #22496
This commit is contained in:
@@ -56,11 +56,27 @@ on ``X``'s dependents:
|
||||
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.
|
||||
so ``Y``'s usage requirements, including ``INTERFACE_PROPERTY_LINK_DIRECT``,
|
||||
``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, and the usage requirements
|
||||
declared by the direct link dependencies they add, are propagated to
|
||||
``X``'s dependents.
|
||||
|
||||
* If ``X`` links ``Y`` privately:
|
||||
* If ``X`` is a static library or object library, and links ``Y`` privately:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
target_link_libraries(X PRIVATE Y)
|
||||
|
||||
then ``$<LINK_ONLY:Y>`` is placed in ``X``'s
|
||||
:prop_tgt:`INTERFACE_LINK_LIBRARIES`. ``Y``'s linking requirements,
|
||||
including ``INTERFACE_PROPERTY_LINK_DIRECT``,
|
||||
``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, and the transitive link
|
||||
dependencies declared by the direct link dependencies they add, are
|
||||
propagated to ``X``'s dependents. However, ``Y``'s non-linking
|
||||
usage requirements are blocked by the :genex:`LINK_ONLY` generator
|
||||
expression, and are not propagated to ``X``'s dependents.
|
||||
|
||||
* If ``X`` is a shared library or executable, and links ``Y`` privately:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
@@ -68,13 +84,10 @@ on ``X``'s dependents:
|
||||
|
||||
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.)
|
||||
and ``INTERFACE_PROPERTY_LINK_DIRECT_EXCLUDE``, are not propagated to
|
||||
``X``'s dependents.
|
||||
|
||||
* In either case, the content of ``X``'s :prop_tgt:`INTERFACE_LINK_LIBRARIES`
|
||||
* In all cases, 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``.
|
||||
|
||||
@@ -184,7 +197,7 @@ be an intermediate library:
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(app_impl STATIC app_impl.cpp)
|
||||
target_link_libraries(app_impl PUBLIC Foo)
|
||||
target_link_libraries(app_impl PRIVATE Foo)
|
||||
|
||||
add_executable(app main.cpp)
|
||||
target_link_libraries(app PRIVATE app_impl)
|
||||
|
||||
@@ -8035,6 +8035,7 @@ class TransitiveLinkImpl
|
||||
{
|
||||
cmGeneratorTarget const* Self;
|
||||
std::string const& Config;
|
||||
LinkInterfaceFor ImplFor;
|
||||
cmLinkImplementation& Impl;
|
||||
|
||||
std::set<cmLinkItem> Emitted;
|
||||
@@ -8045,9 +8046,10 @@ class TransitiveLinkImpl
|
||||
|
||||
public:
|
||||
TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config,
|
||||
cmLinkImplementation& impl)
|
||||
LinkInterfaceFor implFor, cmLinkImplementation& impl)
|
||||
: Self(self)
|
||||
, Config(config)
|
||||
, ImplFor(implFor)
|
||||
, Impl(impl)
|
||||
{
|
||||
}
|
||||
@@ -8064,8 +8066,8 @@ void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target)
|
||||
}
|
||||
|
||||
// Get this target's usage requirements.
|
||||
cmLinkInterfaceLibraries const* iface = target->GetLinkInterfaceLibraries(
|
||||
this->Config, this->Self, LinkInterfaceFor::Usage);
|
||||
cmLinkInterfaceLibraries const* iface =
|
||||
target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor);
|
||||
if (!iface) {
|
||||
return;
|
||||
}
|
||||
@@ -8129,9 +8131,10 @@ void TransitiveLinkImpl::Compute()
|
||||
|
||||
void ComputeLinkImplTransitive(cmGeneratorTarget const* self,
|
||||
std::string const& config,
|
||||
LinkInterfaceFor implFor,
|
||||
cmLinkImplementation& impl)
|
||||
{
|
||||
TransitiveLinkImpl transitiveLinkImpl(self, config, impl);
|
||||
TransitiveLinkImpl transitiveLinkImpl(self, config, implFor, impl);
|
||||
transitiveLinkImpl.Compute();
|
||||
}
|
||||
}
|
||||
@@ -8140,7 +8143,6 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
|
||||
const std::string& config, cmOptionalLinkImplementation& impl,
|
||||
cmGeneratorTarget const* head, LinkInterfaceFor implFor) const
|
||||
{
|
||||
static_cast<void>(implFor);
|
||||
cmLocalGenerator const* lg = this->LocalGenerator;
|
||||
cmMakefile const* mf = lg->GetMakefile();
|
||||
cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries();
|
||||
@@ -8245,7 +8247,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
|
||||
|
||||
// Update the list of direct link dependencies from usage requirements.
|
||||
if (head == this) {
|
||||
ComputeLinkImplTransitive(this, config, impl);
|
||||
ComputeLinkImplTransitive(this, config, implFor, impl);
|
||||
}
|
||||
|
||||
// Get the list of configurations considered to be DEBUG.
|
||||
|
||||
@@ -52,7 +52,7 @@ set_property(TARGET A APPEND PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT
|
||||
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.
|
||||
# Uses A's usage requirements, but propagates only the linking requirements.
|
||||
# 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)
|
||||
@@ -63,10 +63,15 @@ 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.
|
||||
# Does not use A's usage requirements, but does use its non-optional linking 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)
|
||||
|
||||
# Uses A's usage requirements, including an optional one, but overrides the link ordering.
|
||||
add_executable(exe_use_static_A_public_explicit exe_use_static_A_public_explicit.c)
|
||||
target_link_libraries(exe_use_static_A_public_explicit PRIVATE static_A_public A direct_from_A direct_from_A_for_exe direct_from_A_optional)
|
||||
set_property(TARGET exe_use_static_A_public_explicit PROPERTY A_LINK_OPTIONAL 1)
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
# Test how original and injected dependencies get ordered.
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
#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 direct_from_A(void);
|
||||
extern void 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();
|
||||
direct_from_A();
|
||||
direct_from_A_for_exe();
|
||||
not_direct_from_A_optional();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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 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_public();
|
||||
not_direct_from_A();
|
||||
not_direct_from_A_for_exe();
|
||||
not_direct_from_A_optional();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user