Преглед изворни кода

Merge topic 'link-strategy'

7abd3137b7 Linking: Optionally reorder direct dependencies from LINK_LIBRARIES
9285a9dc9a cmComputeLinkDepends: Add final dependency ordering to debug output
f792db4ca2 cmComputeLinkDepends: Add undocumented per-target debug property
80b469a51d cmComputeLinkDepends: Factor out string literals as named constants
3bd73fcc76 cmComputeLinkDepends: Modernize member initialization
8db69c767b cmComputeLinkDepends: Remove redundant member
dccdd030cd cmComputeLinkDepends: Replace depender index sentinel value with cm::optional
6c9d8dc243 cmComputeLinkDepends: Replace group index sentinel value with cm::optional
...

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !9835
Brad King пре 1 година
родитељ
комит
53f3e5da1e
39 измењених фајлова са 465 додато и 135 уклоњено
  1. 5 0
      Help/command/target_link_libraries.rst
  2. 1 0
      Help/manual/cmake-properties.7.rst
  3. 1 0
      Help/manual/cmake-variables.7.rst
  4. 5 0
      Help/prop_tgt/LINK_LIBRARIES.rst
  5. 11 0
      Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst
  6. 7 0
      Help/release/dev/link-strategy.rst
  7. 68 0
      Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst
  8. 132 110
      Source/cmComputeLinkDepends.cxx
  9. 30 17
      Source/cmComputeLinkDepends.h
  10. 19 1
      Source/cmComputeLinkInformation.cxx
  11. 8 7
      Source/cmMakefileTargetGenerator.cxx
  12. 1 0
      Source/cmTarget.cxx
  13. 1 0
      Tests/RunCMake/CMakeLists.txt
  14. 1 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt
  15. 1 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt
  16. 7 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt
  17. 2 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake
  18. 1 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt
  19. 6 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt
  20. 2 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake
  21. 15 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake
  22. 11 0
      Tests/RunCMake/LinkLibrariesStrategy/Basic.c
  23. 4 0
      Tests/RunCMake/LinkLibrariesStrategy/BasicA.c
  24. 5 0
      Tests/RunCMake/LinkLibrariesStrategy/BasicB.c
  25. 5 0
      Tests/RunCMake/LinkLibrariesStrategy/BasicC.c
  26. 7 0
      Tests/RunCMake/LinkLibrariesStrategy/BasicX.c
  27. 3 0
      Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt
  28. 15 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt
  29. 2 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake
  30. 9 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt
  31. 2 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake
  32. 12 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake
  33. 4 0
      Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c
  34. 12 0
      Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake
  35. 36 0
      Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake
  36. 1 0
      Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt
  37. 4 0
      Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt
  38. 5 0
      Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake
  39. 4 0
      Tests/RunCMake/LinkLibrariesStrategy/main.c

+ 5 - 0
Help/command/target_link_libraries.rst

@@ -140,6 +140,11 @@ Items containing ``::``, such as ``Foo::Bar``, are assumed to be
 target names and will cause an error if no such target exists.
 target names and will cause an error if no such target exists.
 See policy :policy:`CMP0028`.
 See policy :policy:`CMP0028`.
 
 
+See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and
+corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property
+for details on how CMake orders direct link dependencies on linker
+command lines.
+
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 buildsystem properties.
 buildsystem properties.
 
 

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -336,6 +336,7 @@ Properties on Targets
    /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG
    /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG
    /prop_tgt/LINK_LIBRARIES
    /prop_tgt/LINK_LIBRARIES
    /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS
    /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS
+   /prop_tgt/LINK_LIBRARIES_STRATEGY
    /prop_tgt/LINK_LIBRARY_OVERRIDE
    /prop_tgt/LINK_LIBRARY_OVERRIDE
    /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY
    /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY
    /prop_tgt/LINK_OPTIONS
    /prop_tgt/LINK_OPTIONS

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -491,6 +491,7 @@ Variables that Control the Build
    /variable/CMAKE_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
+   /variable/CMAKE_LINK_LIBRARIES_STRATEGY
    /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES
    /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG

+ 5 - 0
Help/prop_tgt/LINK_LIBRARIES.rst

@@ -28,3 +28,8 @@ In advanced use cases, the list of direct link dependencies specified
 by this property may be updated by usage requirements from dependencies.
 by this property may be updated by usage requirements from dependencies.
 See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and
 See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and
 :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties.
 :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties.
+
+See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and
+corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property
+for details on how CMake orders direct link dependencies on linker
+command lines.

+ 11 - 0
Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst

@@ -0,0 +1,11 @@
+LINK_LIBRARIES_STRATEGY
+-----------------------
+
+.. versionadded:: 3.31
+
+Specify a strategy for ordering a target's direct link dependencies
+on linker command lines.
+
+See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable for details
+and supported values.  This property is initialized by the value of that
+variable when a target is created.

+ 7 - 0
Help/release/dev/link-strategy.rst

@@ -0,0 +1,7 @@
+link-strategy
+-------------
+
+* The :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and
+  corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target
+  property were added to optionally specify the strategy
+  CMake uses to generate link lines.

+ 68 - 0
Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst

