Browse Source

target_link_libraries: Add support for the LINKER: prefix

It is now possible to use the `LINKER:` prefix in `LINK_LIBRARIES`
and `INTERFACE_LINK_LIBRARIES` target properties.

Fixes: #26318
Marc Chevrier 1 year ago
parent
commit
aba1c9d172

+ 24 - 0
Help/command/LINK_LIBRARIES_LINKER.txt

@@ -0,0 +1,24 @@
+Handling Compiler Driver Differences
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.32
+
+To pass options to the linker tool, each compiler driver has its own syntax.
+The ``LINKER:`` prefix and ``,`` separator can be used to specify, in a portable
+way, options to pass to the linker tool. ``LINKER:`` is replaced by the
+appropriate driver option and ``,`` by the appropriate driver separator.
+The driver prefix and driver separator are given by the values of the
+:variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG` and
+:variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP` variables.
+
+For example, ``"LINKER:-z,defs"`` becomes ``-Xlinker -z -Xlinker defs`` for
+``Clang`` and ``-Wl,-z,defs`` for ``GNU GCC``.
+
+The ``LINKER:`` prefix supports, as an alternative syntax, specification of
+arguments using the ``SHELL:`` prefix and space as separator. The previous
+example then becomes ``"LINKER:SHELL:-z defs"``.
+
+.. note::
+
+  Specifying the ``SHELL:`` prefix anywhere other than at the beginning of the
+  ``LINKER:`` prefix is not supported.

+ 2 - 0
Help/command/target_link_libraries.rst

@@ -148,6 +148,8 @@ command lines.
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 buildsystem properties.
 
+.. include:: ../command/LINK_LIBRARIES_LINKER.txt
+
 Libraries for a Target and/or its Dependents
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

+ 2 - 0
Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst

@@ -32,6 +32,8 @@ direct link dependencies of a target's dependents by using the
 :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and
 :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties.
 
+.. include:: ../command/LINK_LIBRARIES_LINKER.txt
+
 Creating Relocatable Packages
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

+ 6 - 0
Help/prop_tgt/INTERFACE_LINK_OPTIONS.rst

@@ -8,3 +8,9 @@ INTERFACE_LINK_OPTIONS
 .. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_LINK_OPTIONS``
 .. |PROPERTY_LINK| replace:: :prop_tgt:`LINK_OPTIONS`
 .. include:: INTERFACE_BUILD_PROPERTY.txt
+
+.. include:: ../command/DEVICE_LINK_OPTIONS.txt
+
+.. include:: ../command/OPTIONS_SHELL.txt
+
+.. include:: ../command/LINK_OPTIONS_LINKER.txt

+ 2 - 0
Help/prop_tgt/LINK_LIBRARIES.rst

@@ -33,3 +33,5 @@ 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.
+
+.. include:: ../command/LINK_LIBRARIES_LINKER.txt

+ 5 - 0
Help/release/dev/target_link_libraries-LINKER-prefix.rst

@@ -0,0 +1,5 @@
+target_link_libraries-LINKER-prefix
+-----------------------------------
+
+* The :command:`target_link_libraries` command gains the support of the
+  ``LINKER:`` prefix.

+ 8 - 0
Source/cmComputeLinkInformation.cxx

@@ -1883,6 +1883,7 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
   //   foo       ==>  -lfoo
   //   libfoo.a  ==>  -Wl,-Bstatic -lfoo
 
+  const cm::string_view LINKER{ "LINKER:" };
   BT<std::string> const& item = entry.Item;
 
   if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
@@ -1905,6 +1906,13 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
     this->Items.emplace_back(item, ItemIsPath::No);
     return;
   }
+  if (cmHasPrefix(item.Value, LINKER)) {
+    std::vector<BT<std::string>> linkerFlag{ 1, item };
+    this->Target->ResolveLinkerWrapper(linkerFlag, this->GetLinkLanguage(),
+                                       true);
+    this->Items.emplace_back(linkerFlag.front(), ItemIsPath::No);
+    return;
+  }
 
   // Parse out the prefix, base, and suffix components of the
   // library name.  If the name matches that of a shared or static

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -875,6 +875,7 @@ add_RunCMake_test(target_link_libraries-LINK_GROUP -DCMAKE_SYSTEM_NAME=${CMAKE_S
                                                    -DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX}
                                                    -DCMAKE_IMPORT_LIBRARY_SUFFIX=${CMAKE_IMPORT_LIBRARY_SUFFIX}
                                                    -DCMAKE_LINK_LIBRARY_FLAG=${CMAKE_LINK_LIBRARY_FLAG})
