Browse Source

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 year ago
parent
commit
53f3e5da1e
39 changed files with 465 additions and 135 deletions
  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.
 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
 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_LIBRARIES
    /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS
+   /prop_tgt/LINK_LIBRARIES_STRATEGY
    /prop_tgt/LINK_LIBRARY_OVERRIDE
    /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY
    /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_SUPPORTED
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
+   /variable/CMAKE_LINK_LIBRARIES_STRATEGY
    /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES
    /variable/CMAKE_LINK_LIBRARY_FILE_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.
 See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and
 :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
-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)
 {
@@ -431,8 +433,8 @@ public:
       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) {
+      for (const auto& g : groups) {
+        for (auto index : g.second) {
           this->Emitted.insert(index);
         }
       }
@@ -477,8 +479,8 @@ public:
 
     // expand 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();
         while (true) {
           it = std::find_if(it, this->FinalEntries.end(),
@@ -488,13 +490,13 @@ public:
           if (it == this->FinalEntries.end()) {
             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, 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,
                                            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
   // global override property
   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;
@@ -706,7 +697,7 @@ cmComputeLinkDepends::Compute()
             "---------------------------------------\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->Config.empty() ? "noconfig" : this->Config.c_str());
     this->DisplayConstraintGraph();
   }
 
@@ -726,6 +717,11 @@ cmComputeLinkDepends::Compute()
   // Compute the final ordering.
   this->OrderLinkEntries();
 
+  // Display the final ordering.
+  if (this->DebugMode) {
+    this->DisplayOrderedEntries();
+  }
+
   // Compute the final set of link entries.
   EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
                                        this->EntryList,
@@ -766,7 +762,7 @@ cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
 }
 
 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.
   auto lei = this->AllocateLinkEntry(item);
@@ -842,10 +838,7 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
 void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
 {
   // 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];
 
   // 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
   // <item>_LIB_DEPENDS.  The variable contains a semicolon-separated
@@ -1006,15 +999,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
   // Add direct link dependencies in this configuration.
   cmLinkImplementation const* impl = this->Target->GetLinkImplementation(
     this->Config, cmGeneratorTarget::UseTo::Link);
-  this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT,
-                       impl->Libraries);
+  this->AddLinkEntries(cm::nullopt, 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);
+      this->AddLinkEntries(cm::nullopt, runtimeEntries->second);
     }
   }
   for (cmLinkItem const& wi : impl->WrongConfigLibraries) {
@@ -1023,16 +1014,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries()
 }
 
 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.
   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;
 
   // 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
     // 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() &&
           !IsFeatureSupported(this->Makefile, this->LinkLanguage,
                               item.Feature)) {
@@ -1069,18 +1056,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
 
     if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
         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());
       }
-      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 {
         // This is a direct dependency of the target being linked.
-        this->OriginalEntries.push_back(groupIndex.first);
+        this->OriginalEntries.push_back(group->first);
       }
       continue;
     }