@@ -0,0 +1,68 @@
+CMAKE_LINK_LIBRARIES_STRATEGY
+-----------------------------
+
+.. versionadded:: 3.31
+
+Specify a strategy for ordering targets' direct link dependencies
+on linker command lines.
+
+The value of this variable initializes the :prop_tgt:`LINK_LIBRARIES_STRATEGY`
+target property of targets as they are created.  Set that property directly
+to specify a strategy for a single target.
+
+CMake generates a target's link line using its :ref:`Target Link Properties`.
+In particular, the :prop_tgt:`LINK_LIBRARIES` target property records the
+target's direct link dependencies, typically populated by calls to
+:command:`target_link_libraries`.  Indirect link dependencies are
+propagated from those entries of :prop_tgt:`LINK_LIBRARIES` that name
+library targets by following the transitive closure of their
+:prop_tgt:`INTERFACE_LINK_LIBRARIES` properties.  CMake supports multiple
+strategies for passing direct and indirect link dependencies to the linker.
+
+Consider this example for the strategies below:
+
+.. code-block:: cmake
+
+  add_library(A STATIC ...)
+  add_library(B STATIC ...)
+  add_library(C STATIC ...)
+  add_executable(main ...)
+  target_link_libraries(B PRIVATE A)
+  target_link_libraries(C PRIVATE A)
+  target_link_libraries(main PRIVATE A B C)
+
+The supported strategies are:
+
+``PRESERVE_ORDER``
+  Entries of :prop_tgt:`LINK_LIBRARIES` always appear first and in their
+  original order.  Indirect link dependencies not satisfied by the
+  original entries may be reordered and de-duplicated with respect to
+  one another, but are always appended after the original entries.
+  This may result in less efficient link lines, but gives projects
+  control of ordering among independent entries.  Such control may be
+  important when intermixing link flags with libraries, or when multiple
+  libraries provide a given symbol.
+
+  This is the default.
+
+  In the above example, this strategy computes a link line for ``main``
+  by starting with its original entries ``A B C``, and then appends
+  another ``A`` to satisfy the dependencies of ``B`` and ``C`` on ``A``.
+  The final order is ``A B C A``.
+
+``REORDER``
+  Entries of :prop_tgt:`LINK_LIBRARIES` may be reordered, de-duplicated,
+  and intermixed with indirect link dependencies.  This may result in
+  more efficient link lines, but does not give projects any control of
+  ordering among independent entries.
+
+  In the above example, this strategy computes a link line for ``main``
+  by re-ordering its original entries ``A B C`` to satisfy the
+  dependencies of ``B`` and ``C`` on ``A``.
+  The final order is ``B C A``.
+
+.. note::
+
+  Regardless of the strategy used, the actual linker invocation for
+  some platforms may de-duplicate entries based on linker capabilities.
+  See policy :policy:`CMP0156`.

+ 132 - 110
Source/cmComputeLinkDepends.cxx

@@ -315,8 +315,10 @@ const LinkLibraryFeatureAttributeSet& GetLinkLibraryFeatureAttributes(
 }
 }
 
 
 // LINK_GROUP helpers
 // LINK_GROUP helpers
-const auto LG_BEGIN = "<LINK_GROUP:"_s;
-const auto LG_END = "</LINK_GROUP:"_s;
+const cm::string_view LG_BEGIN = "<LINK_GROUP:"_s;
+const cm::string_view LG_END = "</LINK_GROUP:"_s;
+const cm::string_view LG_ITEM_BEGIN = "<LINK_GROUP>"_s;
+const cm::string_view LG_ITEM_END = "</LINK_GROUP>"_s;
 
 
 inline std::string ExtractGroupFeature(std::string const& item)
 inline std::string ExtractGroupFeature(std::string const& item)
 {
 {
@@ -431,8 +433,8 @@ public:
       this->Groups = &groups;
       this->Groups = &groups;
       // record all libraries as part of groups to ensure correct
       // record all libraries as part of groups to ensure correct
       // deduplication: libraries as part of groups are always kept.
       // deduplication: libraries as part of groups are always kept.
-      for (const auto& group : groups) {
-        for (auto index : group.second) {
+      for (const auto& g : groups) {
+        for (auto index : g.second) {
           this->Emitted.insert(index);
           this->Emitted.insert(index);
         }
         }
       }
       }
@@ -477,8 +479,8 @@ public:
 
 
     // expand groups
     // expand groups
     if (this->Groups) {
     if (this->Groups) {
-      for (const auto& group : *this->Groups) {
-        const LinkEntry& groupEntry = this->Entries[group.first];
+      for (const auto& g : *this->Groups) {
+        const LinkEntry& groupEntry = this->Entries[g.first];
         auto it = this->FinalEntries.begin();
         auto it = this->FinalEntries.begin();
         while (true) {
         while (true) {
           it = std::find_if(it, this->FinalEntries.end(),
           it = std::find_if(it, this->FinalEntries.end(),
@@ -488,13 +490,13 @@ public:
           if (it == this->FinalEntries.end()) {
           if (it == this->FinalEntries.end()) {
             break;
             break;
           }
           }
-          it->Item.Value = "</LINK_GROUP>";
-          for (auto index = group.second.rbegin();
-               index != group.second.rend(); ++index) {
+          it->Item.Value = std::string(LG_ITEM_END);
+          for (auto index = g.second.rbegin(); index != g.second.rend();
+               ++index) {
             it = this->FinalEntries.insert(it, this->Entries[*index]);
             it = this->FinalEntries.insert(it, this->Entries[*index]);
           }
           }
           it = this->FinalEntries.insert(it, groupEntry);
           it = this->FinalEntries.insert(it, groupEntry);
-          it->Item.Value = "<LINK_GROUP>";
+          it->Item.Value = std::string(LG_ITEM_BEGIN);
         }
         }
       }
       }
     }
     }
@@ -579,16 +581,21 @@ std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT =
 
 
 cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
 cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
                                            const std::string& config,
                                            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;
+                                           const std::string& linkLanguage,
+                                           LinkLibrariesStrategy strategy)
+  : Target(target)
+  , Makefile(this->Target->Target->GetMakefile())
+  , GlobalGenerator(this->Target->GetLocalGenerator()->GetGlobalGenerator())
+  , CMakeInstance(this->GlobalGenerator->GetCMakeInstance())
+  , Config(config)
+  , DebugMode(this->Makefile->IsOn("CMAKE_LINK_DEPENDS_DEBUG_MODE") ||
+              this->Target->GetProperty("LINK_DEPENDS_DEBUG_MODE").IsOn())
+  , LinkLanguage(linkLanguage)
+  , LinkType(CMP0003_ComputeLinkType(
+      this->Config, this->Makefile->GetCMakeInstance()->GetDebugConfigs()))
+  , Strategy(strategy)
 
 
+{
   // target oriented feature override property takes precedence over
   // target oriented feature override property takes precedence over
   // global override property
   // global override property
   cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
   cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
@@ -640,22 +647,6 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
                });
                });
     }
     }
   }
   }