+add_RunCMake_test(target_link_libraries-LINKER-prefix -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 add_RunCMake_test(add_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 add_RunCMake_test(target_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
                                       -DCMake_TEST_CUDA=${CMake_TEST_CUDA})

+ 5 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/CMakeLists.txt

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

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion-LINKER-check.cmake

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

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion-LINKER_CONSUMER-check.cmake

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

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion-LINKER_SHELL-check.cmake

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

+ 15 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion-validation.cmake

@@ -0,0 +1,15 @@
+
+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}/${reference_file}")
+  set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/${reference_file}: Reference file not found.")
+  return()
+endif()
+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.")
+endif()

+ 63 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion.cmake

@@ -0,0 +1,63 @@
+
+enable_language(C)
+
+set(cfg_dir)
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+  set(cfg_dir /Debug)
+endif()
+set(DUMP_EXE "${CMAKE_CURRENT_BINARY_DIR}${cfg_dir}/dump${CMAKE_EXECUTABLE_SUFFIX}")
+
+add_executable(dump dump.c)
+
+# ensure no temp file nor response file will be used
+set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
+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 LAUNCH facility to dump linker command
+  set_property(TARGET ${target_name} PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"")
+
+  add_dependencies(${target_name} dump)
+endfunction()
+
+# Use LINKER alone
+add_test_library(linker)
+target_link_libraries(linker PRIVATE "LINKER:-foo,bar")
+
+# Use LINKER with SHELL
+add_test_library(linker_shell)
+target_link_libraries(linker_shell PRIVATE "LINKER:SHELL:-foo bar")
+
+# Propagate LINKER
+add_library(linker_interface INTERFACE)
+target_link_libraries(linker_interface INTERFACE "LINKER:-foo,bar")
+add_test_library(linker_consumer)
+target_link_libraries(linker_consumer PRIVATE linker_interface)
+
+# generate reference for LINKER flag
+if (CMAKE_C_LINKER_WRAPPER_FLAG)
+  set(linker_flag ${CMAKE_C_LINKER_WRAPPER_FLAG})
+  list(GET linker_flag -1 linker_space)
+  if (linker_space STREQUAL " ")
+    list(REMOVE_AT linker_flag -1)
+  else()
+    set(linker_space)
+  endif()
+  list (JOIN linker_flag " " linker_flag)
+  if (CMAKE_C_LINKER_WRAPPER_FLAG_SEP)
+    set(linker_sep "${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}")
+
+    string (APPEND  linker_flag "${linker_space}" "-foo${linker_sep}bar")
+  else()
+    set(linker_prefix "${linker_flag}${linker_space}")
+
+    set (linker_flag "${linker_prefix}-foo ${linker_prefix}bar")
+  endif()
+else()
+  set(linker_flag "-foo bar")
+endif()
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER.txt" "${linker_flag}")

+ 7 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LinkOptionsLib.c

@@ -0,0 +1,7 @@
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  int flags_lib(void)
+{
+  return 0;
+}

+ 22 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/RunCMakeTest.cmake

@@ -0,0 +1,22 @@
+
+include(RunCMake)
+
+macro(run_cmake_target test subtest target)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --target ${target} ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+
+run_cmake(bad_SHELL_usage)
+
+if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
+  run_cmake(LINKER_expansion)
+
+  run_cmake_target(LINKER_expansion LINKER linker)
+  run_cmake_target(LINKER_expansion LINKER_SHELL linker_shell)
+  run_cmake_target(LINKER_expansion LINKER_CONSUMER linker_consumer)
+endif()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/bad_SHELL_usage-result.txt

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

+ 4 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/bad_SHELL_usage-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at bad_SHELL_usage.cmake:[0-9]+ \(add_library\):
+  'SHELL:' prefix is not supported as part of 'LINKER:' arguments.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 5 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/bad_SHELL_usage.cmake

@@ -0,0 +1,5 @@
+
+enable_language(C)
+
+add_library(example SHARED LinkOptionsLib.c)
+target_link_libraries(example PRIVATE "LINKER:-foo,SHELL:-bar")

+ 13 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/dump.c

@@ -0,0 +1,13 @@
+
+#include "stdio.h"
+
+int main(int argc, char* argv[])
+{
+  int i;
+
+  for (i = 1; i < argc; i++)
+    printf("%s ", argv[i]);
+  printf("\n");
+
+  return 0;
+}