| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmComputeLinkDepends.h"
- #include <algorithm>
- #include <cassert>
- #include <cstddef>
- #include <cstdio>
- #include <iterator>
- #include <sstream>
- #include <type_traits>
- #include <unordered_map>
- #include <utility>
- #include <cm/memory>
- #include <cm/string_view>
- #include <cmext/string_view>
- #include "cmsys/RegularExpression.hxx"
- #include "cmComputeComponentGraph.h"
- #include "cmGeneratorExpression.h"
- #include "cmGeneratorExpressionDAGChecker.h"
- #include "cmGeneratorTarget.h"
- #include "cmGlobalGenerator.h"
- #include "cmList.h"
- #include "cmListFileCache.h"
- #include "cmLocalGenerator.h"
- #include "cmMakefile.h"
- #include "cmMessageType.h"
- #include "cmPolicies.h"
- #include "cmRange.h"
- #include "cmStateTypes.h"
- #include "cmStringAlgorithms.h"
- #include "cmTarget.h"
- #include "cmValue.h"
- #include "cmake.h"
- /*
- This file computes an ordered list of link items to use when linking a
- single target in one configuration. Each link item is identified by
- the string naming it. A graph of dependencies is created in which
- each node corresponds to one item and directed edges lead from nodes to
- those which must *follow* them on the link line. For example, the
- graph
- A -> B -> C
- will lead to the link line order
- A B C
- The set of items placed in the graph is formed with a breadth-first
- search of the link dependencies starting from the main target.
- There are two types of items: those with known direct dependencies and
- those without known dependencies. We will call the two types "known
- items" and "unknown items", respectively. Known items are those whose
- names correspond to targets (built or imported) and those for which an
- old-style <item>_LIB_DEPENDS variable is defined. All other items are
- unknown and we must infer dependencies for them. For items that look
- like flags (beginning with '-') we trivially infer no dependencies,
- and do not include them in the dependencies of other items.
- Known items have dependency lists ordered based on how the user
- specified them. We can use this order to infer potential dependencies
- of unknown items. For example, if link items A and B are unknown and
- items X and Y are known, then we might have the following dependency
- lists:
- X: Y A B
- Y: A B
- The explicitly known dependencies form graph edges
- X -> Y , X -> A , X -> B , Y -> A , Y -> B
- We can also infer the edge
- A -> B
- because *every* time A appears B is seen on its right. We do not know
- whether A really needs symbols from B to link, but it *might* so we
- must preserve their order. This is the case also for the following
- explicit lists:
- X: A B Y
- Y: A B
- Here, A is followed by the set {B,Y} in one list, and {B} in the other
- list. The intersection of these sets is {B}, so we can infer that A
- depends on at most B. Meanwhile B is followed by the set {Y} in one
- list and {} in the other. The intersection is {} so we can infer that
- B has no dependencies.
- Let's make a more complex example by adding unknown item C and
- considering these dependency lists:
- X: A B Y C
- Y: A C B
- The explicit edges are
- X -> Y , X -> A , X -> B , X -> C , Y -> A , Y -> B , Y -> C
- For the unknown items, we infer dependencies by looking at the
- "follow" sets:
- A: intersect( {B,Y,C} , {C,B} ) = {B,C} ; infer edges A -> B , A -> C
- B: intersect( {Y,C} , {} ) = {} ; infer no edges
- C: intersect( {} , {B} ) = {} ; infer no edges
- Note that targets are never inferred as dependees because outside
- libraries should not depend on them.
- ------------------------------------------------------------------------------
- The initial exploration of dependencies using a BFS associates an
- integer index with each link item. When the graph is built outgoing
- edges are sorted by this index.
- After the initial exploration of the link interface tree, any
- transitive (dependent) shared libraries that were encountered and not
- included in the interface are processed in their own BFS. This BFS
- follows only the dependent library lists and not the link interfaces.
- They are added to the link items with a mark indicating that the are
- transitive dependencies. Then cmComputeLinkInformation deals with
- them on a per-platform basis.
- The complete graph formed from all known and inferred dependencies may
- not be acyclic, so an acyclic version must be created.
- The original graph is converted to a directed acyclic graph in which
- each node corresponds to a strongly connected component of the
- original graph. For example, the dependency graph
- X -> A -> B -> C -> A -> Y
- contains strongly connected components {X}, {A,B,C}, and {Y}. The
- implied directed acyclic graph (DAG) is
- {X} -> {A,B,C} -> {Y}
- We then compute a topological order for the DAG nodes to serve as a
- reference for satisfying dependencies efficiently. We perform the DFS
- in reverse order and assign topological order indices counting down so
- that the result is as close to the original BFS order as possible
- without violating dependencies.
- ------------------------------------------------------------------------------
- The final link entry order is constructed as follows. We first walk
- through and emit the *original* link line as specified by the user.
- As each item is emitted, a set of pending nodes in the component DAG
- is maintained. When a pending component has been completely seen, it
- is removed from the pending set and its dependencies (following edges
- of the DAG) are added. A trivial component (those with one item) is
- complete as soon as its item is seen. A non-trivial component (one
- with more than one item; assumed to be static libraries) is complete
- when *all* its entries have been seen *twice* (all entries seen once,
- then all entries seen again, not just each entry twice). A pending
- component tracks which items have been seen and a count of how many
- times the component needs to be seen (once for trivial components,
- twice for non-trivial). If at any time another component finishes and
- re-adds an already pending component, the pending component is reset
- so that it needs to be seen in its entirety again. This ensures that
- all dependencies of a component are satisfied no matter where it
- appears.
- After the original link line has been completed, we append to it the
- remaining pending components and their dependencies. This is done by
- repeatedly emitting the first item from the first pending component
- and following the same update rules as when traversing the original
- link line. Since the pending components are kept in topological order
- they are emitted with minimal repeats (we do not want to emit a
- component just to have it added again when another component is
- completed later). This process continues until no pending components
- remain. We know it will terminate because the component graph is
- guaranteed to be acyclic.
- The final list of items produced by this procedure consists of the
- original user link line followed by minimal additional items needed to
- satisfy dependencies. The final list is then filtered to de-duplicate
- items that we know the linker will reuse automatically (shared libs).
- */
- namespace {
- // LINK_LIBRARY helpers
- bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
- std::string const& feature)
- {
- auto featureSupported = cmStrCat(
- "CMAKE_", linkLanguage, "_LINK_LIBRARY_USING_", feature, "_SUPPORTED");
- if (makefile->GetDefinition(featureSupported).IsOn()) {
- return true;
- }
- featureSupported =
- cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature, "_SUPPORTED");
- return makefile->GetDefinition(featureSupported).IsOn();
- }
- // LINK_GROUP helpers
- const auto LG_BEGIN = "<LINK_GROUP:"_s;
- const auto LG_END = "</LINK_GROUP:"_s;
- inline std::string ExtractGroupFeature(std::string const& item)
- {
- return item.substr(LG_BEGIN.length(),
- item.find(':', LG_BEGIN.length()) - LG_BEGIN.length());
- }
- bool IsGroupFeatureSupported(cmMakefile* makefile,
- std::string const& linkLanguage,
- std::string const& feature)
- {
- auto featureSupported = cmStrCat(
- "CMAKE_", linkLanguage, "_LINK_GROUP_USING_", feature, "_SUPPORTED");
- if (makefile->GetDefinition(featureSupported).IsOn()) {
- return true;
- }
- featureSupported =
- cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
- return makefile->GetDefinition(featureSupported).IsOn();
- }
- class EntriesProcessing
- {
- public:
- using LinkEntry = cmComputeLinkDepends::LinkEntry;
- using EntryVector = cmComputeLinkDepends::EntryVector;
- EntriesProcessing(const cmGeneratorTarget* target,
- const std::string& linkLanguage, EntryVector& entries,
- EntryVector& finalEntries)
- : Entries(entries)
- , FinalEntries(finalEntries)
- {
- const auto* makefile = target->Makefile;
- switch (target->GetPolicyStatusCMP0156()) {
- case cmPolicies::WARN:
- if (!makefile->GetCMakeInstance()->GetIsInTryCompile() &&
- makefile->PolicyOptionalWarningEnabled(
- "CMAKE_POLICY_WARNING_CMP0156")) {
- makefile->GetCMakeInstance()->IssueMessage(
- MessageType::AUTHOR_WARNING,
- cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156),
- "\nSince the policy is not set, legacy libraries "
- "de-duplication strategy will be applied."),
- target->GetBacktrace());
- }
- CM_FALLTHROUGH;
- case cmPolicies::OLD:
- // rely on default initialization of the class
- break;
- case cmPolicies::REQUIRED_IF_USED:
- case cmPolicies::REQUIRED_ALWAYS:
- makefile->GetCMakeInstance()->IssueMessage(
- MessageType::FATAL_ERROR,
- cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0156),
- target->GetBacktrace());
- CM_FALLTHROUGH;
- case cmPolicies::NEW: {
- if (auto libProcessing = makefile->GetDefinition(cmStrCat(
- "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) {
- cmsys::RegularExpression processingOption{
- "^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$"
- };
- std::string errorMessage;
- for (auto const& option : cmList{ libProcessing }) {
- if (processingOption.find(option)) {
- if (processingOption.match(1) == "ORDER") {
- if (processingOption.match(2) == "FORWARD") {
- this->Order = Forward;
- } else if (processingOption.match(2) == "REVERSE") {
- this->Order = Reverse;
- } else {
- errorMessage += cmStrCat(" ", option, '\n');
- }
- } else if (processingOption.match(1) == "UNICITY") {
- if (processingOption.match(2) == "ALL") {
- this->Unicity = All;
- } else if (processingOption.match(2) == "NONE") {
- this->Unicity = None;
- } else if (processingOption.match(2) == "SHARED") {
- this->Unicity = Shared;
- } else {
- errorMessage += cmStrCat(" ", option, '\n');
- }
- }
- } else {
- errorMessage += cmStrCat(" ", option, '\n');
- }
- }
- if (!errorMessage.empty()) {
- makefile->GetCMakeInstance()->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Erroneous option(s) for 'CMAKE_", linkLanguage,
- "_LINK_LIBRARIES_PROCESSING':\n", errorMessage),
- target->GetBacktrace());
- }
- }
- }
- }
- }
- void AddGroups(const std::map<size_t, std::vector<size_t>>& groups)
- {
- if (!groups.empty()) {
- this->Groups = &groups;
- // record all libraries as part of groups to ensure correct
- // deduplication: libraries as part of groups are always kept.
- for (const auto& group : groups) {
- for (auto index : group.second) {
- this->Emitted.insert(index);
- }
- }
- }
- }
- void AddLibraries(const std::vector<size_t>& libEntries)
- {
- if (this->Order == Reverse) {
- // Iterate in reverse order so we can keep only the last occurrence
- // of a library.
- this->AddLibraries(cmReverseRange(libEntries));
- } else {
- this->AddLibraries(cmMakeRange(libEntries));
- }
- }
- void AddObjects(const std::vector<size_t>& objectEntries)
- {
- // Place explicitly linked object files in the front. The linker will
- // always use them anyway, and they may depend on symbols from libraries.
- if (this->Order == Reverse) {
- // Append in reverse order at the end since we reverse the final order.
- for (auto index : cmReverseRange(objectEntries)) {
- this->FinalEntries.emplace_back(this->Entries[index]);
- }
- } else {
- // Append in reverse order to ensure correct final order
- for (auto index : cmReverseRange(objectEntries)) {
- this->FinalEntries.emplace(this->FinalEntries.begin(),
- this->Entries[index]);
- }
- }
- }
- void Finalize()
- {
- if (this->Order == Reverse) {
- // Reverse the resulting order since we iterated in reverse.
- std::reverse(this->FinalEntries.begin(), this->FinalEntries.end());
- }
- // expand groups
- if (this->Groups != nullptr) {
- for (const auto& group : *this->Groups) {
- const LinkEntry& groupEntry = this->Entries[group.first];
- auto it = this->FinalEntries.begin();
- while (true) {
- it = std::find_if(it, this->FinalEntries.end(),
- [&groupEntry](const LinkEntry& entry) -> bool {
- return groupEntry.Item == entry.Item;
- });
- if (it == this->FinalEntries.end()) {
- break;
- }
- it->Item.Value = "</LINK_GROUP>";
- for (auto index = group.second.rbegin();
- index != group.second.rend(); ++index) {
- it = this->FinalEntries.insert(it, this->Entries[*index]);
- }
- it = this->FinalEntries.insert(it, groupEntry);
- it->Item.Value = "<LINK_GROUP>";
- }
- }
- }
- }
- private:
- enum OrderKind
- {
- Forward,
- Reverse
- };
- enum UnicityKind
- {
- None,
- Shared,
- All
- };
- bool IncludeEntry(LinkEntry const& entry) const
- {
- return this->Unicity == None ||
- (this->Unicity == Shared &&
- (entry.Target == nullptr ||
- entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) ||
- (this->Unicity == All && entry.Kind != LinkEntry::Library);
- }
- template <typename Range>
- void AddLibraries(const Range& libEntries)
- {
- for (auto index : libEntries) {
- LinkEntry const& entry = this->Entries[index];
- if (this->IncludeEntry(entry) || this->Emitted.insert(index).second) {
- this->FinalEntries.emplace_back(entry);
- }
- }
- }
- OrderKind Order = Reverse;
- UnicityKind Unicity = Shared;
- EntryVector& Entries;
- EntryVector& FinalEntries;
- std::set<size_t> Emitted;
- const std::map<size_t, std::vector<size_t>>* Groups = nullptr;
- };
- }
- std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT =
- cmLinkItem::DEFAULT;
- cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
- const std::string& config,
- const std::string& linkLanguage)
- {
- // Store context information.
- this->Target = target;
- this->Makefile = this->Target->Target->GetMakefile();
- this->GlobalGenerator =
- this->Target->GetLocalGenerator()->GetGlobalGenerator();
- this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
- this->LinkLanguage = linkLanguage;
- // target oriented feature override property takes precedence over
- // global override property
- cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
- auto const& keys = this->Target->GetPropertyKeys();
- std::for_each(
- keys.cbegin(), keys.cend(),
- [this, &lloPrefix, &config, &linkLanguage](std::string const& key) {
- if (cmHasPrefix(key, lloPrefix)) {
- if (cmValue feature = this->Target->GetProperty(key)) {
- if (!feature->empty() && key.length() > lloPrefix.length()) {
- auto item = key.substr(lloPrefix.length());
- cmGeneratorExpressionDAGChecker dag{
- this->Target->GetBacktrace(),
- this->Target,
- "LINK_LIBRARY_OVERRIDE",
- nullptr,
- nullptr,
- this->Target->GetLocalGenerator()
- };
- auto overrideFeature = cmGeneratorExpression::Evaluate(
- *feature, this->Target->GetLocalGenerator(), config,
- this->Target, &dag, this->Target, linkLanguage);
- this->LinkLibraryOverride.emplace(item, overrideFeature);
- }
- }
- }
- });
- // global override property
- if (cmValue linkLibraryOverride =
- this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
- cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(),
- target,
- "LINK_LIBRARY_OVERRIDE",
- nullptr,
- nullptr,
- target->GetLocalGenerator() };
- auto overrideValue = cmGeneratorExpression::Evaluate(
- *linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
- target, linkLanguage);
- auto overrideList = cmTokenize(overrideValue, ","_s);
- if (overrideList.size() >= 2) {
- auto const& feature = overrideList.front();
- for_each(overrideList.cbegin() + 1, overrideList.cend(),
- [this, &feature](std::string const& item) {
- this->LinkLibraryOverride.emplace(item, feature);
- });
- }
- }
- // The configuration being linked.
- this->HasConfig = !config.empty();
- this->Config = (this->HasConfig) ? config : std::string();
- std::vector<std::string> debugConfigs =
- this->Makefile->GetCMakeInstance()->GetDebugConfigs();
- this->LinkType = CMP0003_ComputeLinkType(this->Config, debugConfigs);
- // Enable debug mode if requested.
- this->DebugMode = this->Makefile->IsOn("CMAKE_LINK_DEPENDS_DEBUG_MODE");
- // Assume no compatibility until set.
- this->OldLinkDirMode = false;
- // No computation has been done.
- this->CCG = nullptr;
- }
- cmComputeLinkDepends::~cmComputeLinkDepends() = default;
- void cmComputeLinkDepends::SetOldLinkDirMode(bool b)
- {
- this->OldLinkDirMode = b;
- }
- std::vector<cmComputeLinkDepends::LinkEntry> const&
- cmComputeLinkDepends::Compute()
- {
- // Follow the link dependencies of the target to be linked.
- this->AddDirectLinkEntries();
- // Complete the breadth-first search of dependencies.
- while (!this->BFSQueue.empty()) {
- // Get the next entry.
- BFSEntry qe = this->BFSQueue.front();
- this->BFSQueue.pop();
- // Follow the entry's dependencies.
- this->FollowLinkEntry(qe);
- }
- // Complete the search of shared library dependencies.
- while (!this->SharedDepQueue.empty()) {
- // Handle the next entry.
- this->HandleSharedDependency(this->SharedDepQueue.front());
- this->SharedDepQueue.pop();
- }
- // Infer dependencies of targets for which they were not known.
- this->InferDependencies();
- // finalize groups dependencies
- // All dependencies which are raw items must be replaced by the group
- // it belongs to, if any.
- this->UpdateGroupDependencies();
- // Cleanup the constraint graph.
- this->CleanConstraintGraph();
- // Display the constraint graph.
- if (this->DebugMode) {
- fprintf(stderr,
- "---------------------------------------"
- "---------------------------------------\n");
- fprintf(stderr, "Link dependency analysis for target %s, config %s\n",
- this->Target->GetName().c_str(),
- this->HasConfig ? this->Config.c_str() : "noconfig");
- this->DisplayConstraintGraph();
- }
- // Compute the DAG of strongly connected components. The algorithm
- // used by cmComputeComponentGraph should identify the components in
- // the same order in which the items were originally discovered in
- // the BFS. This should preserve the original order when no
- // constraints disallow it.
- this->CCG =
- cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
- this->CCG->Compute();
- if (!this->CheckCircularDependencies()) {
- return this->FinalLinkEntries;
- }
- // Compute the final ordering.
- this->OrderLinkEntries();
- // Compute the final set of link entries.
- EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
- this->EntryList,
- this->FinalLinkEntries };
- // Add groups first, to ensure that libraries of the groups are always kept.
- entriesProcessing.AddGroups(this->GroupItems);
- entriesProcessing.AddLibraries(this->FinalLinkOrder);
- entriesProcessing.AddObjects(this->ObjectEntries);
- entriesProcessing.Finalize();
- // Display the final set.
- if (this->DebugMode) {
- this->DisplayFinalEntries();
- }
- return this->FinalLinkEntries;
- }
- std::string const& cmComputeLinkDepends::GetCurrentFeature(
- std::string const& item, std::string const& defaultFeature) const
- {
- auto it = this->LinkLibraryOverride.find(item);
- return it == this->LinkLibraryOverride.end() ? defaultFeature : it->second;
- }
- std::pair<std::map<cmLinkItem, size_t>::iterator, bool>
- cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
- {
- std::map<cmLinkItem, size_t>::value_type index_entry(
- item, static_cast<size_t>(this->EntryList.size()));
- auto lei = this->LinkEntryIndex.insert(index_entry);
- if (lei.second) {
- this->EntryList.emplace_back();
- this->InferredDependSets.emplace_back();
- this->EntryConstraintGraph.emplace_back();
- }
- return lei;
- }
- std::pair<size_t, bool> cmComputeLinkDepends::AddLinkEntry(
- cmLinkItem const& item, size_t groupIndex)
- {
- // Allocate a spot for the item entry.
- auto lei = this->AllocateLinkEntry(item);
- // Check if the item entry has already been added.
- if (!lei.second) {
- // Yes. We do not need to follow the item's dependencies again.
- return { lei.first->second, false };
- }
- // Initialize the item entry.
- size_t index = lei.first->second;
- LinkEntry& entry = this->EntryList[index];
- entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
- entry.Target = item.Target;
- entry.Feature = item.Feature;
- if (!entry.Target && entry.Item.Value[0] == '-' &&
- entry.Item.Value[1] != 'l' &&
- entry.Item.Value.substr(0, 10) != "-framework") {
- entry.Kind = LinkEntry::Flag;
- entry.Feature = LinkEntry::DEFAULT;
- } else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) &&
- cmHasSuffix(entry.Item.Value, '>')) {
- entry.Kind = LinkEntry::Group;
- }
- if (entry.Kind != LinkEntry::Group) {
- // If the item has dependencies queue it to follow them.
- if (entry.Target) {
- // Target dependencies are always known. Follow them.
- BFSEntry qe = { index, groupIndex, nullptr };
- this->BFSQueue.push(qe);
- } else {
- // Look for an old-style <item>_LIB_DEPENDS variable.
- std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
- if (cmValue val = this->Makefile->GetDefinition(var)) {
- // The item dependencies are known. Follow them.
- BFSEntry qe = { index, groupIndex, val->c_str() };
- this->BFSQueue.push(qe);
- } else if (entry.Kind != LinkEntry::Flag) {
- // The item dependencies are not known. We need to infer them.
- this->InferredDependSets[index].Initialized = true;
- }
- }
- }
- return { index, true };
- }
- void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
- {
- assert(!item.Target); // The item is an object file, not its target.
- // Allocate a spot for the item entry.
- auto lei = this->AllocateLinkEntry(item);
- // Check if the item entry has already been added.
- if (!lei.second) {
- return;
- }
- // Initialize the item entry.
- size_t index = lei.first->second;
- LinkEntry& entry = this->EntryList[index];
- entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
- entry.Kind = LinkEntry::Object;
- entry.ObjectSource = item.ObjectSource;
- // Record explicitly linked object files separately.
- this->ObjectEntries.emplace_back(index);
- }
- void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
- {
- // Get this entry representation.
- size_t depender_index =
- qe.GroupIndex == cmComputeComponentGraph::INVALID_COMPONENT
- ? qe.Index
- : qe.GroupIndex;
- LinkEntry const& entry = this->EntryList[qe.Index];
- // Follow the item's dependencies.
- if (entry.Target) {
- // Follow the target dependencies.
- if (cmLinkInterface const* iface =
- entry.Target->GetLinkInterface(this->Config, this->Target)) {
- const bool isIface =
- entry.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY;
- // This target provides its own link interface information.
- this->AddLinkEntries(depender_index, iface->Libraries);
- this->AddLinkObjects(iface->Objects);
- for (auto const& language : iface->Languages) {
- auto runtimeEntries = iface->LanguageRuntimeLibraries.find(language);
- if (runtimeEntries != iface->LanguageRuntimeLibraries.end()) {
- this->AddLinkEntries(depender_index, runtimeEntries->second);
- }
- }
- if (isIface) {
- return;
- }
- // Handle dependent shared libraries.
- this->FollowSharedDeps(depender_index, iface);
- // Support for CMP0003.
- for (cmLinkItem const& oi : iface->WrongConfigLibraries) {
- this->CheckWrongConfigItem(oi);
- }
- }
- } else {
- // Follow the old-style dependency list.
- this->AddVarLinkEntries(depender_index, qe.LibDepends);
- }
- }
- void cmComputeLinkDepends::FollowSharedDeps(size_t depender_index,
- cmLinkInterface const* iface,
- bool follow_interface)
- {
- // Follow dependencies if we have not followed them already.
- if (this->SharedDepFollowed.insert(depender_index).second) {
- if (follow_interface) {
- this->QueueSharedDependencies(depender_index, iface->Libraries);
- }
- this->QueueSharedDependencies(depender_index, iface->SharedDeps);
- }
- }
- void cmComputeLinkDepends::QueueSharedDependencies(
- size_t depender_index, std::vector<cmLinkItem> const& deps)
- {
- for (cmLinkItem const& li : deps) {
- SharedDepEntry qe;
- qe.Item = li;
- qe.DependerIndex = depender_index;
- this->SharedDepQueue.push(qe);
- }
- }
- void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
- {
- // Allocate a spot for the item entry.
- auto lei = this->AllocateLinkEntry(dep.Item);
- size_t index = lei.first->second;
- // Check if the target does not already has an entry.
- if (lei.second) {
- // Initialize the item entry.
- LinkEntry& entry = this->EntryList[index];
- entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace);
- entry.Target = dep.Item.Target;
- // This item was added specifically because it is a dependent
- // shared library. It may get special treatment
- // in cmComputeLinkInformation.
- entry.Kind = LinkEntry::SharedDep;
- }
- // Get the link entry for this target.
- LinkEntry& entry = this->EntryList[index];
- // This shared library dependency must follow the item that listed
- // it.
- this->EntryConstraintGraph[dep.DependerIndex].emplace_back(
- index, true, false, cmListFileBacktrace());
- // Target items may have their own dependencies.
- if (entry.Target) {
- if (cmLinkInterface const* iface =
- entry.Target->GetLinkInterface(this->Config, this->Target)) {
- // Follow public and private dependencies transitively.
- this->FollowSharedDeps(index, iface, true);
- }
- }
- }
- void cmComputeLinkDepends::AddVarLinkEntries(size_t depender_index,
- const char* value)
- {
- // This is called to add the dependencies named by
- // <item>_LIB_DEPENDS. The variable contains a semicolon-separated
- // list. The list contains link-type;item pairs and just items.
- cmList deplist{ value };
- // Look for entries meant for this configuration.
- std::vector<cmLinkItem> actual_libs;
- cmTargetLinkLibraryType llt = GENERAL_LibraryType;
- bool haveLLT = false;
- for (std::string const& d : deplist) {
- if (d == "debug") {
- llt = DEBUG_LibraryType;
- haveLLT = true;
- } else if (d == "optimized") {
- llt = OPTIMIZED_LibraryType;
- haveLLT = true;
- } else if (d == "general") {
- llt = GENERAL_LibraryType;
- haveLLT = true;
- } else if (!d.empty()) {
- // If no explicit link type was given prior to this entry then
- // check if the entry has its own link type variable. This is
- // needed for compatibility with dependency files generated by
- // the export_library_dependencies command from CMake 2.4 and
- // lower.
- if (!haveLLT) {
- std::string var = cmStrCat(d, "_LINK_TYPE");
- if (cmValue val = this->Makefile->GetDefinition(var)) {
- if (*val == "debug") {
- llt = DEBUG_LibraryType;
- } else if (*val == "optimized") {
- llt = OPTIMIZED_LibraryType;
- }
- }
- }
- // If the library is meant for this link type then use it.
- if (llt == GENERAL_LibraryType || llt == this->LinkType) {
- actual_libs.emplace_back(this->ResolveLinkItem(depender_index, d));
- } else if (this->OldLinkDirMode) {
- cmLinkItem item = this->ResolveLinkItem(depender_index, d);
- this->CheckWrongConfigItem(item);
- }
- // Reset the link type until another explicit type is given.
- llt = GENERAL_LibraryType;
- haveLLT = false;
- }
- }
- // Add the entries from this list.
- this->AddLinkEntries(depender_index, actual_libs);
- }
- void cmComputeLinkDepends::AddDirectLinkEntries()
- {
- // Add direct link dependencies in this configuration.
- cmLinkImplementation const* impl = this->Target->GetLinkImplementation(
- this->Config, cmGeneratorTarget::LinkInterfaceFor::Link);
- this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT,
- impl->Libraries);
- this->AddLinkObjects(impl->Objects);
- for (auto const& language : impl->Languages) {
- auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language);
- if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) {
- this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT,
- runtimeEntries->second);
- }
- }
- for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
- this->CheckWrongConfigItem(wi);
- }
- }
- template <typename T>
- void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
- std::vector<T> const& libs)
- {
- // Track inferred dependency sets implied by this list.
- std::map<size_t, DependSet> dependSets;
- bool inGroup = false;
- std::pair<size_t, bool> groupIndex{
- cmComputeComponentGraph::INVALID_COMPONENT, false
- };
- std::vector<size_t> groupItems;
- // Loop over the libraries linked directly by the depender.
- for (T const& l : libs) {
- // Skip entries that will resolve to the target getting linked or
- // are empty.
- cmLinkItem const& item = l;
- if (item.AsStr() == this->Target->GetName() || item.AsStr().empty()) {
- continue;
- }
- // emit a warning if an undefined feature is used as part of
- // an imported target
- if (item.Feature != LinkEntry::DEFAULT &&
- depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
- const auto& depender = this->EntryList[depender_index];
- if (depender.Target != nullptr && depender.Target->IsImported() &&
- !IsFeatureSupported(this->Makefile, this->LinkLanguage,
- item.Feature)) {
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_ERROR,
- cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
- "' uses the generator-expression '$<LINK_LIBRARY>' with "
- "the feature '",
- item.Feature,
- "', which is undefined or unsupported.\nDid you miss to "
- "define it by setting variables \"CMAKE_",
- this->LinkLanguage, "_LINK_LIBRARY_USING_", item.Feature,
- "\" and \"CMAKE_", this->LinkLanguage,
- "_LINK_LIBRARY_USING_", item.Feature, "_SUPPORTED\"?"),
- this->Target->GetBacktrace());
- }
- }
- if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
- cmHasSuffix(item.AsStr(), '>')) {
- groupIndex = this->AddLinkEntry(item);
- if (groupIndex.second) {
- LinkEntry& entry = this->EntryList[groupIndex.first];
- entry.Feature = ExtractGroupFeature(item.AsStr());
- }
- inGroup = true;
- if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
- this->EntryConstraintGraph[depender_index].emplace_back(
- groupIndex.first, false, false, cmListFileBacktrace());
- } else {
- // This is a direct dependency of the target being linked.
- this->OriginalEntries.push_back(groupIndex.first);
- }
- continue;
- }
- size_t dependee_index;
- if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) {
- dependee_index = groupIndex.first;
- if (groupIndex.second) {
- this->GroupItems.emplace(groupIndex.first, groupItems);
- }
- inGroup = false;
- groupIndex = std::make_pair(-1, false);
- groupItems.clear();
- continue;
- }
- if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT &&
- inGroup) {
- const auto& depender = this->EntryList[depender_index];
- const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
- if (depender.Target != nullptr && depender.Target->IsImported() &&
- !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
- groupFeature)) {
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_ERROR,
- cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
- "' uses the generator-expression '$<LINK_GROUP>' with "
- "the feature '",
- groupFeature,
- "', which is undefined or unsupported.\nDid you miss to "
- "define it by setting variables \"CMAKE_",
- this->LinkLanguage, "_LINK_GROUP_USING_", groupFeature,
- "\" and \"CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_",
- groupFeature, "_SUPPORTED\"?"),
- this->Target->GetBacktrace());
- }
- }
- // Add a link entry for this item.
- auto ale = this->AddLinkEntry(item, groupIndex.first);
- dependee_index = ale.first;
- LinkEntry& entry = this->EntryList[dependee_index];
- auto const& itemFeature =
- this->GetCurrentFeature(entry.Item.Value, item.Feature);
- if (inGroup && ale.second && entry.Target != nullptr &&
- (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
- entry.Target->GetType() ==
- cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
- const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_WARNING,
- cmStrCat(
- "The feature '", groupFeature,
- "', specified as part of a generator-expression "
- "'$",
- LG_BEGIN, groupFeature, ">', will not be applied to the ",
- (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY
- ? "OBJECT"
- : "INTERFACE"),
- " library '", entry.Item.Value, "'."),
- this->Target->GetBacktrace());
- }
- if (ale.second) {
- // current item not yet defined
- if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr &&
- (entry.Target->GetType() ==
- cmStateEnums::TargetType::OBJECT_LIBRARY ||
- entry.Target->GetType() ==
- cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
- this->CMakeInstance->IssueMessage(
- MessageType::AUTHOR_WARNING,
- cmStrCat("The feature '", itemFeature,
- "', specified as part of a generator-expression "
- "'$<LINK_LIBRARY:",
- itemFeature, ">', will not be applied to the ",
- (entry.Target->GetType() ==
- cmStateEnums::TargetType::OBJECT_LIBRARY
- ? "OBJECT"
- : "INTERFACE"),
- " library '", entry.Item.Value, "'."),
- this->Target->GetBacktrace());
- }
- entry.Feature = itemFeature;
- }
- bool supportedItem = entry.Target == nullptr ||
- (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
- entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
- if (supportedItem) {
- if (inGroup) {
- const auto& currentFeature = this->EntryList[groupIndex.first].Feature;
- for (const auto& g : this->GroupItems) {
- const auto& groupFeature = this->EntryList[g.first].Feature;
- if (groupFeature == currentFeature) {
- continue;
- }
- if (std::find(g.second.cbegin(), g.second.cend(), dependee_index) !=
- g.second.cend()) {
- this->CMakeInstance->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Impossible to link target '", this->Target->GetName(),
- "' because the link item '", entry.Item.Value,
- "', specified with the group feature '", currentFeature,
- '\'', ", has already occurred with the feature '",
- groupFeature, '\'', ", which is not allowed."),
- this->Target->GetBacktrace());
- continue;
- }
- }
- }
- if (entry.Feature != itemFeature) {
- // incompatibles features occurred
- this->CMakeInstance->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Impossible to link target '", this->Target->GetName(),
- "' because the link item '", entry.Item.Value,
- "', specified ",
- (itemFeature == LinkEntry::DEFAULT
- ? "without any feature or 'DEFAULT' feature"
- : cmStrCat("with the feature '", itemFeature, '\'')),
- ", has already occurred ",
- (entry.Feature == LinkEntry::DEFAULT
- ? "without any feature or 'DEFAULT' feature"
- : cmStrCat("with the feature '", entry.Feature, '\'')),
- ", which is not allowed."),
- this->Target->GetBacktrace());
- }
- }
- if (inGroup) {
- // store item index for dependencies handling
- groupItems.push_back(dependee_index);
- } else {
- std::vector<size_t> indexes;
- bool entryHandled = false;
- // search any occurrence of the library in already defined groups
- for (const auto& group : this->GroupItems) {
- for (auto index : group.second) {
- if (entry.Item.Value == this->EntryList[index].Item.Value) {
- indexes.push_back(group.first);
- entryHandled = true;
- break;
- }
- }
- }
- if (!entryHandled) {
- indexes.push_back(dependee_index);
- }
- for (auto index : indexes) {
- // The dependee must come after the depender.
- if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
- this->EntryConstraintGraph[depender_index].emplace_back(
- index, false, false, cmListFileBacktrace());
- } else {
- // This is a direct dependency of the target being linked.
- this->OriginalEntries.push_back(index);
- }
- // Update the inferred dependencies for earlier items.
- for (auto& dependSet : dependSets) {
- // Add this item to the inferred dependencies of other items.
- // Target items are never inferred dependees because unknown
- // items are outside libraries that should not be depending on
- // targets.
- if (!this->EntryList[index].Target &&
- this->EntryList[index].Kind != LinkEntry::Flag &&
- this->EntryList[index].Kind != LinkEntry::Group &&
- dependee_index != dependSet.first) {
- dependSet.second.insert(index);
- }
- }
- // If this item needs to have dependencies inferred, do so.
- if (this->InferredDependSets[index].Initialized) {
- // Make sure an entry exists to hold the set for the item.
- dependSets[index];
- }
- }
- }
- }
- // Store the inferred dependency sets discovered for this list.
- for (auto const& dependSet : dependSets) {
- this->InferredDependSets[dependSet.first].push_back(dependSet.second);
- }
- }
- void cmComputeLinkDepends::AddLinkObjects(std::vector<cmLinkItem> const& objs)
- {
- for (cmLinkItem const& obj : objs) {
- this->AddLinkObject(obj);
- }
- }
- cmLinkItem cmComputeLinkDepends::ResolveLinkItem(size_t depender_index,
- const std::string& name)
- {
- // Look for a target in the scope of the depender.
- cmGeneratorTarget const* from = this->Target;
- if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
- if (cmGeneratorTarget const* depender =
- this->EntryList[depender_index].Target) {
- from = depender;
- }
- }
- return from->ResolveLinkItem(BT<std::string>(name));
- }
- void cmComputeLinkDepends::InferDependencies()
- {
- // The inferred dependency sets for each item list the possible
- // dependencies. The intersection of the sets for one item form its
- // inferred dependencies.
- for (size_t depender_index = 0;
- depender_index < this->InferredDependSets.size(); ++depender_index) {
- // Skip items for which dependencies do not need to be inferred or
- // for which the inferred dependency sets are empty.
- DependSetList& sets = this->InferredDependSets[depender_index];
- if (!sets.Initialized || sets.empty()) {
- continue;
- }
- // Intersect the sets for this item.
- DependSet common = sets.front();
- for (DependSet const& i : cmMakeRange(sets).advance(1)) {
- DependSet intersection;
- std::set_intersection(common.begin(), common.end(), i.begin(), i.end(),
- std::inserter(intersection, intersection.begin()));
- common = intersection;
- }
- // Add the inferred dependencies to the graph.
- cmGraphEdgeList& edges = this->EntryConstraintGraph[depender_index];
- edges.reserve(edges.size() + common.size());
- for (auto const& c : common) {
- edges.emplace_back(c, true, false, cmListFileBacktrace());
- }
- }
- }
- void cmComputeLinkDepends::UpdateGroupDependencies()
- {
- if (this->GroupItems.empty()) {
- return;
- }
- // Walks through all entries of the constraint graph to replace dependencies
- // over raw items by the group it belongs to, if any.
- for (auto& edgeList : this->EntryConstraintGraph) {
- for (auto& edge : edgeList) {
- size_t index = edge;
- if (this->EntryList[index].Kind == LinkEntry::Group ||
- this->EntryList[index].Kind == LinkEntry::Flag ||
- this->EntryList[index].Kind == LinkEntry::Object) {
- continue;
- }
- // search the item in the defined groups
- for (const auto& groupItems : this->GroupItems) {
- auto pos = std::find(groupItems.second.cbegin(),
- groupItems.second.cend(), index);
- if (pos != groupItems.second.cend()) {
- // replace lib dependency by the group it belongs to
- edge = cmGraphEdge{ groupItems.first, false, false,
- cmListFileBacktrace() };
- }
- }
- }
- }
- }
- void cmComputeLinkDepends::CleanConstraintGraph()
- {
- for (cmGraphEdgeList& edgeList : this->EntryConstraintGraph) {
- // Sort the outgoing edges for each graph node so that the
- // original order will be preserved as much as possible.
- std::sort(edgeList.begin(), edgeList.end());
- // Make the edge list unique.
- edgeList.erase(std::unique(edgeList.begin(), edgeList.end()),
- edgeList.end());
- }
- }
- bool cmComputeLinkDepends::CheckCircularDependencies() const
- {
- std::vector<NodeList> const& components = this->CCG->GetComponents();
- size_t nc = components.size();
- for (size_t c = 0; c < nc; ++c) {
- // Get the current component.
- NodeList const& nl = components[c];
- // Skip trivial components.
- if (nl.size() < 2) {
- continue;
- }
- // no group must be evolved
- bool cycleDetected = false;
- for (size_t ni : nl) {
- if (this->EntryList[ni].Kind == LinkEntry::Group) {
- cycleDetected = true;
- break;
- }
- }
- if (!cycleDetected) {
- continue;
- }
- // Construct the error message.
- auto formatItem = [](LinkEntry const& entry) -> std::string {
- if (entry.Kind == LinkEntry::Group) {
- auto items =
- entry.Item.Value.substr(entry.Item.Value.find(':', 12) + 1);
- items.pop_back();
- std::replace(items.begin(), items.end(), '|', ',');
- return cmStrCat("group \"", ExtractGroupFeature(entry.Item.Value),
- ":{", items, "}\"");
- }
- return cmStrCat('"', entry.Item.Value, '"');
- };
- std::ostringstream e;
- e << "The inter-target dependency graph, for the target \""
- << this->Target->GetName()
- << "\", contains the following strongly connected component "
- "(cycle):\n";
- std::vector<size_t> const& cmap = this->CCG->GetComponentMap();
- for (size_t i : nl) {
- // Get the depender.
- LinkEntry const& depender = this->EntryList[i];
- // Describe the depender.
- e << " " << formatItem(depender) << "\n";
- // List its dependencies that are inside the component.
- EdgeList const& el = this->EntryConstraintGraph[i];
- for (cmGraphEdge const& ni : el) {
- size_t j = ni;
- if (cmap[j] == c) {
- LinkEntry const& dependee = this->EntryList[j];
- e << " depends on " << formatItem(dependee) << "\n";
- }
- }
- }
- this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
- this->Target->GetBacktrace());
- return false;
- }
- return true;
- }
- void cmComputeLinkDepends::DisplayConstraintGraph()
- {
- // Display the graph nodes and their edges.
- std::ostringstream e;
- for (size_t i = 0; i < this->EntryConstraintGraph.size(); ++i) {
- EdgeList const& nl = this->EntryConstraintGraph[i];
- e << "item " << i << " is [" << this->EntryList[i].Item << "]\n";
- e << cmWrap(" item ", nl, " must follow it", "\n") << "\n";
- }
- fprintf(stderr, "%s\n", e.str().c_str());
- }
- void cmComputeLinkDepends::OrderLinkEntries()
- {
- // The component graph is guaranteed to be acyclic. Start a DFS
- // from every entry to compute a topological order for the
- // components.
- Graph const& cgraph = this->CCG->GetComponentGraph();
- size_t n = cgraph.size();
- this->ComponentVisited.resize(cgraph.size(), 0);
- this->ComponentOrder.resize(cgraph.size(), n);
- this->ComponentOrderId = n;
- // Run in reverse order so the topological order will preserve the
- // original order where there are no constraints.
- for (size_t c = n - 1; c != cmComputeComponentGraph::INVALID_COMPONENT;
- --c) {
- this->VisitComponent(c);
- }
- // Display the component graph.
- if (this->DebugMode) {
- this->DisplayComponents();
- }
- // Start with the original link line.
- for (size_t originalEntry : this->OriginalEntries) {
- this->VisitEntry(originalEntry);
- }
- // Now explore anything left pending. Since the component graph is
- // guaranteed to be acyclic we know this will terminate.
- while (!this->PendingComponents.empty()) {
- // Visit one entry from the first pending component. The visit
- // logic will update the pending components accordingly. Since
- // the pending components are kept in topological order this will
- // not repeat one.
- size_t e = *this->PendingComponents.begin()->second.Entries.begin();
- this->VisitEntry(e);
- }
- }
- void cmComputeLinkDepends::DisplayComponents()
- {
- fprintf(stderr, "The strongly connected components are:\n");
- std::vector<NodeList> const& components = this->CCG->GetComponents();
- for (size_t c = 0; c < components.size(); ++c) {
- fprintf(stderr, "Component (%zu):\n", c);
- NodeList const& nl = components[c];
- for (size_t i : nl) {
- fprintf(stderr, " item %zu [%s]\n", i,
- this->EntryList[i].Item.Value.c_str());
- }
- EdgeList const& ol = this->CCG->GetComponentGraphEdges(c);
- for (cmGraphEdge const& oi : ol) {
- size_t i = oi;
- fprintf(stderr, " followed by Component (%zu)\n", i);
- }
- fprintf(stderr, " topo order index %zu\n", this->ComponentOrder[c]);
- }
- fprintf(stderr, "\n");
- }
- void cmComputeLinkDepends::VisitComponent(size_t c)
- {
- // Check if the node has already been visited.
- if (this->ComponentVisited[c]) {
- return;
- }
- // We are now visiting this component so mark it.
- this->ComponentVisited[c] = 1;
- // Visit the neighbors of the component first.
- // Run in reverse order so the topological order will preserve the
- // original order where there are no constraints.
- EdgeList const& nl = this->CCG->GetComponentGraphEdges(c);
- for (cmGraphEdge const& edge : cmReverseRange(nl)) {
- this->VisitComponent(edge);
- }
- // Assign an ordering id to this component.
- this->ComponentOrder[c] = --this->ComponentOrderId;
- }
- void cmComputeLinkDepends::VisitEntry(size_t index)
- {
- // Include this entry on the link line.
- this->FinalLinkOrder.push_back(index);
- // This entry has now been seen. Update its component.
- bool completed = false;
- size_t component = this->CCG->GetComponentMap()[index];
- auto mi = this->PendingComponents.find(this->ComponentOrder[component]);
- if (mi != this->PendingComponents.end()) {
- // The entry is in an already pending component.
- PendingComponent& pc = mi->second;
- // Remove the entry from those pending in its component.
- pc.Entries.erase(index);
- if (pc.Entries.empty()) {
- // The complete component has been seen since it was last needed.
- --pc.Count;
- if (pc.Count == 0) {
- // The component has been completed.
- this->PendingComponents.erase(mi);
- completed = true;
- } else {
- // The whole component needs to be seen again.
- NodeList const& nl = this->CCG->GetComponent(component);
- assert(nl.size() > 1);
- pc.Entries.insert(nl.begin(), nl.end());
- }
- }
- } else {
- // The entry is not in an already pending component.
- NodeList const& nl = this->CCG->GetComponent(component);
- if (nl.size() > 1) {
- // This is a non-trivial component. It is now pending.
- PendingComponent& pc = this->MakePendingComponent(component);
- // The starting entry has already been seen.
- pc.Entries.erase(index);
- } else {
- // This is a trivial component, so it is already complete.
- completed = true;
- }
- }
- // If the entry completed a component, the component's dependencies
- // are now pending.
- if (completed) {
- EdgeList const& ol = this->CCG->GetComponentGraphEdges(component);
- for (cmGraphEdge const& oi : ol) {
- // This entire component is now pending no matter whether it has
- // been partially seen already.
- this->MakePendingComponent(oi);
- }
- }
- }
- cmComputeLinkDepends::PendingComponent&
- cmComputeLinkDepends::MakePendingComponent(size_t component)
- {
- // Create an entry (in topological order) for the component.
- PendingComponent& pc =
- this->PendingComponents[this->ComponentOrder[component]];
- pc.Id = component;
- NodeList const& nl = this->CCG->GetComponent(component);
- if (nl.size() == 1) {
- // Trivial components need be seen only once.
- pc.Count = 1;
- } else {
- // This is a non-trivial strongly connected component of the
- // original graph. It consists of two or more libraries
- // (archives) that mutually require objects from one another. In
- // the worst case we may have to repeat the list of libraries as
- // many times as there are object files in the biggest archive.
- // For now we just list them twice.
- //
- // The list of items in the component has been sorted by the order
- // of discovery in the original BFS of dependencies. This has the
- // advantage that the item directly linked by a target requiring
- // this component will come first which minimizes the number of
- // repeats needed.
- pc.Count = this->ComputeComponentCount(nl);
- }
- // Store the entries to be seen.
- pc.Entries.insert(nl.begin(), nl.end());
- return pc;
- }
- size_t cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
- {
- size_t count = 2;
- for (size_t ni : nl) {
- if (cmGeneratorTarget const* target = this->EntryList[ni].Target) {
- if (cmLinkInterface const* iface =
- target->GetLinkInterface(this->Config, this->Target)) {
- if (iface->Multiplicity > count) {
- count = iface->Multiplicity;
- }
- }
- }
- }
- return count;
- }
- void cmComputeLinkDepends::DisplayFinalEntries()
- {
- fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
- char space[] = " ";
- int count = 2;
- for (LinkEntry const& lei : this->FinalLinkEntries) {
- if (lei.Kind == LinkEntry::Group) {
- fprintf(stderr, " %s group",
- lei.Item.Value == "<LINK_GROUP>" ? "start" : "end");
- count = lei.Item.Value == "<LINK_GROUP>" ? 4 : 2;
- } else if (lei.Target) {
- fprintf(stderr, "%*starget [%s]", count, space,
- lei.Target->GetName().c_str());
- } else {
- fprintf(stderr, "%*sitem [%s]", count, space, lei.Item.Value.c_str());
- }
- if (lei.Feature != LinkEntry::DEFAULT) {
- fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
- }
- fprintf(stderr, "\n");
- }
- fprintf(stderr, "\n");
- }
- void cmComputeLinkDepends::CheckWrongConfigItem(cmLinkItem const& item)
- {
- if (!this->OldLinkDirMode) {
- return;
- }
- // For CMake 2.4 bug-compatibility we need to consider the output
- // directories of targets linked in another configuration as link
- // directories.
- if (item.Target && !item.Target->IsImported()) {
- this->OldWrongConfigItems.insert(item.Target);
- }
- }
|