-
-  // 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;
 cmComputeLinkDepends::~cmComputeLinkDepends() = default;
@@ -706,7 +697,7 @@ cmComputeLinkDepends::Compute()
             "---------------------------------------\n");
             "---------------------------------------\n");
     fprintf(stderr, "Link dependency analysis for target %s, config %s\n",
     fprintf(stderr, "Link dependency analysis for target %s, config %s\n",
             this->Target->GetName().c_str(),
             this->Target->GetName().c_str(),
-            this->HasConfig ? this->Config.c_str() : "noconfig");
+            this->Config.empty() ? "noconfig" : this->Config.c_str());
     this->DisplayConstraintGraph();
     this->DisplayConstraintGraph();
   }
   }
 
 
@@ -726,6 +717,11 @@ cmComputeLinkDepends::Compute()
   // Compute the final ordering.
   // Compute the final ordering.
   this->OrderLinkEntries();
   this->OrderLinkEntries();
 
 
+  // Display the final ordering.
+  if (this->DebugMode) {
+    this->DisplayOrderedEntries();
+  }
+
   // Compute the final set of link entries.
   // Compute the final set of link entries.
   EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
   EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
                                        this->EntryList,
                                        this->EntryList,
@@ -766,7 +762,7 @@ cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
 }
 }
 
 
 std::pair<size_t, bool> cmComputeLinkDepends::AddLinkEntry(
 std::pair<size_t, bool> cmComputeLinkDepends::AddLinkEntry(
-  cmLinkItem const& item, size_t groupIndex)
+  cmLinkItem const& item, cm::optional<size_t> const& groupIndex)
 {
 {
   // Allocate a spot for the item entry.
   // Allocate a spot for the item entry.
   auto lei = this->AllocateLinkEntry(item);
   auto lei = this->AllocateLinkEntry(item);
@@ -842,10 +838,7 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
 void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
 void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
 {
 {
   // Get this entry representation.
   // Get this entry representation.
-  size_t depender_index =
-    qe.GroupIndex == cmComputeComponentGraph::INVALID_COMPONENT
-    ? qe.Index
-    : qe.GroupIndex;
+  size_t depender_index = qe.GroupIndex ? *qe.GroupIndex : qe.Index;
   LinkEntry const& entry = this->EntryList[qe.Index];
   LinkEntry const& entry = this->EntryList[qe.Index];
 
 
   // Follow the item's dependencies.
   // Follow the item's dependencies.
@@ -944,8 +937,8 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
   }
   }
 }
 }
 
 
-void cmComputeLinkDepends::AddVarLinkEntries(size_t depender_index,
-                                             const char* value)
+void cmComputeLinkDepends::AddVarLinkEntries(
+  cm::optional<size_t> const& depender_index, const char* value)
 {
 {
   // This is called to add the dependencies named by
   // This is called to add the dependencies named by
   // <item>_LIB_DEPENDS.  The variable contains a semicolon-separated
   // <item>_LIB_DEPENDS.  The variable contains a semicolon-separated
@@ -1006,15 +999,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
   // Add direct link dependencies in this configuration.
   // Add direct link dependencies in this configuration.
   cmLinkImplementation const* impl = this->Target->GetLinkImplementation(
   cmLinkImplementation const* impl = this->Target->GetLinkImplementation(
     this->Config, cmGeneratorTarget::UseTo::Link);
     this->Config, cmGeneratorTarget::UseTo::Link);
-  this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT,
-                       impl->Libraries);
+  this->AddLinkEntries(cm::nullopt, impl->Libraries);
   this->AddLinkObjects(impl->Objects);
   this->AddLinkObjects(impl->Objects);
 
 
   for (auto const& language : impl->Languages) {
   for (auto const& language : impl->Languages) {
     auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language);
     auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language);
     if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) {
     if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) {
-      this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT,
-                           runtimeEntries->second);
+      this->AddLinkEntries(cm::nullopt, runtimeEntries->second);
     }
     }
   }
   }
   for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
   for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
@@ -1023,16 +1014,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
 }
 }
 
 
 template <typename T>
 template <typename T>
