/src/CMake/Source/cmComputeTargetDepends.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmComputeTargetDepends.h" |
4 | | |
5 | | #include <cassert> |
6 | | #include <cstddef> |
7 | | #include <cstdio> |
8 | | #include <memory> |
9 | | #include <sstream> |
10 | | #include <utility> |
11 | | |
12 | | #include "cmComputeComponentGraph.h" |
13 | | #include "cmGeneratorTarget.h" |
14 | | #include "cmGlobalGenerator.h" |
15 | | #include "cmLinkItem.h" |
16 | | #include "cmListFileCache.h" |
17 | | #include "cmLocalGenerator.h" |
18 | | #include "cmMakefile.h" |
19 | | #include "cmMessageType.h" |
20 | | #include "cmRange.h" |
21 | | #include "cmSourceFile.h" |
22 | | #include "cmSourceFileLocationKind.h" |
23 | | #include "cmState.h" |
24 | | #include "cmStateTypes.h" |
25 | | #include "cmStringAlgorithms.h" |
26 | | #include "cmSystemTools.h" |
27 | | #include "cmTarget.h" |
28 | | #include "cmTargetDepend.h" |
29 | | #include "cmValue.h" |
30 | | #include "cmake.h" |
31 | | |
32 | | /* |
33 | | |
34 | | This class is meant to analyze inter-target dependencies globally |
35 | | during the generation step. The goal is to produce a set of direct |
36 | | dependencies for each target such that no cycles are left and the |
37 | | build order is safe. |
38 | | |
39 | | For most target types cyclic dependencies are not allowed. However |
40 | | STATIC libraries may depend on each other in a cyclic fashion. In |
41 | | general the directed dependency graph forms a directed-acyclic-graph |
42 | | of strongly connected components. All strongly connected components |
43 | | should consist of only STATIC_LIBRARY targets. |
44 | | |
45 | | In order to safely break dependency cycles we must preserve all other |
46 | | dependencies passing through the corresponding strongly connected component. |
47 | | The approach taken by this class is as follows: |
48 | | |
49 | | - Collect all targets and form the original dependency graph |
50 | | - Run Tarjan's algorithm to extract the strongly connected components |
51 | | (error if any member of a non-trivial component is not STATIC) |
52 | | - The original dependencies imply a DAG on the components. |
53 | | Use the implied DAG to construct a final safe set of dependencies. |
54 | | |
55 | | The final dependency set is constructed as follows: |
56 | | |
57 | | - For each connected component targets are placed in an arbitrary |
58 | | order. Each target depends on the target following it in the order. |
59 | | The first target is designated the head and the last target the tail. |
60 | | (most components will be just 1 target anyway) |
61 | | |
62 | | - Original dependencies between targets in different components are |
63 | | converted to connect the depender's component tail to the |
64 | | dependee's component head. |
65 | | |
66 | | In most cases this will reproduce the original dependencies. However |
67 | | when there are cycles of static libraries they will be broken in a |
68 | | safe manner. |
69 | | |
70 | | For example, consider targets A0, A1, A2, B0, B1, B2, and C with these |
71 | | dependencies: |
72 | | |
73 | | A0 -> A1 -> A2 -> A0 , B0 -> B1 -> B2 -> B0 -> A0 , C -> B0 |
74 | | |
75 | | Components may be identified as |
76 | | |
77 | | Component 0: A0, A1, A2 |
78 | | Component 1: B0, B1, B2 |
79 | | Component 2: C |
80 | | |
81 | | Intra-component dependencies are: |
82 | | |
83 | | 0: A0 -> A1 -> A2 , head=A0, tail=A2 |
84 | | 1: B0 -> B1 -> B2 , head=B0, tail=B2 |
85 | | 2: head=C, tail=C |
86 | | |
87 | | The inter-component dependencies are converted as: |
88 | | |
89 | | B0 -> A0 is component 1->0 and becomes B2 -> A0 |
90 | | C -> B0 is component 2->1 and becomes C -> B0 |
91 | | |
92 | | This leads to the final target dependencies: |
93 | | |
94 | | C -> B0 -> B1 -> B2 -> A0 -> A1 -> A2 |
95 | | |
96 | | These produce a safe build order since C depends directly or |
97 | | transitively on all the static libraries it links. |
98 | | |
99 | | */ |
100 | | |
101 | | cmComputeTargetDepends::cmComputeTargetDepends(cmGlobalGenerator* gg) |
102 | 0 | { |
103 | 0 | this->GlobalGenerator = gg; |
104 | 0 | cmake* cm = this->GlobalGenerator->GetCMakeInstance(); |
105 | 0 | this->DebugMode = |
106 | 0 | cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_DEBUG_MODE"); |
107 | 0 | this->NoCycles = |
108 | 0 | cm->GetState()->GetGlobalPropertyAsBool("GLOBAL_DEPENDS_NO_CYCLES"); |
109 | 0 | } |
110 | | |
111 | 0 | cmComputeTargetDepends::~cmComputeTargetDepends() = default; |
112 | | |
113 | | bool cmComputeTargetDepends::Compute() |
114 | 0 | { |
115 | | // Build the original graph. |
116 | 0 | this->CollectTargets(); |
117 | 0 | this->CollectDepends(); |
118 | 0 | if (this->DebugMode) { |
119 | 0 | this->DisplayGraph(this->InitialGraph, "initial"); |
120 | 0 | } |
121 | 0 | cmComputeComponentGraph ccg1(this->InitialGraph); |
122 | 0 | ccg1.Compute(); |
123 | 0 | if (!this->CheckComponents(ccg1)) { |
124 | 0 | return false; |
125 | 0 | } |
126 | | |
127 | | // Compute the intermediate graph. |
128 | 0 | this->CollectSideEffects(); |
129 | 0 | this->ComputeIntermediateGraph(); |
130 | 0 | if (this->DebugMode) { |
131 | 0 | this->DisplaySideEffects(); |
132 | 0 | this->DisplayGraph(this->IntermediateGraph, "intermediate"); |
133 | 0 | } |
134 | | |
135 | | // Identify components. |
136 | 0 | cmComputeComponentGraph ccg2(this->IntermediateGraph); |
137 | 0 | ccg2.Compute(); |
138 | 0 | if (this->DebugMode) { |
139 | 0 | this->DisplayComponents(ccg2, "intermediate"); |
140 | 0 | } |
141 | 0 | if (!this->CheckComponents(ccg2)) { |
142 | 0 | return false; |
143 | 0 | } |
144 | | |
145 | | // Compute the final dependency graph. |
146 | 0 | if (!this->ComputeFinalDepends(ccg2)) { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | if (this->DebugMode) { |
150 | 0 | this->DisplayGraph(this->FinalGraph, "final"); |
151 | 0 | } |
152 | |
|
153 | 0 | return true; |
154 | 0 | } |
155 | | |
156 | | void cmComputeTargetDepends::GetTargetDirectDepends(cmGeneratorTarget const* t, |
157 | | cmTargetDependSet& deps) |
158 | 0 | { |
159 | | // Lookup the index for this target. All targets should be known by |
160 | | // this point. |
161 | 0 | auto tii = this->TargetIndex.find(t); |
162 | 0 | assert(tii != this->TargetIndex.end()); |
163 | 0 | size_t i = tii->second; |
164 | | |
165 | | // Get its final dependencies. |
166 | 0 | EdgeList const& nl = this->FinalGraph[i]; |
167 | 0 | for (cmGraphEdge const& ni : nl) { |
168 | 0 | cmGeneratorTarget const* dep = this->Targets[ni]; |
169 | 0 | auto di = deps.insert(dep).first; |
170 | 0 | di->SetType(ni.IsStrong()); |
171 | 0 | di->SetCross(ni.IsCross()); |
172 | 0 | di->SetBacktrace(ni.GetBacktrace()); |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | void cmComputeTargetDepends::CollectTargets() |
177 | 0 | { |
178 | | // Collect all targets from all generators. |
179 | 0 | auto const& lgens = this->GlobalGenerator->GetLocalGenerators(); |
180 | 0 | for (auto const& lgen : lgens) { |
181 | 0 | for (auto const& ti : lgen->GetGeneratorTargets()) { |
182 | 0 | size_t index = this->Targets.size(); |
183 | 0 | this->TargetIndex[ti.get()] = index; |
184 | 0 | this->Targets.push_back(ti.get()); |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | void cmComputeTargetDepends::CollectDepends() |
190 | 0 | { |
191 | | // Allocate the dependency graph adjacency lists. |
192 | 0 | this->InitialGraph.resize(this->Targets.size()); |
193 | | |
194 | | // Compute each dependency list. |
195 | 0 | for (size_t i = 0; i < this->Targets.size(); ++i) { |
196 | 0 | this->CollectTargetDepends(i); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | void cmComputeTargetDepends::CollectTargetDepends(size_t depender_index) |
201 | 0 | { |
202 | | // Get the depender. |
203 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
204 | 0 | if (!depender->IsInBuildSystem()) { |
205 | 0 | return; |
206 | 0 | } |
207 | | |
208 | | // Loop over all targets linked directly in all configs. |
209 | | // We need to make targets depend on the union of all config-specific |
210 | | // dependencies in all targets, because the generated build-systems can't |
211 | | // deal with config-specific dependencies. |
212 | 0 | { |
213 | 0 | std::set<cmLinkItem> emitted; |
214 | |
|
215 | 0 | std::vector<std::string> const& configs = |
216 | 0 | depender->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
217 | 0 | for (std::string const& it : configs) { |
218 | | // A target should not depend on itself. |
219 | 0 | emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); |
220 | 0 | emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); |
221 | |
|
222 | 0 | if (cmLinkImplementation const* impl = depender->GetLinkImplementation( |
223 | 0 | it, cmGeneratorTarget::UseTo::Link)) { |
224 | 0 | for (cmLinkItem const& lib : impl->Libraries) { |
225 | | // Don't emit the same library twice for this target. |
226 | 0 | if (emitted.insert(lib).second) { |
227 | 0 | this->AddTargetDepend(depender_index, lib, true, false, emitted); |
228 | 0 | this->AddInterfaceDepends(depender_index, lib, it, emitted); |
229 | 0 | } |
230 | 0 | } |
231 | 0 | for (cmLinkItem const& obj : impl->Objects) { |
232 | 0 | if (cmSourceFile const* o = depender->Makefile->GetSource( |
233 | 0 | obj.AsStr(), cmSourceFileLocationKind::Known)) { |
234 | 0 | this->AddObjectDepends(depender_index, o, emitted); |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | // Add dependencies on object libraries not otherwise handled above. |
240 | 0 | std::vector<cmSourceFile const*> objectFiles; |
241 | 0 | depender->GetExternalObjects(objectFiles, it); |
242 | 0 | for (cmSourceFile const* o : objectFiles) { |
243 | 0 | this->AddObjectDepends(depender_index, o, emitted); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | // Loop over all utility dependencies. |
249 | 0 | { |
250 | 0 | std::set<cmLinkItem> const& tutils = depender->GetUtilityItems(); |
251 | 0 | std::set<cmLinkItem> emitted; |
252 | | // A target should not depend on itself. |
253 | 0 | emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); |
254 | 0 | emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); |
255 | 0 | for (cmLinkItem const& litem : tutils) { |
256 | | // Don't emit the same utility twice for this target. |
257 | 0 | if (emitted.insert(litem).second) { |
258 | 0 | this->AddTargetDepend(depender_index, litem, false, litem.Cross, |
259 | 0 | emitted); |
260 | 0 | } |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | void cmComputeTargetDepends::AddInterfaceDepends( |
266 | | size_t depender_index, cmGeneratorTarget const* dependee, |
267 | | cmListFileBacktrace const& dependee_backtrace, std::string const& config, |
268 | | std::set<cmLinkItem>& emitted) |
269 | 0 | { |
270 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
271 | 0 | if (cmLinkInterface const* iface = |
272 | 0 | dependee->GetLinkInterface(config, depender)) { |
273 | 0 | for (cmLinkItem const& lib : iface->Libraries) { |
274 | | // Don't emit the same library twice for this target. |
275 | 0 | if (emitted.insert(lib).second) { |
276 | | // Inject the backtrace of the original link dependency whose |
277 | | // link interface we are adding. This indicates the line of |
278 | | // code in the project that caused this dependency to be added. |
279 | 0 | cmLinkItem libBT = lib; |
280 | 0 | libBT.Backtrace = dependee_backtrace; |
281 | 0 | this->AddTargetDepend(depender_index, libBT, true, false, emitted); |
282 | 0 | this->AddInterfaceDepends(depender_index, libBT, config, emitted); |
283 | 0 | } |
284 | 0 | } |
285 | 0 | for (cmLinkItem const& obj : iface->Objects) { |
286 | 0 | if (cmSourceFile const* o = depender->Makefile->GetSource( |
287 | 0 | obj.AsStr(), cmSourceFileLocationKind::Known)) { |
288 | 0 | this->AddObjectDepends(depender_index, o, emitted); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | void cmComputeTargetDepends::AddInterfaceDepends( |
295 | | size_t depender_index, cmLinkItem const& dependee_name, |
296 | | std::string const& config, std::set<cmLinkItem>& emitted) |
297 | 0 | { |
298 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
299 | 0 | cmGeneratorTarget const* dependee = dependee_name.Target; |
300 | | // Skip targets that will not really be linked. This is probably a |
301 | | // name conflict between an external library and an executable |
302 | | // within the project. |
303 | 0 | if (dependee && dependee->GetType() == cmStateEnums::EXECUTABLE && |
304 | 0 | !dependee->IsExecutableWithExports()) { |
305 | 0 | dependee = nullptr; |
306 | 0 | } |
307 | |
|
308 | 0 | if (dependee) { |
309 | | // A target should not depend on itself. |
310 | 0 | emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); |
311 | 0 | emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); |
312 | 0 | this->AddInterfaceDepends(depender_index, dependee, |
313 | 0 | dependee_name.Backtrace, config, emitted); |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | | void cmComputeTargetDepends::AddObjectDepends(size_t depender_index, |
318 | | cmSourceFile const* o, |
319 | | std::set<cmLinkItem>& emitted) |
320 | 0 | { |
321 | 0 | std::string const& objLib = o->GetObjectLibrary(); |
322 | 0 | if (objLib.empty()) { |
323 | 0 | return; |
324 | 0 | } |
325 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
326 | 0 | cmLinkItem const& objItem = |
327 | 0 | depender->ResolveLinkItem(BT<std::string>(objLib)); |
328 | 0 | if (emitted.insert(objItem).second) { |
329 | 0 | if (depender->GetType() != cmStateEnums::EXECUTABLE && |
330 | 0 | depender->GetType() != cmStateEnums::STATIC_LIBRARY && |
331 | 0 | depender->GetType() != cmStateEnums::SHARED_LIBRARY && |
332 | 0 | depender->GetType() != cmStateEnums::MODULE_LIBRARY && |
333 | 0 | depender->GetType() != cmStateEnums::OBJECT_LIBRARY) { |
334 | 0 | this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
335 | 0 | MessageType::FATAL_ERROR, |
336 | 0 | "Only executables and libraries may reference target objects.", |
337 | 0 | depender->GetBacktrace()); |
338 | 0 | return; |
339 | 0 | } |
340 | 0 | const_cast<cmGeneratorTarget*>(depender)->Target->AddUtility(objLib, |
341 | 0 | false); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | void cmComputeTargetDepends::AddTargetDepend(size_t depender_index, |
346 | | cmLinkItem const& dependee_name, |
347 | | bool linking, bool cross, |
348 | | std::set<cmLinkItem>& emitted) |
349 | 0 | { |
350 | | // Get the depender. |
351 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
352 | | |
353 | | // Check the target's makefile first. |
354 | 0 | cmGeneratorTarget const* dependee = dependee_name.Target; |
355 | |
|
356 | 0 | if (!dependee && !linking && |
357 | 0 | (depender->GetType() != cmStateEnums::GLOBAL_TARGET)) { |
358 | 0 | this->GlobalGenerator->GetCMakeInstance()->IssueMessage( |
359 | 0 | MessageType::FATAL_ERROR, |
360 | 0 | cmStrCat("The dependency target \"", dependee_name.AsStr(), |
361 | 0 | "\" of target \"", depender->GetName(), "\" does not exist."), |
362 | 0 | dependee_name.Backtrace); |
363 | 0 | } |
364 | | |
365 | | // Skip targets that will not really be linked. This is probably a |
366 | | // name conflict between an external library and an executable |
367 | | // within the project. |
368 | 0 | if (linking && dependee && dependee->GetType() == cmStateEnums::EXECUTABLE && |
369 | 0 | !dependee->IsExecutableWithExports()) { |
370 | 0 | dependee = nullptr; |
371 | 0 | } |
372 | |
|
373 | 0 | if (dependee) { |
374 | 0 | this->AddTargetDepend(depender_index, dependee, dependee_name.Backtrace, |
375 | 0 | linking, cross, emitted); |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | void cmComputeTargetDepends::AddTargetDepend( |
380 | | size_t depender_index, cmGeneratorTarget const* dependee, |
381 | | cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross, |
382 | | std::set<cmLinkItem>& emitted) |
383 | 0 | { |
384 | 0 | if (!dependee->IsInBuildSystem()) { |
385 | | // Skip targets that are not in the buildsystem but follow their |
386 | | // utility dependencies. |
387 | 0 | std::set<cmLinkItem> const& utils = dependee->GetUtilityItems(); |
388 | 0 | for (cmLinkItem const& i : utils) { |
389 | 0 | if (emitted.insert(i).second) { |
390 | 0 | if (cmGeneratorTarget const* transitive_dependee = i.Target) { |
391 | 0 | this->AddTargetDepend(depender_index, transitive_dependee, |
392 | 0 | i.Backtrace, false, i.Cross, emitted); |
393 | 0 | } |
394 | 0 | } |
395 | 0 | } |
396 | 0 | } else { |
397 | | // Lookup the index for this target. All targets should be known by |
398 | | // this point. |
399 | 0 | auto tii = this->TargetIndex.find(dependee); |
400 | 0 | assert(tii != this->TargetIndex.end()); |
401 | 0 | size_t dependee_index = tii->second; |
402 | | |
403 | | // Add this entry to the dependency graph. |
404 | 0 | this->InitialGraph[depender_index].emplace_back(dependee_index, !linking, |
405 | 0 | cross, dependee_backtrace); |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | void cmComputeTargetDepends::CollectSideEffects() |
410 | 0 | { |
411 | 0 | this->SideEffects.resize(0); |
412 | 0 | this->SideEffects.resize(this->InitialGraph.size()); |
413 | |
|
414 | 0 | size_t n = this->InitialGraph.size(); |
415 | 0 | std::set<size_t> visited; |
416 | 0 | for (size_t i = 0; i < n; ++i) { |
417 | 0 | this->CollectSideEffectsForTarget(visited, i); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | void cmComputeTargetDepends::CollectSideEffectsForTarget( |
422 | | std::set<size_t>& visited, size_t depender_index) |
423 | 0 | { |
424 | 0 | if (!visited.count(depender_index)) { |
425 | 0 | auto& se = this->SideEffects[depender_index]; |
426 | 0 | visited.insert(depender_index); |
427 | 0 | this->Targets[depender_index]->AppendCustomCommandSideEffects( |
428 | 0 | se.CustomCommandSideEffects); |
429 | 0 | this->Targets[depender_index]->AppendLanguageSideEffects( |
430 | 0 | se.LanguageSideEffects); |
431 | |
|
432 | 0 | for (auto const& edge : this->InitialGraph[depender_index]) { |
433 | 0 | this->CollectSideEffectsForTarget(visited, edge); |
434 | 0 | auto const& dse = this->SideEffects[edge]; |
435 | 0 | se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(), |
436 | 0 | dse.CustomCommandSideEffects.cend()); |
437 | 0 | for (auto const& it : dse.LanguageSideEffects) { |
438 | 0 | se.LanguageSideEffects[it.first].insert(it.second.cbegin(), |
439 | 0 | it.second.cend()); |
440 | 0 | } |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | void cmComputeTargetDepends::ComputeIntermediateGraph() |
446 | 0 | { |
447 | 0 | this->IntermediateGraph.resize(0); |
448 | 0 | this->IntermediateGraph.resize(this->InitialGraph.size()); |
449 | |
|
450 | 0 | size_t n = this->InitialGraph.size(); |
451 | 0 | for (size_t i = 0; i < n; ++i) { |
452 | 0 | auto const& initialEdges = this->InitialGraph[i]; |
453 | 0 | auto& intermediateEdges = this->IntermediateGraph[i]; |
454 | 0 | cmGeneratorTarget const* gt = this->Targets[i]; |
455 | 0 | if (gt->GetType() != cmStateEnums::STATIC_LIBRARY && |
456 | 0 | gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { |
457 | 0 | intermediateEdges = initialEdges; |
458 | 0 | } else { |
459 | 0 | if (cmValue optimizeDependencies = |
460 | 0 | gt->GetProperty("OPTIMIZE_DEPENDENCIES")) { |
461 | 0 | if (optimizeDependencies.IsOn()) { |
462 | 0 | this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges); |
463 | 0 | } else { |
464 | 0 | intermediateEdges = initialEdges; |
465 | 0 | } |
466 | 0 | } else { |
467 | 0 | intermediateEdges = initialEdges; |
468 | 0 | } |
469 | 0 | } |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | | void cmComputeTargetDepends::OptimizeLinkDependencies( |
474 | | cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges, |
475 | | cmGraphEdgeList const& inputEdges) |
476 | 0 | { |
477 | 0 | std::set<size_t> emitted; |
478 | 0 | for (auto const& edge : inputEdges) { |
479 | 0 | if (edge.IsStrong()) { |
480 | | // Preserve strong edges |
481 | 0 | outputEdges.push_back(edge); |
482 | 0 | } else { |
483 | 0 | auto const& dse = this->SideEffects[edge]; |
484 | | |
485 | | // Add edges that have custom command side effects |
486 | 0 | for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) { |
487 | 0 | auto index = this->TargetIndex[dep]; |
488 | 0 | if (!emitted.count(index)) { |
489 | 0 | emitted.insert(index); |
490 | 0 | outputEdges.push_back( |
491 | 0 | cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace())); |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | // Add edges that have language side effects for languages we |
496 | | // care about |
497 | 0 | for (auto const& lang : gt->GetAllConfigCompileLanguages()) { |
498 | 0 | auto it = dse.LanguageSideEffects.find(lang); |
499 | 0 | if (it != dse.LanguageSideEffects.end()) { |
500 | 0 | for (cmGeneratorTarget const* dep : it->second) { |
501 | 0 | auto index = this->TargetIndex[dep]; |
502 | 0 | if (!emitted.count(index)) { |
503 | 0 | emitted.insert(index); |
504 | 0 | outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(), |
505 | 0 | edge.GetBacktrace())); |
506 | 0 | } |
507 | 0 | } |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | 0 | } |
512 | 0 | } |
513 | | |
514 | | void cmComputeTargetDepends::DisplayGraph(Graph const& graph, |
515 | | std::string const& name) |
516 | 0 | { |
517 | 0 | fprintf(stderr, "The %s target dependency graph is:\n", name.c_str()); |
518 | 0 | size_t n = graph.size(); |
519 | 0 | for (size_t depender_index = 0; depender_index < n; ++depender_index) { |
520 | 0 | EdgeList const& nl = graph[depender_index]; |
521 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
522 | 0 | fprintf(stderr, "target %zu is [%s]\n", depender_index, |
523 | 0 | depender->GetName().c_str()); |
524 | 0 | for (cmGraphEdge const& ni : nl) { |
525 | 0 | size_t dependee_index = ni; |
526 | 0 | cmGeneratorTarget const* dependee = this->Targets[dependee_index]; |
527 | 0 | fprintf(stderr, " depends on target %zu [%s] (%s)\n", dependee_index, |
528 | 0 | dependee->GetName().c_str(), ni.IsStrong() ? "strong" : "weak"); |
529 | 0 | } |
530 | 0 | } |
531 | 0 | fprintf(stderr, "\n"); |
532 | 0 | } |
533 | | |
534 | | void cmComputeTargetDepends::DisplaySideEffects() |
535 | 0 | { |
536 | 0 | fprintf(stderr, "The side effects are:\n"); |
537 | 0 | size_t n = this->SideEffects.size(); |
538 | 0 | for (size_t depender_index = 0; depender_index < n; ++depender_index) { |
539 | 0 | cmGeneratorTarget const* depender = this->Targets[depender_index]; |
540 | 0 | fprintf(stderr, "target %zu is [%s]\n", depender_index, |
541 | 0 | depender->GetName().c_str()); |
542 | 0 | if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) { |
543 | 0 | fprintf(stderr, " custom commands\n"); |
544 | 0 | for (auto const* gt : |
545 | 0 | this->SideEffects[depender_index].CustomCommandSideEffects) { |
546 | 0 | fprintf(stderr, " from target %zu [%s]\n", this->TargetIndex[gt], |
547 | 0 | gt->GetName().c_str()); |
548 | 0 | } |
549 | 0 | } |
550 | 0 | for (auto const& it : |
551 | 0 | this->SideEffects[depender_index].LanguageSideEffects) { |
552 | 0 | fprintf(stderr, " language %s\n", it.first.c_str()); |
553 | 0 | for (auto const* gt : it.second) { |
554 | 0 | fprintf(stderr, " from target %zu [%s]\n", this->TargetIndex[gt], |
555 | 0 | gt->GetName().c_str()); |
556 | 0 | } |
557 | 0 | } |
558 | 0 | } |
559 | 0 | fprintf(stderr, "\n"); |
560 | 0 | } |
561 | | |
562 | | void cmComputeTargetDepends::DisplayComponents( |
563 | | cmComputeComponentGraph const& ccg, std::string const& name) |
564 | 0 | { |
565 | 0 | fprintf(stderr, "The strongly connected components for the %s graph are:\n", |
566 | 0 | name.c_str()); |
567 | 0 | std::vector<NodeList> const& components = ccg.GetComponents(); |
568 | 0 | size_t n = components.size(); |
569 | 0 | for (size_t c = 0; c < n; ++c) { |
570 | 0 | NodeList const& nl = components[c]; |
571 | 0 | fprintf(stderr, "Component (%zu):\n", c); |
572 | 0 | for (size_t i : nl) { |
573 | 0 | fprintf(stderr, " contains target %zu [%s]\n", i, |
574 | 0 | this->Targets[i]->GetName().c_str()); |
575 | 0 | } |
576 | 0 | } |
577 | 0 | fprintf(stderr, "\n"); |
578 | 0 | } |
579 | | |
580 | | bool cmComputeTargetDepends::CheckComponents( |
581 | | cmComputeComponentGraph const& ccg) |
582 | 0 | { |
583 | | // All non-trivial components should consist only of static |
584 | | // libraries. |
585 | 0 | std::vector<NodeList> const& components = ccg.GetComponents(); |
586 | 0 | size_t nc = components.size(); |
587 | 0 | for (size_t c = 0; c < nc; ++c) { |
588 | | // Get the current component. |
589 | 0 | NodeList const& nl = components[c]; |
590 | | |
591 | | // Skip trivial components. |
592 | 0 | if (nl.size() < 2) { |
593 | 0 | continue; |
594 | 0 | } |
595 | | |
596 | | // Immediately complain if no cycles are allowed at all. |
597 | 0 | if (this->NoCycles) { |
598 | 0 | this->ComplainAboutBadComponent(ccg, c); |
599 | 0 | return false; |
600 | 0 | } |
601 | | |
602 | | // Make sure the component is all STATIC_LIBRARY targets. |
603 | 0 | for (size_t ni : nl) { |
604 | 0 | if (this->Targets[ni]->GetType() != cmStateEnums::STATIC_LIBRARY) { |
605 | 0 | this->ComplainAboutBadComponent(ccg, c); |
606 | 0 | return false; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | 0 | return true; |
611 | 0 | } |
612 | | |
613 | | void cmComputeTargetDepends::ComplainAboutBadComponent( |
614 | | cmComputeComponentGraph const& ccg, size_t c, bool strong) |
615 | 0 | { |
616 | | // Construct the error message. |
617 | 0 | std::ostringstream e; |
618 | 0 | e << "The inter-target dependency graph contains the following " |
619 | 0 | << "strongly connected component (cycle):\n"; |
620 | 0 | std::vector<NodeList> const& components = ccg.GetComponents(); |
621 | 0 | std::vector<size_t> const& cmap = ccg.GetComponentMap(); |
622 | 0 | NodeList const& cl = components[c]; |
623 | 0 | for (size_t i : cl) { |
624 | | // Get the depender. |
625 | 0 | cmGeneratorTarget const* depender = this->Targets[i]; |
626 | | |
627 | | // Describe the depender. |
628 | 0 | e << " \"" << depender->GetName() << "\" of type " |
629 | 0 | << cmState::GetTargetTypeName(depender->GetType()) << "\n"; |
630 | | |
631 | | // List its dependencies that are inside the component. |
632 | 0 | EdgeList const& nl = this->InitialGraph[i]; |
633 | 0 | for (cmGraphEdge const& ni : nl) { |
634 | 0 | size_t j = ni; |
635 | 0 | if (cmap[j] == c) { |
636 | 0 | cmGeneratorTarget const* dependee = this->Targets[j]; |
637 | 0 | e << " depends on \"" << dependee->GetName() << "\"" |
638 | 0 | << " (" << (ni.IsStrong() ? "strong" : "weak") << ")\n"; |
639 | 0 | } |
640 | 0 | } |
641 | 0 | } |
642 | 0 | if (strong) { |
643 | | // Custom command executable dependencies cannot occur within a |
644 | | // component of static libraries. The cycle must appear in calls |
645 | | // to add_dependencies. |
646 | 0 | e << "The component contains at least one cycle consisting of strong " |
647 | 0 | << "dependencies (created by add_dependencies) that cannot be broken."; |
648 | 0 | } else if (this->NoCycles) { |
649 | 0 | e << "The GLOBAL_DEPENDS_NO_CYCLES global property is enabled, so " |
650 | 0 | << "cyclic dependencies are not allowed even among static libraries."; |
651 | 0 | } else { |
652 | 0 | e << "At least one of these targets is not a STATIC_LIBRARY. " |
653 | 0 | << "Cyclic dependencies are allowed only among static libraries."; |
654 | 0 | } |
655 | 0 | cmSystemTools::Error(e.str()); |
656 | 0 | } |
657 | | |
658 | | bool cmComputeTargetDepends::IntraComponent(std::vector<size_t> const& cmap, |
659 | | size_t c, size_t i, size_t* head, |
660 | | std::set<size_t>& emitted, |
661 | | std::set<size_t>& visited) |
662 | 0 | { |
663 | 0 | if (!visited.insert(i).second) { |
664 | | // Cycle in utility depends! |
665 | 0 | return false; |
666 | 0 | } |
667 | 0 | if (emitted.insert(i).second) { |
668 | | // Honor strong intra-component edges in the final order. |
669 | 0 | EdgeList const& el = this->InitialGraph[i]; |
670 | 0 | for (cmGraphEdge const& edge : el) { |
671 | 0 | size_t j = edge; |
672 | 0 | if (cmap[j] == c && edge.IsStrong()) { |
673 | 0 | this->FinalGraph[i].emplace_back(j, true, edge.IsCross(), |
674 | 0 | edge.GetBacktrace()); |
675 | 0 | if (!this->IntraComponent(cmap, c, j, head, emitted, visited)) { |
676 | 0 | return false; |
677 | 0 | } |
678 | 0 | } |
679 | 0 | } |
680 | | |
681 | | // Prepend to a linear linked-list of intra-component edges. |
682 | 0 | if (*head != cmComputeComponentGraph::INVALID_COMPONENT) { |
683 | 0 | this->FinalGraph[i].emplace_back(*head, false, false, |
684 | 0 | cmListFileBacktrace()); |
685 | 0 | } else { |
686 | 0 | this->ComponentTail[c] = i; |
687 | 0 | } |
688 | 0 | *head = i; |
689 | 0 | } |
690 | 0 | return true; |
691 | 0 | } |
692 | | |
693 | | bool cmComputeTargetDepends::ComputeFinalDepends( |
694 | | cmComputeComponentGraph const& ccg) |
695 | 0 | { |
696 | | // Get the component graph information. |
697 | 0 | std::vector<NodeList> const& components = ccg.GetComponents(); |
698 | 0 | Graph const& cgraph = ccg.GetComponentGraph(); |
699 | | |
700 | | // Allocate the final graph. |
701 | 0 | this->FinalGraph.resize(0); |
702 | 0 | this->FinalGraph.resize(this->InitialGraph.size()); |
703 | | |
704 | | // Choose intra-component edges to linearize dependencies. |
705 | 0 | std::vector<size_t> const& cmap = ccg.GetComponentMap(); |
706 | 0 | this->ComponentHead.resize(components.size()); |
707 | 0 | this->ComponentTail.resize(components.size()); |
708 | 0 | size_t nc = components.size(); |
709 | 0 | for (size_t c = 0; c < nc; ++c) { |
710 | 0 | size_t head = cmComputeComponentGraph::INVALID_COMPONENT; |
711 | 0 | std::set<size_t> emitted; |
712 | 0 | NodeList const& nl = components[c]; |
713 | 0 | for (size_t ni : cmReverseRange(nl)) { |
714 | 0 | std::set<size_t> visited; |
715 | 0 | if (!this->IntraComponent(cmap, c, ni, &head, emitted, visited)) { |
716 | | // Cycle in add_dependencies within component! |
717 | 0 | this->ComplainAboutBadComponent(ccg, c, true); |
718 | 0 | return false; |
719 | 0 | } |
720 | 0 | } |
721 | 0 | this->ComponentHead[c] = head; |
722 | 0 | } |
723 | | |
724 | | // Convert inter-component edges to connect component tails to heads. |
725 | 0 | size_t n = cgraph.size(); |
726 | 0 | for (size_t depender_component = 0; depender_component < n; |
727 | 0 | ++depender_component) { |
728 | 0 | size_t depender_component_tail = this->ComponentTail[depender_component]; |
729 | 0 | EdgeList const& nl = cgraph[depender_component]; |
730 | 0 | for (cmGraphEdge const& ni : nl) { |
731 | 0 | size_t dependee_component = ni; |
732 | 0 | size_t dependee_component_head = this->ComponentHead[dependee_component]; |
733 | 0 | this->FinalGraph[depender_component_tail].emplace_back( |
734 | 0 | dependee_component_head, ni.IsStrong(), ni.IsCross(), |
735 | 0 | ni.GetBacktrace()); |
736 | 0 | } |
737 | 0 | } |
738 | 0 | return true; |
739 | 0 | } |