@@ -1088,20 +1074,19 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     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);
+      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();
       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() &&
           !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
                                    groupFeature)) {
@@ -1121,18 +1106,19 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     }
 
     // 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;
     LinkEntry& entry = this->EntryList[dependee_index];
     bool supportedItem = true;
     auto const& itemFeature =
       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::INTERFACE_LIBRARY)) {
       supportedItem = false;
-      const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
+      const auto& groupFeature = this->EntryList[group->first].Feature;
       this->CMakeInstance->IssueMessage(
         MessageType::AUTHOR_WARNING,
         cmStrCat(
@@ -1173,8 +1159,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
     }
 
     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) {
           const auto& groupFeature = this->EntryList[g.first].Feature;
           if (groupFeature == currentFeature) {
@@ -1245,17 +1231,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
       }
     }
 
-    if (inGroup) {
+    if (group) {
       // 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) {
+      for (const auto& g : this->GroupItems) {
+        for (auto index : g.second) {
           if (entry.Item.Value == this->EntryList[index].Item.Value) {
-            indexes.push_back(group.first);
+            indexes.push_back(g.first);
             entryHandled = true;
             break;
           }
@@ -1267,8 +1253,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index,
 
       for (auto index : indexes) {
         // 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());
         } else {
           // 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.
   cmGeneratorTarget const* from = this->Target;
-  if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
+  if (depender_index) {
     if (cmGeneratorTarget const* depender =
-          this->EntryList[depender_index].Target) {
+          this->EntryList[*depender_index].Target) {
       from = depender;
     }
   }
@@ -1494,9 +1480,8 @@ void cmComputeLinkDepends::OrderLinkEntries()
   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);
+  for (size_t c = n; c > 0; --c) {
+    this->VisitComponent(c - 1);
   }
 
   // Display the component graph.
@@ -1505,8 +1490,22 @@ void cmComputeLinkDepends::OrderLinkEntries()
   }
 
   // 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
@@ -1671,26 +1670,49 @@ size_t cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
   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 {
-      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");
 }

+ 30 - 17
Source/cmComputeLinkDepends.h

@@ -13,18 +13,26 @@
 #include <utility>
 #include <vector>
 
-#include "cmComputeComponentGraph.h"
+#include <cm/optional>
+
 #include "cmGraphAdjacencyList.h"
 #include "cmLinkItem.h"
 #include "cmListFileCache.h"
 #include "cmTargetLinkLibraryType.h"
 
+class cmComputeComponentGraph;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmMakefile;
 class cmSourceFile;
 class cmake;
 
+enum class LinkLibrariesStrategy
+{
+  PRESERVE_ORDER,
+  REORDER,
+};
+
 /** \class cmComputeLinkDepends
  * \brief Compute link dependencies for targets.
  */
@@ -33,7 +41,8 @@ class cmComputeLinkDepends
 public:
   cmComputeLinkDepends(cmGeneratorTarget const* target,
                        const std::string& config,
-                       const std::string& linkLanguage);
+                       const std::string& linkLanguage,
+                       LinkLibrariesStrategy strategy);
   ~cmComputeLinkDepends();
 
   cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -84,12 +93,16 @@ public:
 
 private:
   // Context information.
-  cmGeneratorTarget const* Target;
-  cmMakefile* Makefile;
-  cmGlobalGenerator const* GlobalGenerator;
+  cmGeneratorTarget const* Target = nullptr;
+  cmMakefile* Makefile = nullptr;
+  cmGlobalGenerator const* GlobalGenerator = nullptr;
   cmake* CMakeInstance;
-  std::string LinkLanguage;
   std::string Config;
+  bool DebugMode = false;
+  std::string LinkLanguage;
+  cmTargetLinkLibraryType LinkType;
+  LinkLibrariesStrategy Strategy;
+
   EntryVector FinalLinkEntries;
   std::map<std::string, std::string> LinkLibraryOverride;
 
@@ -98,16 +111,18 @@ private:
 
   std::pair<std::map<cmLinkItem, size_t>::iterator, bool> AllocateLinkEntry(
     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 AddVarLinkEntries(size_t depender_index, const char* value);
+  void AddVarLinkEntries(cm::optional<size_t> const& depender_index,
+                         const char* value);
   void AddDirectLinkEntries();
   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);
-  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.
   std::vector<LinkEntry> EntryList;
@@ -120,7 +135,7 @@ private:
   struct BFSEntry
   {
     size_t Index;
-    size_t GroupIndex;
+    cm::optional<size_t> GroupIndex;
     const char* LibDepends;
   };
   std::queue<BFSEntry> BFSQueue;
@@ -192,6 +207,7 @@ private:
   void VisitEntry(size_t index);
   PendingComponent& MakePendingComponent(size_t component);
   size_t ComputeComponentCount(NodeList const& nl);
+  void DisplayOrderedEntries();
   void DisplayFinalEntries();
 
   // Record of the original link line.
@@ -203,8 +219,5 @@ private:
   std::vector<size_t> ObjectEntries;
 
   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/optional>
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -567,8 +568,25 @@ bool cmComputeLinkInformation::Compute()
     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.
-  cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
+  cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage,
+                           strategy);
   cld.SetOldLinkDirMode(this->OldLinkDirMode);
   cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
   FeatureDescriptor const* currentFeature = nullptr;

+ 8 - 7
Source/cmMakefileTargetGenerator.cxx

@@ -2206,13 +2206,14 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
   bool useResponseFile, std::vector<std::string>& makefile_depends,
   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 &&
       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 },
   { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
+  { "LINK_LIBRARIES_STRATEGY"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
   { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources },
   // 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()
 
 add_RunCMake_test(LinkLibrariesProcessing)
+add_RunCMake_test(LinkLibrariesStrategy)
 add_RunCMake_test(File_Archive)
 add_RunCMake_test(File_Configure)
 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;
+}