-void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
-                                          std::vector<T> const& libs)
+void cmComputeLinkDepends::AddLinkEntries(
+  cm::optional<size_t> const& depender_index, std::vector<T> const& libs)
 {
 {
   // Track inferred dependency sets implied by this list.
   // Track inferred dependency sets implied by this list.
   std::map<size_t, DependSet> dependSets;
   std::map<size_t, DependSet> dependSets;
 
 
-  bool inGroup = false;
-  std::pair<size_t, bool> groupIndex{
-    cmComputeComponentGraph::INVALID_COMPONENT, false
-  };
+  cm::optional<std::pair<size_t, bool>> group;
   std::vector<size_t> groupItems;
   std::vector<size_t> groupItems;
 
 
   // Loop over the libraries linked directly by the depender.
   // Loop over the libraries linked directly by the depender.
@@ -1046,9 +1034,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
 
 
     // emit a warning if an undefined feature is used as part of
     // emit a warning if an undefined feature is used as part of
     // an imported target
     // an imported target
-    if (item.Feature != LinkEntry::DEFAULT &&
-        depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
-      const auto& depender = this->EntryList[depender_index];
+    if (item.Feature != LinkEntry::DEFAULT && depender_index) {
+      const auto& depender = this->EntryList[*depender_index];
       if (depender.Target && depender.Target->IsImported() &&
       if (depender.Target && depender.Target->IsImported() &&
           !IsFeatureSupported(this->Makefile, this->LinkLanguage,
           !IsFeatureSupported(this->Makefile, this->LinkLanguage,
                               item.Feature)) {
                               item.Feature)) {
@@ -1069,18 +1056,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
 
 
     if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
     if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
         cmHasSuffix(item.AsStr(), '>')) {
         cmHasSuffix(item.AsStr(), '>')) {
-      groupIndex = this->AddLinkEntry(item);
-      if (groupIndex.second) {
-        LinkEntry& entry = this->EntryList[groupIndex.first];
+      group = this->AddLinkEntry(item, cm::nullopt);
+      if (group->second) {
+        LinkEntry& entry = this->EntryList[group->first];
         entry.Feature = ExtractGroupFeature(item.AsStr());
         entry.Feature = ExtractGroupFeature(item.AsStr());
       }
       }
-      inGroup = true;
-      if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
-        this->EntryConstraintGraph[depender_index].emplace_back(
-          groupIndex.first, false, false, cmListFileBacktrace());
+      if (depender_index) {
+        this->EntryConstraintGraph[*depender_index].emplace_back(
+          group->first, false, false, cmListFileBacktrace());
       } else {
       } else {
         // This is a direct dependency of the target being linked.
         // This is a direct dependency of the target being linked.
-        this->OriginalEntries.push_back(groupIndex.first);
+        this->OriginalEntries.push_back(group->first);
       }
       }
       continue;
       continue;
     }
     }
@@ -1088,20 +1074,19 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     size_t dependee_index;
     size_t dependee_index;
 
 
     if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) {
     if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) {
-      dependee_index = groupIndex.first;
-      if (groupIndex.second) {
-        this->GroupItems.emplace(groupIndex.first, groupItems);
+      assert(group);
+      dependee_index = group->first;
+      if (group->second) {
+        this->GroupItems.emplace(group->first, std::move(groupItems));
       }
       }
-      inGroup = false;
-      groupIndex = std::make_pair(-1, false);
+      group = cm::nullopt;
       groupItems.clear();
       groupItems.clear();
       continue;
       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_index && group) {
+      const auto& depender = this->EntryList[*depender_index];
+      const auto& groupFeature = this->EntryList[group->first].Feature;
       if (depender.Target && depender.Target->IsImported() &&
       if (depender.Target && depender.Target->IsImported() &&
           !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
           !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
                                    groupFeature)) {
                                    groupFeature)) {
@@ -1121,18 +1106,19 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     }
     }
 
 
     // Add a link entry for this item.
     // Add a link entry for this item.
-    auto ale = this->AddLinkEntry(item, groupIndex.first);
+    auto ale = this->AddLinkEntry(
+      item, group ? cm::optional<size_t>(group->first) : cm::nullopt);
     dependee_index = ale.first;
     dependee_index = ale.first;
     LinkEntry& entry = this->EntryList[dependee_index];
     LinkEntry& entry = this->EntryList[dependee_index];
     bool supportedItem = true;
     bool supportedItem = true;
     auto const& itemFeature =
     auto const& itemFeature =
       this->GetCurrentFeature(entry.Item.Value, item.Feature);
       this->GetCurrentFeature(entry.Item.Value, item.Feature);
-    if (inGroup && ale.second && entry.Target &&
+    if (group && ale.second && entry.Target &&
         (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
         (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
          entry.Target->GetType() ==
          entry.Target->GetType() ==
            cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
            cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
       supportedItem = false;
       supportedItem = false;
-      const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
+      const auto& groupFeature = this->EntryList[group->first].Feature;
       this->CMakeInstance->IssueMessage(
       this->CMakeInstance->IssueMessage(
         MessageType::AUTHOR_WARNING,
         MessageType::AUTHOR_WARNING,
         cmStrCat(
         cmStrCat(
@@ -1173,8 +1159,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     }
     }
 
 
     if (supportedItem) {
     if (supportedItem) {
-      if (inGroup) {
-        const auto& currentFeature = this->EntryList[groupIndex.first].Feature;
+      if (group) {
+        const auto& currentFeature = this->EntryList[group->first].Feature;
         for (const auto& g : this->GroupItems) {
         for (const auto& g : this->GroupItems) {
           const auto& groupFeature = this->EntryList[g.first].Feature;
           const auto& groupFeature = this->EntryList[g.first].Feature;
           if (groupFeature == currentFeature) {
           if (groupFeature == currentFeature) {
@@ -1245,17 +1231,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
       }
       }
     }
     }
 
 
-    if (inGroup) {
+    if (group) {
       // store item index for dependencies handling
       // store item index for dependencies handling
       groupItems.push_back(dependee_index);
       groupItems.push_back(dependee_index);
     } else {
     } else {
       std::vector<size_t> indexes;
       std::vector<size_t> indexes;
       bool entryHandled = false;
       bool entryHandled = false;
       // search any occurrence of the library in already defined groups
       // search any occurrence of the library in already defined groups
-      for (const auto& group : this->GroupItems) {
-        for (auto index : group.second) {
+      for (const auto& g : this->GroupItems) {
+        for (auto index : g.second) {
           if (entry.Item.Value == this->EntryList[index].Item.Value) {
           if (entry.Item.Value == this->EntryList[index].Item.Value) {
-            indexes.push_back(group.first);
+            indexes.push_back(g.first);
             entryHandled = true;
             entryHandled = true;
             break;
             break;
           }
           }
@@ -1267,8 +1253,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
 
 
       for (auto index : indexes) {
       for (auto index : indexes) {
         // The dependee must come after the depender.
         // The dependee must come after the depender.
-        if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
-          this->EntryConstraintGraph[depender_index].emplace_back(
+        if (depender_index) {
+          this->EntryConstraintGraph[*depender_index].emplace_back(
             index, false, false, cmListFileBacktrace());
             index, false, false, cmListFileBacktrace());
         } else {
         } else {
           // This is a direct dependency of the target being linked.
           // This is a direct dependency of the target being linked.
@@ -1311,14 +1297,14 @@ void cmComputeLinkDepends::AddLinkObjects(std::vector<cmLinkItem> const& objs)
   }
   }
 }
 }
 
 
-cmLinkItem cmComputeLinkDepends::ResolveLinkItem(size_t depender_index,
-                                                 const std::string& name)
+cmLinkItem cmComputeLinkDepends::ResolveLinkItem(
+  cm::optional<size_t> const& depender_index, const std::string& name)
 {
 {
   // Look for a target in the scope of the depender.
   // Look for a target in the scope of the depender.
   cmGeneratorTarget const* from = this->Target;
   cmGeneratorTarget const* from = this->Target;
-  if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
+  if (depender_index) {
     if (cmGeneratorTarget const* depender =
     if (cmGeneratorTarget const* depender =
-          this->EntryList[depender_index].Target) {
+          this->EntryList[*depender_index].Target) {
       from = depender;
       from = depender;
     }
     }
   }
   }
@@ -1494,9 +1480,8 @@ void cmComputeLinkDepends::OrderLinkEntries()
   this->ComponentOrderId = n;
   this->ComponentOrderId = n;
   // Run in reverse order so the topological order will preserve the
   // Run in reverse order so the topological order will preserve the
   // original order where there are no constraints.
   // original order where there are no constraints.
-  for (size_t c = n - 1; c != cmComputeComponentGraph::INVALID_COMPONENT;
-       --c) {
-    this->VisitComponent(c);
+  for (size_t c = n; c > 0; --c) {
+    this->VisitComponent(c - 1);
   }
   }
 
 
   // Display the component graph.
   // Display the component graph.
@@ -1505,8 +1490,22 @@ void cmComputeLinkDepends::OrderLinkEntries()
   }
   }
 
 
   // Start with the original link line.
   // Start with the original link line.
-  for (size_t originalEntry : this->OriginalEntries) {
-    this->VisitEntry(originalEntry);
+  switch (this->Strategy) {
+    case LinkLibrariesStrategy::PRESERVE_ORDER: {
+      // Emit the direct dependencies in their original order.
+      // This gives projects control over ordering.
+      for (size_t originalEntry : this->OriginalEntries) {
+        this->VisitEntry(originalEntry);
+      }
+    } break;
+    case LinkLibrariesStrategy::REORDER: {
+      // Schedule the direct dependencies for emission in topo order.
+      // This may produce more efficient link lines.
+      for (size_t originalEntry : this->OriginalEntries) {
+        this->MakePendingComponent(
+          this->CCG->GetComponentMap()[originalEntry]);
+      }
+    } break;
   }
   }
 
 
   // Now explore anything left pending.  Since the component graph is
   // Now explore anything left pending.  Since the component graph is
@@ -1671,26 +1670,49 @@ size_t cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
   return count;
   return count;
 }
 }
 
 
-void cmComputeLinkDepends::DisplayFinalEntries()
+namespace {
+void DisplayLinkEntry(int& count, cmComputeLinkDepends::LinkEntry const& entry)
 {
 {
-  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());
+  if (entry.Kind == cmComputeLinkDepends::LinkEntry::Group) {
+    if (entry.Item.Value == LG_ITEM_BEGIN) {
+      fprintf(stderr, "  start group");
+      count = 4;
+    } else if (entry.Item.Value == LG_ITEM_END) {
+      fprintf(stderr, "  end group");
+      count = 2;
     } else {
     } 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, "  group");
     }
     }
-    fprintf(stderr, "\n");
+  } else if (entry.Target) {
+    fprintf(stderr, "%*starget [%s]", count, "",
+            entry.Target->GetName().c_str());
+  } else {
+    fprintf(stderr, "%*sitem [%s]", count, "", entry.Item.Value.c_str());
+  }
+  if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) {
+    fprintf(stderr, ", feature [%s]", entry.Feature.c_str());
+  }
+  fprintf(stderr, "\n");
+}
+}
+
+void cmComputeLinkDepends::DisplayOrderedEntries()
+{
+  fprintf(stderr, "target [%s] link dependency ordering:\n",
+          this->Target->GetName().c_str());
+  int count = 2;
+  for (auto index : this->FinalLinkOrder) {
+    DisplayLinkEntry(count, this->EntryList[index]);
+  }
+  fprintf(stderr, "\n");
+}
+
+void cmComputeLinkDepends::DisplayFinalEntries()
+{
+  fprintf(stderr, "target [%s] link line:\n", this->Target->GetName().c_str());
+  int count = 2;
+  for (LinkEntry const& entry : this->FinalLinkEntries) {
+    DisplayLinkEntry(count, entry);
   }
   }
   fprintf(stderr, "\n");
   fprintf(stderr, "\n");
 }
 }

+ 30 - 17
Source/cmComputeLinkDepends.h

@@ -13,18 +13,26 @@
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
-#include "cmComputeComponentGraph.h"
+#include <cm/optional>
+
 #include "cmGraphAdjacencyList.h"
 #include "cmGraphAdjacencyList.h"
 #include "cmLinkItem.h"
 #include "cmLinkItem.h"
 #include "cmListFileCache.h"
 #include "cmListFileCache.h"
 #include "cmTargetLinkLibraryType.h"
 #include "cmTargetLinkLibraryType.h"
 
 
+class cmComputeComponentGraph;
 class cmGeneratorTarget;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmGlobalGenerator;
 class cmMakefile;
 class cmMakefile;
 class cmSourceFile;
 class cmSourceFile;
 class cmake;
 class cmake;
 
 
+enum class LinkLibrariesStrategy
+{
+  PRESERVE_ORDER,
+  REORDER,
+};
+
 /** \class cmComputeLinkDepends
 /** \class cmComputeLinkDepends
  * \brief Compute link dependencies for targets.
  * \brief Compute link dependencies for targets.
  */
  */
@@ -33,7 +41,8 @@ class cmComputeLinkDepends
 public:
 public:
   cmComputeLinkDepends(cmGeneratorTarget const* target,
   cmComputeLinkDepends(cmGeneratorTarget const* target,
                        const std::string& config,
                        const std::string& config,
-                       const std::string& linkLanguage);
+                       const std::string& linkLanguage,
+                       LinkLibrariesStrategy strategy);
   ~cmComputeLinkDepends();
   ~cmComputeLinkDepends();
 
 
   cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
   cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -84,12 +93,16 @@ public:
 
 
 private:
 private:
   // Context information.
   // Context information.
-  cmGeneratorTarget const* Target;
-  cmMakefile* Makefile;
-  cmGlobalGenerator const* GlobalGenerator;
+  cmGeneratorTarget const* Target = nullptr;
+  cmMakefile* Makefile = nullptr;
+  cmGlobalGenerator const* GlobalGenerator = nullptr;
   cmake* CMakeInstance;
   cmake* CMakeInstance;
-  std::string LinkLanguage;
   std::string Config;
   std::string Config;
+  bool DebugMode = false;
+  std::string LinkLanguage;
+  cmTargetLinkLibraryType LinkType;
+  LinkLibrariesStrategy Strategy;
+
   EntryVector FinalLinkEntries;
   EntryVector FinalLinkEntries;
   std::map<std::string, std::string> LinkLibraryOverride;
   std::map<std::string, std::string> LinkLibraryOverride;
 
 
@@ -98,16 +111,18 @@ private:
 
 
   std::pair<std::map<cmLinkItem, size_t>::iterator, bool> AllocateLinkEntry(
   std::pair<std::map<cmLinkItem, size_t>::iterator, bool> AllocateLinkEntry(
     cmLinkItem const& item);
     cmLinkItem const& item);
-  std::pair<size_t, bool> AddLinkEntry(
-    cmLinkItem const& item,
-    size_t groupIndex = cmComputeComponentGraph::INVALID_COMPONENT);
+  std::pair<size_t, bool> AddLinkEntry(cmLinkItem const& item,
+                                       cm::optional<size_t> const& groupIndex);
   void AddLinkObject(cmLinkItem const& item);
   void AddLinkObject(cmLinkItem const& item);
-  void AddVarLinkEntries(size_t depender_index, const char* value);
+  void AddVarLinkEntries(cm::optional<size_t> const& depender_index,
+                         const char* value);
   void AddDirectLinkEntries();
   void AddDirectLinkEntries();
   template <typename T>
   template <typename T>
-  void AddLinkEntries(size_t depender_index, std::vector<T> const& libs);
+  void AddLinkEntries(cm::optional<size_t> const& depender_index,
+                      std::vector<T> const& libs);
   void AddLinkObjects(std::vector<cmLinkItem> const& objs);
   void AddLinkObjects(std::vector<cmLinkItem> const& objs);
-  cmLinkItem ResolveLinkItem(size_t depender_index, const std::string& name);
+  cmLinkItem ResolveLinkItem(cm::optional<size_t> const& depender_index,
+                             const std::string& name);
 
 
   // One entry for each unique item.
   // One entry for each unique item.
   std::vector<LinkEntry> EntryList;
   std::vector<LinkEntry> EntryList;
@@ -120,7 +135,7 @@ private:
   struct BFSEntry
   struct BFSEntry
   {
   {
     size_t Index;
     size_t Index;
-    size_t GroupIndex;
+    cm::optional<size_t> GroupIndex;
     const char* LibDepends;
     const char* LibDepends;
   };
   };
   std::queue<BFSEntry> BFSQueue;
   std::queue<BFSEntry> BFSQueue;
@@ -192,6 +207,7 @@ private:
   void VisitEntry(size_t index);
   void VisitEntry(size_t index);
   PendingComponent& MakePendingComponent(size_t component);
   PendingComponent& MakePendingComponent(size_t component);
   size_t ComputeComponentCount(NodeList const& nl);
   size_t ComputeComponentCount(NodeList const& nl);
+  void DisplayOrderedEntries();
   void DisplayFinalEntries();
   void DisplayFinalEntries();
 
 
   // Record of the original link line.
   // Record of the original link line.
@@ -203,8 +219,5 @@ private:
   std::vector<size_t> ObjectEntries;
   std::vector<size_t> ObjectEntries;
 
 
   size_t ComponentOrderId;
   size_t ComponentOrderId;
-  cmTargetLinkLibraryType LinkType;
-  bool HasConfig;
-  bool DebugMode;
-  bool OldLinkDirMode;
+  bool OldLinkDirMode = false;
 };
 };

+ 19 - 1
Source/cmComputeLinkInformation.cxx

@@ -9,6 +9,7 @@
 
 
 #include <cm/memory>
 #include <cm/memory>
 #include <cm/optional>
 #include <cm/optional>
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 #include <cmext/string_view>
 
 
@@ -567,8 +568,25 @@ bool cmComputeLinkInformation::Compute()
     return false;
     return false;
   }
   }
 
 
+  LinkLibrariesStrategy strategy = LinkLibrariesStrategy::PRESERVE_ORDER;
+  if (cmValue s = this->Target->GetProperty("LINK_LIBRARIES_STRATEGY")) {
+    if (*s == "PRESERVE_ORDER"_s) {
+      strategy = LinkLibrariesStrategy::PRESERVE_ORDER;
+    } else if (*s == "REORDER"_s) {
+      strategy = LinkLibrariesStrategy::REORDER;
+    } else {
+      this->CMakeInstance->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("LINK_LIBRARIES_STRATEGY value '", *s,
+                 "' is not recognized."),
+        this->Target->GetBacktrace());
+      return false;
+    }
+  }
+
   // Compute the ordered link line items.
   // Compute the ordered link line items.
-  cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
+  cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage,
+                           strategy);
   cld.SetOldLinkDirMode(this->OldLinkDirMode);
   cld.SetOldLinkDirMode(this->OldLinkDirMode);
   cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
   cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
   FeatureDescriptor const* currentFeature = nullptr;
   FeatureDescriptor const* currentFeature = nullptr;

+ 8 - 7
Source/cmMakefileTargetGenerator.cxx

@@ -2206,13 +2206,14 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
   bool useResponseFile, std::vector<std::string>& makefile_depends,
   bool useResponseFile, std::vector<std::string>& makefile_depends,
   std::string const& linkLanguage, ResponseFlagFor responseMode)
   std::string const& linkLanguage, ResponseFlagFor responseMode)
 {
 {
-  std::string frameworkPath;
-  std::string linkPath;
-  cmComputeLinkInformation* pcli =
-    this->GeneratorTarget->GetLinkInformation(this->GetConfigName());
-  this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
-                                            frameworkPath, linkPath);
-  linkLibs = frameworkPath + linkPath + linkLibs;
+  if (cmComputeLinkInformation* pcli =
+        this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) {
+    std::string frameworkPath;
+    std::string linkPath;
+    this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
+                                              frameworkPath, linkPath);
+    linkLibs = frameworkPath + linkPath + linkLibs;
+  }
 
 
   if (useResponseFile &&
   if (useResponseFile &&
       linkLibs.find_first_not_of(' ') != std::string::npos) {
       linkLibs.find_first_not_of(' ') != std::string::npos) {

+ 1 - 0
Source/cmTarget.cxx

@@ -466,6 +466,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "LINKER_TYPE"_s, IC::CanCompileSources },
   { "LINKER_TYPE"_s, IC::CanCompileSources },
   { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
+  { "LINK_LIBRARIES_STRATEGY"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources },
   // Initialize per-configuration name postfix property from the variable only
   // Initialize per-configuration name postfix property from the variable only

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -839,6 +839,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)"
 endif()
 endif()
 
 
 add_RunCMake_test(LinkLibrariesProcessing)
 add_RunCMake_test(LinkLibrariesProcessing)
+add_RunCMake_test(LinkLibrariesStrategy)
 add_RunCMake_test(File_Archive)
 add_RunCMake_test(File_Archive)
 add_RunCMake_test(File_Configure)
 add_RunCMake_test(File_Configure)
 add_RunCMake_test(File_Generate)
 add_RunCMake_test(File_Generate)

+ 1 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout-reverse.txt

@@ -0,0 +1 @@
+^Library 'B' was linked first\.$

+ 1 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-run-stdout.txt

@@ -0,0 +1 @@
+^Library 'A' was linked first\.$

+ 7 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER-stderr.txt

@@ -0,0 +1,7 @@
+target \[main\] link dependency ordering:
+  target \[A\]
+  target \[B\]
+  target \[C\]
+  target \[A\]
+
+target \[main\] link line:

+ 2 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-PRESERVE_ORDER.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_LINK_LIBRARIES_STRATEGY PRESERVE_ORDER)
+include(Basic-common.cmake)

