mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 02:39:48 -06:00
De-duplicate dependencies propagated through interface libraries
When processing an indirect dependency of target `A` on target `C` via an interface library B, `cmComputeTargetDepends::AddTargetDepend` was not checking for duplicate dependencies, so that if `A` depended on `C` via multiple interface libraries, the dependency graph built in cmComputeTargetDepends would have duplicate edges. In a real project (LLVM libc) this was causing cmake to consume multiple gigabytes of RAM. Fixes: #27386
This commit is contained in:
@@ -224,7 +224,7 @@ void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index)
|
||||
for (cmLinkItem const& lib : impl->Libraries) {
|
||||
// Don't emit the same library twice for this target.
|
||||
if (emitted.insert(lib).second) {
|
||||
this->AddTargetDepend(depender_index, lib, true, false);
|
||||
this->AddTargetDepend(depender_index, lib, true, false, emitted);
|
||||
this->AddInterfaceDepends(depender_index, lib, it, emitted);
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,8 @@ void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index)
|
||||
for (cmLinkItem const& litem : tutils) {
|
||||
// Don't emit the same utility twice for this target.
|
||||
if (emitted.insert(litem).second) {
|
||||
this->AddTargetDepend(depender_index, litem, false, litem.Cross);
|
||||
this->AddTargetDepend(depender_index, litem, false, litem.Cross,
|
||||
emitted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +278,7 @@ void cmComputeTargetDepends::AddInterfaceDepends(
|
||||
// code in the project that caused this dependency to be added.
|
||||
cmLinkItem libBT = lib;
|
||||
libBT.Backtrace = dependee_backtrace;
|
||||
this->AddTargetDepend(depender_index, libBT, true, false);
|
||||
this->AddTargetDepend(depender_index, libBT, true, false, emitted);
|
||||
this->AddInterfaceDepends(depender_index, libBT, config, emitted);
|
||||
}
|
||||
}
|
||||
@@ -343,7 +344,8 @@ void cmComputeTargetDepends::AddObjectDepends(size_t depender_index,
|
||||
|
||||
void cmComputeTargetDepends::AddTargetDepend(size_t depender_index,
|
||||
cmLinkItem const& dependee_name,
|
||||
bool linking, bool cross)
|
||||
bool linking, bool cross,
|
||||
std::set<cmLinkItem>& emitted)
|
||||
{
|
||||
// Get the depender.
|
||||
cmGeneratorTarget const* depender = this->Targets[depender_index];
|
||||
@@ -370,22 +372,25 @@ void cmComputeTargetDepends::AddTargetDepend(size_t depender_index,
|
||||
|
||||
if (dependee) {
|
||||
this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace,
|
||||
linking, cross);
|
||||
linking, cross, emitted);
|
||||
}
|
||||
}
|
||||
|
||||
void cmComputeTargetDepends::AddTargetDepend(
|
||||
size_t depender_index, cmGeneratorTarget const* dependee,
|
||||
cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross)
|
||||
cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross,
|
||||
std::set<cmLinkItem>& emitted)
|
||||
{
|
||||
if (!dependee->IsInBuildSystem()) {
|
||||
// Skip targets that are not in the buildsystem but follow their
|
||||
// utility dependencies.
|
||||
std::set<cmLinkItem> const& utils = dependee->GetUtilityItems();
|
||||
for (cmLinkItem const& i : utils) {
|
||||
if (cmGeneratorTarget const* transitive_dependee = i.Target) {
|
||||
this->AddTargetDepend(depender_index, transitive_dependee, i.Backtrace,
|
||||
false, i.Cross);
|
||||
if (emitted.insert(i).second) {
|
||||
if (cmGeneratorTarget const* transitive_dependee = i.Target) {
|
||||
this->AddTargetDepend(depender_index, transitive_dependee,
|
||||
i.Backtrace, false, i.Cross, emitted);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -54,11 +54,13 @@ private:
|
||||
void CollectDepends();
|
||||
void CollectTargetDepends(size_t depender_index);
|
||||
void AddTargetDepend(size_t depender_index, cmLinkItem const& dependee_name,
|
||||
bool linking, bool cross);
|
||||
bool linking, bool cross,
|
||||
std::set<cmLinkItem>& emitted);
|
||||
void AddTargetDepend(size_t depender_index,
|
||||
cmGeneratorTarget const* dependee,
|
||||
cmListFileBacktrace const& dependee_backtrace,
|
||||
bool linking, bool cross);
|
||||
bool linking, bool cross,
|
||||
std::set<cmLinkItem>& emitted);
|
||||
void CollectSideEffects();
|
||||
void CollectSideEffectsForTarget(std::set<size_t>& visited,
|
||||
size_t depender_index);
|
||||
|
||||
@@ -10,6 +10,9 @@ run_cmake(add_custom_command-TARGET)
|
||||
run_cmake(IMPORTED_LIBNAME-bad-value)
|
||||
run_cmake(IMPORTED_LIBNAME-non-iface)
|
||||
run_cmake(IMPORTED_LIBNAME-non-imported)
|
||||
if(RunCMake_GENERATOR MATCHES "(Ninja|Make|FASTBuild)")
|
||||
run_cmake(unique_dependencies)
|
||||
endif()
|
||||
|
||||
function(run_WithSources CASE)
|
||||
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1,26 @@
|
||||
The initial target dependency graph is:
|
||||
target 0 is \[lib1\]
|
||||
target 1 is \[lib2\]
|
||||
target 2 is \[top\]
|
||||
depends on target 3 \[util\] \(strong\)
|
||||
target 3 is \[util\]
|
||||
target 4 is \[edit_cache\]
|
||||
target 5 is \[rebuild_cache\]
|
||||
.*
|
||||
The intermediate target dependency graph is:
|
||||
target 0 is \[lib1\]
|
||||
target 1 is \[lib2\]
|
||||
target 2 is \[top\]
|
||||
depends on target 3 \[util\] \(strong\)
|
||||
target 3 is \[util\]
|
||||
target 4 is \[edit_cache\]
|
||||
target 5 is \[rebuild_cache\]
|
||||
.*
|
||||
The final target dependency graph is:
|
||||
target 0 is \[lib1\]
|
||||
target 1 is \[lib2\]
|
||||
target 2 is \[top\]
|
||||
depends on target 3 \[util\] \(strong\)
|
||||
target 3 is \[util\]
|
||||
target 4 is \[edit_cache\]
|
||||
target 5 is \[rebuild_cache\]
|
||||
19
Tests/RunCMake/InterfaceLibrary/unique_dependencies.cmake
Normal file
19
Tests/RunCMake/InterfaceLibrary/unique_dependencies.cmake
Normal file
@@ -0,0 +1,19 @@
|
||||
# Make two interface libraries.
|
||||
add_library(lib1 INTERFACE)
|
||||
add_library(lib2 INTERFACE)
|
||||
|
||||
# Top-level target that depends on both of them.
|
||||
add_custom_target(top
|
||||
COMMAND ${CMAKE_COMMAND} -E echo top
|
||||
DEPENDS lib1 lib2)
|
||||
|
||||
# Lowest-level utility target that both libraries depend on.
|
||||
add_custom_target(util
|
||||
COMMAND ${CMAKE_COMMAND} -E echo util)
|
||||
add_dependencies(lib1 util)
|
||||
add_dependencies(lib2 util)
|
||||
|
||||
# The dependency graph computed by cmComputeTargetDepends will include
|
||||
# an edge directly from 'top' to 'util'. But it should only include
|
||||
# one copy of it, even though there are two paths via lib1 and lib2.
|
||||
set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1)
|
||||
Reference in New Issue
Block a user