Browse Source

Merge topic 'nested_linker_prefixes'

e3895f4a8b Linking: Preserve nested LINKER: prefixes as written
4185dfbe1b Tests/LINK_OPTIONS: extract common code in test (NFC)
54381b5a81 Linking: extract wrapping linker options to a lambda (NFC)

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !9823
Brad King 1 year ago
parent
commit
506255b1f1

+ 69 - 49
Source/cmGeneratorTarget_Options.cxx

@@ -7,6 +7,7 @@
 #include "cmConfigure.h"
 
 #include <algorithm>
+#include <iterator>
 #include <map>
 #include <memory>
 #include <string>
@@ -91,10 +92,16 @@ void processOptions(cmGeneratorTarget const* tgt,
   }
 }
 
+enum class NestedLinkerFlags
+{
+  PreserveAsSpelled,
+  Normalize,
+};
+
 std::vector<BT<std::string>> wrapOptions(
   std::vector<std::string>& options, const cmListFileBacktrace& bt,
   const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep,
-  bool concatFlagAndArgs)
+  bool concatFlagAndArgs, NestedLinkerFlags nestedLinkerFlags)
 {
   std::vector<BT<std::string>> result;
 
@@ -111,6 +118,48 @@ std::vector<BT<std::string>> wrapOptions(
     return result;
   }
 
+  auto insertWrapped = [&](std::vector<std::string>& opts) {
+    if (!wrapperSep.empty()) {
+      if (concatFlagAndArgs) {
+        // insert flag elements except last one
+        for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
+          result.emplace_back(*i, bt);
+        }
+        // concatenate last flag element and all list values
+        // in one option
+        result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
+      } else {
+        for (std::string const& i : wrapperFlag) {
+          result.emplace_back(i, bt);
+        }
+        // concatenate all list values in one option
+        result.emplace_back(cmJoin(opts, wrapperSep), bt);
+      }
+    } else {
+      // prefix each element of list with wrapper
+      if (concatFlagAndArgs) {
+        std::transform(opts.begin(), opts.end(), opts.begin(),
+                       [&wrapperFlag](std::string const& o) -> std::string {
+                         return wrapperFlag.back() + o;
+                       });
+      }
+      for (std::string& o : opts) {
+        for (auto i = wrapperFlag.begin(),
+                  e = concatFlagAndArgs ? wrapperFlag.end() - 1
+                                        : wrapperFlag.end();
+             i != e; ++i) {
+          result.emplace_back(*i, bt);
+        }
+        result.emplace_back(std::move(o), bt);
+      }
+    }
+  };
+
+  if (nestedLinkerFlags == NestedLinkerFlags::PreserveAsSpelled) {
+    insertWrapped(options);
+    return result;
+  }
+
   for (std::vector<std::string>::size_type index = 0; index < options.size();
        index++) {
     if (cmHasLiteralPrefix(options[index], "LINKER:")) {
@@ -154,40 +203,7 @@ std::vector<BT<std::string>> wrapOptions(
       continue;
     }
 
-    if (!wrapperSep.empty()) {
-      if (concatFlagAndArgs) {
-        // insert flag elements except last one
-        for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) {
-          result.emplace_back(*i, bt);
-        }
-        // concatenate last flag element and all list values
-        // in one option
-        result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt);
-      } else {
-        for (std::string const& i : wrapperFlag) {
-          result.emplace_back(i, bt);
-        }
-        // concatenate all list values in one option
-        result.emplace_back(cmJoin(opts, wrapperSep), bt);
-      }
-    } else {
-      // prefix each element of list with wrapper
-      if (concatFlagAndArgs) {
-        std::transform(opts.begin(), opts.end(), opts.begin(),
-                       [&wrapperFlag](std::string const& o) -> std::string {
-                         return wrapperFlag.back() + o;
-                       });
-      }
-      for (std::string& o : opts) {
-        for (auto i = wrapperFlag.begin(),
-                  e = concatFlagAndArgs ? wrapperFlag.end() - 1
-                                        : wrapperFlag.end();
-             i != e; ++i) {
-          result.emplace_back(*i, bt);
-        }
-        result.emplace_back(std::move(o), bt);
-      }
-    }
+    insertWrapped(opts);
   }
   return result;
 }