+ 1 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-run-stdout.txt

@@ -0,0 +1 @@
+^Library 'B' was linked first\.$

+ 6 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER-stderr.txt

@@ -0,0 +1,6 @@
+target \[main\] link dependency ordering:
+  target \[B\]
+  target \[C\]
+  target \[A\]
+
+target \[main\] link line:

+ 2 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER)
+include(Basic-common.cmake)

+ 15 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake

@@ -0,0 +1,15 @@
+enable_language(C)
+
+add_library(A STATIC BasicA.c BasicX.c)
+add_library(B STATIC BasicB.c BasicX.c)
+add_library(C STATIC BasicC.c BasicX.c)
+target_link_libraries(B PRIVATE A)
+target_link_libraries(C PRIVATE A)
+target_compile_definitions(A PRIVATE BASIC_ID="A")
+target_compile_definitions(B PRIVATE BASIC_ID="B")
+target_compile_definitions(C PRIVATE BASIC_ID="C")
+
+add_executable(main Basic.c)
+target_link_libraries(main PRIVATE A B C)
+set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented
+set_property(TARGET main PROPERTY RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_BINARY_DIR}>")

+ 11 - 0
Tests/RunCMake/LinkLibrariesStrategy/Basic.c

@@ -0,0 +1,11 @@
+extern int BasicB(void);
+extern int BasicC(void);
+
+/* Use a symbol provided by a dedicated object file in A, B, and C.
+   The first library linked will determine the return value.  */
+extern int BasicX(void);
+
+int main(void)
+{
+  return BasicB() + BasicC() + BasicX();
+}

