Explorar o código

target_link_libraries: Restore LINK_ONLY for multiple static lib dependencies

Since commit c1e812ad4f (target_link_libraries: Improve tolerance of
unquoted generator expressions, 2022-02-15, v3.23.0-rc2~11^2) we
accumulate consecutive non-keyword arguments to recover an unquoted
generator expression as a single entry.  When given multiple consecutive
non-genex library names, the grouping breaks our logic that expects each
entry is either a raw target name or a genex.  Revise the logic to only
accumulate multiple arguments when they end inside a partial genex.

This bug caused `target_link_libraries` to stop wrapping static library
private dependencies in `$<LINK_ONLY:...>` for `INTERFACE_LINK_LIBRARIES`
when multiple consecutive library names are given.  Add a test case
covering that behavior.

Fixes: #23302
Brad King %!s(int64=3) %!d(string=hai) anos
pai
achega
add64399c5

+ 27 - 2
Source/cmTargetLinkLibrariesCommand.cxx

@@ -9,6 +9,7 @@
 #include <utility>
 
 #include <cm/optional>
+#include <cm/string_view>
 
 #include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
@@ -183,8 +184,11 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
 
   // Accumulate consectuive non-keyword arguments into one entry in
   // order to handle unquoted generator expressions containing ';'.
+  std::size_t genexNesting = 0;
   cm::optional<std::string> currentEntry;
   auto processCurrentEntry = [&]() -> bool {
+    // FIXME: Warn about partial genex if genexNesting > 0?
+    genexNesting = 0;
     if (currentEntry) {
       assert(!haveLLT);
       if (!tll.HandleLibrary(currentProcessingState, *currentEntry,
@@ -220,7 +224,7 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
   // of debug and optimized that can be used.
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (keywords.count(args[i])) {
-      // A keyword argument terminates any preceding accumulated entry.
+      // A keyword argument terminates any accumulated partial genex.
       if (!processCurrentEntry()) {
         return false;
       }
@@ -321,12 +325,33 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
       }
       llt = GENERAL_LibraryType;
     } else {
+      // Track the genex nesting level.
+      {
+        cm::string_view arg = args[i];
+        for (std::string::size_type pos = 0; pos < arg.size(); ++pos) {
+          cm::string_view cur = arg.substr(pos);
+          if (cmHasLiteralPrefix(cur, "$<")) {
+            ++genexNesting;
+            ++pos;
+          } else if (genexNesting > 0 && cmHasLiteralPrefix(cur, ">")) {
+            --genexNesting;
+          }
+        }
+      }
+
       // Accumulate this argument in the current entry.
       extendCurrentEntry(args[i]);
+
+      // Process this entry if it does not end inside a genex.
+      if (genexNesting == 0) {
+        if (!processCurrentEntry()) {
+          return false;
+        }
+      }
     }
   }
 
-  // Process the last accumulated entry, if any.
+  // Process the last accumulated partial genex, if any.
   if (!processCurrentEntry()) {
     return false;
   }

+ 16 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/CMakeLists.txt

@@ -33,6 +33,22 @@ add_library(staticlib2 STATIC staticlib2.cpp)
 generate_export_header(staticlib2)
 target_link_libraries(staticlib1 LINK_PUBLIC staticlib2)
 
+# Test adding LINK_ONLY to each of multiple specified libraries.
+add_library(staticlib2iface1 INTERFACE)
+add_library(staticlib2iface2 INTERFACE)
+target_compile_definitions(staticlib2iface1 INTERFACE STATICLIB2_IFACE_1)
+target_compile_definitions(staticlib2iface2 INTERFACE STATICLIB2_IFACE_2)
+target_link_libraries(staticlib2 PRIVATE staticlib2iface1 staticlib2iface2)
+
+# Test adding unquoted genex with ';' to LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES.
+target_link_libraries(staticlib2
+  PUBLIC $<0:imp::missing1;imp::missing2>
+  PRIVATE $<0:imp::missing3;imp::missing4>
+  INTERFACE $<0:imp::missing5;imp::missing6>
+  )
+assert_property(staticlib2 INTERFACE_LINK_LIBRARIES "$<LINK_ONLY:staticlib2iface1>;$<LINK_ONLY:staticlib2iface2>;$<0:imp::missing1;imp::missing2>;$<LINK_ONLY:$<0:imp::missing3;imp::missing4>>;$<0:imp::missing5;imp::missing6>")
+assert_property(staticlib2 LINK_LIBRARIES "staticlib2iface1;staticlib2iface2;$<0:imp::missing1;imp::missing2>;$<0:imp::missing3;imp::missing4>")
+
 # Try adding a private link item to be propagated out of a static lib.
 set(private_link "")
 if (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang OR CMAKE_CXX_COMPILER_ID MATCHES LCC)

+ 6 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/staticlib1.cpp

@@ -1,3 +1,9 @@
+#ifdef STATICLIB2_IFACE_1
+#  error "STATICLIB2_IFACE_1 incorrectly defined"
+#endif
+#ifdef STATICLIB2_IFACE_2
+#  error "STATICLIB2_IFACE_2 incorrectly defined"
+#endif
 
 int staticlib1()
 {

+ 6 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/staticlib2.cpp

@@ -1,3 +1,9 @@
+#ifndef STATICLIB2_IFACE_1
+#  error "STATICLIB2_IFACE_1 incorrectly not defined"
+#endif
+#ifndef STATICLIB2_IFACE_2
+#  error "STATICLIB2_IFACE_2 incorrectly not defined"
+#endif
 
 int staticlib2()
 {