@@ -484,8 +500,9 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
         // host link options must be wrapped
         std::vector<std::string> options;
         cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options);
-        auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag,
-                                       wrapperSep, concatFlagAndArgs);
+        auto hostOptions =
+          wrapOptions(options, it->Backtrace, wrapperFlag, wrapperSep,
+                      concatFlagAndArgs, NestedLinkerFlags::Normalize);
         it = result.erase(it);
         // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect
         // C++11 standard: 'std::vector::insert()' do not returns an iterator,
@@ -534,12 +551,11 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
   const std::string SHELL{ "SHELL:" };
   const std::string LINKER_SHELL = LINKER + SHELL;
 
-  std::vector<BT<std::string>>::iterator entry;
-  while ((entry = std::find_if(result.begin(), result.end(),
-                               [&LINKER](BT<std::string> const& item) -> bool {
-                                 return item.Value.compare(0, LINKER.length(),
-                                                           LINKER) == 0;
-                               })) != result.end()) {
+  for (auto entry = result.begin(); entry != result.end(); ++entry) {
+    if (entry->Value.compare(0, LINKER.length(), LINKER) != 0) {
+      continue;
+    }
+
     std::string value = std::move(entry->Value);
     cmListFileBacktrace bt = std::move(entry->Backtrace);
     entry = result.erase(entry);
@@ -569,15 +585,19 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
       return result;
     }
 
-    std::vector<BT<std::string>> options = wrapOptions(
-      linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
+    // Very old versions of the C++ standard library return void for insert, so
+    // can't use it to get the new iterator
+    const auto index = entry - result.begin();
+    std::vector<BT<std::string>> options =
+      wrapOptions(linkerOptions, bt, wrapperFlag, wrapperSep,
+                  concatFlagAndArgs, NestedLinkerFlags::PreserveAsSpelled);
     if (joinItems) {
-      result.insert(entry,
-                    cmJoin(cmRange<decltype(options.cbegin())>(
-                             options.cbegin(), options.cend()),
-                           " "_s));
+      result.insert(
+        entry, cmJoin(cmMakeRange(options.begin(), options.end()), " "_s));
+      entry = std::next(result.begin(), index);
     } else {
       result.insert(entry, options.begin(), options.end());
+      entry = std::next(result.begin(), index + options.size() - 1);
     }
   }
   return result;

+ 1 - 0
Tests/RunCMake/target_link_options/LINKER_expansion-LINKER-check.cmake

@@ -1,2 +1,3 @@
 
+set(reference_file "LINKER.txt")
 include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 4 - 0
Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED-check.cmake

@@ -0,0 +1,4 @@
+
+set(reference_file "LINKER_NESTED.txt")
+set(linker_prefix_expected YES)
+include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 4 - 0
Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED_SHELL-check.cmake

@@ -0,0 +1,4 @@
+
+set(reference_file "LINKER_NESTED_SHELL.txt")
+set(linker_prefix_expected YES)
+include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 1 - 0
Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_SHELL-check.cmake

@@ -1,2 +1,3 @@
 
+set(reference_file "LINKER.txt")
 include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 4 - 4
Tests/RunCMake/target_link_options/LINKER_expansion-validation.cmake

@@ -1,14 +1,14 @@
 
-if (actual_stdout MATCHES "LINKER:")
+if (actual_stdout MATCHES "LINKER:" AND NOT linker_prefix_expected)
   set (RunCMake_TEST_FAILED "LINKER: prefix was not expanded.")
   return()
 endif()
 
-if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/LINKER.txt")
-  set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/LINKER.txt: Reference file not found.")
+if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${reference_file}")
+  set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/${reference_file}: Reference file not found.")
   return()
 endif()
-file(READ "${RunCMake_TEST_BINARY_DIR}/LINKER.txt" linker_flag)
+file(READ "${RunCMake_TEST_BINARY_DIR}/${reference_file}" linker_flag)
 
 if (NOT actual_stdout MATCHES "${linker_flag}")
   set (RunCMake_TEST_FAILED "LINKER: was not expanded correctly.")

+ 30 - 14
Tests/RunCMake/target_link_options/LINKER_expansion.cmake

@@ -14,26 +14,30 @@ add_executable(dump dump.c)
 string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
 string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
 
+function (add_test_library target_name)
+  add_library(${target_name} SHARED LinkOptionsLib.c)
 
-# Use LINKER alone
-add_library(linker SHARED LinkOptionsLib.c)
-target_link_options(linker PRIVATE "LINKER:-foo,bar")
+  # use LAUNCH facility to dump linker command
+  set_property(TARGET ${target_name} PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"")
 
-# use LAUNCH facility to dump linker command
-set_property(TARGET linker PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"")
-
-add_dependencies (linker dump)
+  add_dependencies(${target_name} dump)
+endfunction()
 
+# Use LINKER alone
+add_test_library(linker)
+target_link_options(linker PRIVATE "LINKER:-foo,bar")
 
 # Use LINKER with SHELL
-add_library(linker_shell SHARED LinkOptionsLib.c)
+add_test_library(linker_shell)
 target_link_options(linker_shell PRIVATE "LINKER:SHELL:-foo bar")
 
-# use LAUNCH facility to dump linker command
-set_property(TARGET linker_shell PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"")
-
-add_dependencies (linker_shell dump)
+# Nested LINKER: prefixes should be preserved as written, only the outermost LINKER: prefix removed
+add_test_library(linker_nested)
+target_link_options(linker_nested PRIVATE "LINKER:LINKER:-foo,-Xlinker=bar,-Xlinker,--baz,-Wl,/qux")
 
+# Same with LINKER:SHELL:
+add_test_library(linker_nested_shell SHARED LinkOptionsLib.c)
+target_link_options(linker_nested_shell PRIVATE "LINKER:SHELL:LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl,/qux")
 
 # generate reference for LINKER flag
 if (CMAKE_C_LINKER_WRAPPER_FLAG)
@@ -46,11 +50,23 @@ if (CMAKE_C_LINKER_WRAPPER_FLAG)
   endif()
   list (JOIN linker_flag " " linker_flag)
   if (CMAKE_C_LINKER_WRAPPER_FLAG_SEP)
-    string (APPEND  linker_flag "${linker_space}" "-foo${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}bar")
+    set(linker_sep "${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}")
+
+    set(linker_flag_nested       "${linker_flag}${linker_space}LINKER:-foo${linker_sep}-Xlinker=bar${linker_sep}-Xlinker${linker_sep}--baz${linker_sep}-Wl${linker_sep}/qux")
+    set(linker_flag_nested_shell "${linker_flag}${linker_space}LINKER:-foo${linker_sep}-Xlinker=bar${linker_sep}-Xlinker${linker_sep}--baz${linker_sep}-Wl,/qux")
+    string (APPEND  linker_flag "${linker_space}" "-foo${linker_sep}bar")
   else()
-    set (linker_flag "${linker_flag}${linker_space}-foo ${linker_flag}${linker_space}bar")
+    set(linker_prefix "${linker_flag}${linker_space}")
+
+    set(linker_flag_nested       "${linker_prefix}LINKER:-foo ${linker_prefix}-Xlinker=bar ${linker_prefix}-Xlinker ${linker_prefix}--baz ${linker_prefix}-Wl ${linker_prefix}/qux")
+    set(linker_flag_nested_shell "${linker_prefix}LINKER:-foo ${linker_prefix}-Xlinker=bar ${linker_prefix}-Xlinker ${linker_prefix}--baz ${linker_prefix}-Wl,/qux")
+    set (linker_flag "${linker_prefix}-foo ${linker_prefix}bar")
   endif()
 else()
+  set(linker_flag_nested       "LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl /qux")
+  set(linker_flag_nested_shell "LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl,/qux")
   set(linker_flag "-foo bar")
 endif()
 file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER.txt" "${linker_flag}")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_NESTED.txt" "${linker_flag_nested}")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_NESTED_SHELL.txt" "${linker_flag_nested_shell}")

+ 2 - 0
Tests/RunCMake/target_link_options/RunCMakeTest.cmake

@@ -76,6 +76,8 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
 
   run_cmake_target(LINKER_expansion LINKER linker)
   run_cmake_target(LINKER_expansion LINKER_SHELL linker_shell)
+  run_cmake_target(LINKER_expansion LINKER_NESTED linker_nested)
+  run_cmake_target(LINKER_expansion LINKER_NESTED_SHELL linker_nested_shell)
 endif()
 
 run_cmake(empty_keyword_args)