+ 4 - 0
Tests/RunCMake/LinkLibrariesStrategy/BasicA.c

@@ -0,0 +1,4 @@
+int BasicA(void)
+{
+  return 0;
+}

+ 5 - 0
Tests/RunCMake/LinkLibrariesStrategy/BasicB.c

@@ -0,0 +1,5 @@
+extern int BasicA(void);
+int BasicB(void)
+{
+  return BasicA();
+}

+ 5 - 0
Tests/RunCMake/LinkLibrariesStrategy/BasicC.c

@@ -0,0 +1,5 @@
+extern int BasicA(void);
+int BasicC(void)
+{
+  return BasicA();
+}

+ 7 - 0
Tests/RunCMake/LinkLibrariesStrategy/BasicX.c

@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int BasicX(void)
+{
+  printf("Library '%s' was linked first.\n", BASIC_ID);
+  return 0;
+}

+ 3 - 0
Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.30)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 15 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER-stderr.txt

@@ -0,0 +1,15 @@
+target \[main\] link dependency ordering:
+  item \[-Flag1\]
+  target \[A\]
+  item \[-Flag1\]
+  target \[B\]
+  item \[-Flag2\]
+  target \[C\]
+  item \[-Flag2\]
+  target \[A\]
+  item \[-Flag2\]
+  target \[B\]
+  item \[-Flag3\]
+  target \[C\]
+
+target \[main\] link line:

+ 2 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate-PRESERVE_ORDER.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_LINK_LIBRARIES_STRATEGY PRESERVE_ORDER)
+include(Duplicate-common.cmake)

+ 9 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER-stderr.txt

@@ -0,0 +1,9 @@
+target \[main\] link dependency ordering:
+  item \[-Flag1\]
+  target \[A\]
+  target \[B\]
+  item \[-Flag2\]
+  target \[C\]
+  item \[-Flag3\]
+
+target \[main\] link line:

+ 2 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER)
+include(Duplicate-common.cmake)

+ 12 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_library(A INTERFACE IMPORTED)
+add_library(B INTERFACE IMPORTED)
+add_library(C INTERFACE IMPORTED)
+set_property(TARGET A PROPERTY IMPORTED_LIBNAME A)
+set_property(TARGET B PROPERTY IMPORTED_LIBNAME B)
+set_property(TARGET C PROPERTY IMPORTED_LIBNAME C)
+
+add_executable(main Duplicate.c)
+target_link_libraries(main PRIVATE -Flag1 A -Flag1 B -Flag2 C -Flag2 A -Flag2 B -Flag3 C)
+set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented

+ 4 - 0
Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c

@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}

+ 12 - 0
Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+set(info "")
+foreach(var
+    CMAKE_C_LINK_LIBRARIES_PROCESSING
+    )
+  if(DEFINED ${var})
+    string(APPEND info "set(${var} \"${${var}}\")\n")
+  endif()
+endforeach()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")

+ 36 - 0
Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake

@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.30)
+
+include(RunCMake)
+
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+else()
+  set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+endif()
+
+# Detect information from the toolchain:
+# - CMAKE_C_LINK_LIBRARIES_PROCESSING
+run_cmake(Inspect)
+include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
+
+run_cmake(Unknown)
+
+function(run_strategy case exe)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+  run_cmake(${case})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
+  if(exe)
+    if("ORDER=REVERSE" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING)
+      set(RunCMake-stdout-file ${case}-run-stdout-reverse.txt)
+    endif()
+    run_cmake_command(${case}-run ${RunCMake_TEST_BINARY_DIR}/${exe})
+    unset(RunCMake-stdout-file)
+  endif()
+endfunction()
+
+run_strategy(Basic-PRESERVE_ORDER "main")
+run_strategy(Basic-REORDER "main")
+
+run_cmake(Duplicate-PRESERVE_ORDER)
+run_cmake(Duplicate-REORDER)

+ 1 - 0
Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error at Unknown.cmake:5 \(add_executable\):
+  LINK_LIBRARIES_STRATEGY value 'unknown' is not recognized\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9] \(include\)

+ 5 - 0
Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake

@@ -0,0 +1,5 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARIES_STRATEGY unknown)
+
+add_executable(main main.c)

+ 4 - 0
Tests/RunCMake/LinkLibrariesStrategy/main.c

@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}