target_link_options() 是 CMake 3.13 及更高版本中引入的命令,用于向特定的目标(可执行文件、静态库或共享库)添加链接器选项。这些选项会直接传递给链接器命令。

核心功能: 精细控制链接阶段传递给链接器的标志。

与 target_link_libraries() 的区别:

  • target_link_libraries(): 主要用于指定目标依赖的其他库(如 -lm, -lpthread, 具体的库文件路径或 CMake 目标名)。它也会隐式处理一些必要的链接器搜索路径(-L)。
  • target_link_options(): 专门用于传递纯粹的链接器标志(如 -Wl,--as-needed, -fsanitize=address, /DEBUG:FULL, -z defs, --exclude-libs)。这些标志直接影响链接器本身的行为,而不是指定要链接的库。

命令语法:

target_link_options(<target> [BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
  • <target>: 要添加链接选项的目标名称(由 add_executable() 或 add_library() 创建)。
  • [BEFORE]: (可选) 如果指定,新添加的选项将插入到目标现有选项列表的开头,而不是默认的结尾。这在需要覆盖某些默认设置时很有用。
  • <INTERFACE|PUBLIC|PRIVATE>: 指定选项的作用域和传播行为:
    • PRIVATE: 选项仅用于链接 <target> 本身。当其他目标链接 <target> 时,不会继承这些选项。
    • INTERFACE: 选项不会用于链接 <target> 本身。但是,当其他目标链接 <target>(使用 target_link_libraries())时,这些选项会传播给那些依赖目标。
    • PUBLIC: 选项既用于链接 <target> 本身,也会传播给链接 <target> 的其他目标。相当于 PRIVATE + INTERFACE。
  • [items...]: 要添加的链接器选项。可以是字符串形式的选项(如 "-Wl,--no-undefined")或 CMake 生成器表达式(如 $<LINK_LANG_AND_ID:CXX,AppleClang,Clang>:...)。

关键注意事项:

  1. CMake 版本要求: 最低需要 CMake 3.13。
  2. 作用对象: 只能用于 add_executable(), add_library(), 和 add_custom_target()(仅当它有依赖项时)创建的目标。
  3. 库依赖: 不要用它来指定要链接的库文件(.a, .so, .dll, .lib)。那是 target_link_libraries() 的工作。
  4. 平台和编译器特定性: 链接器选项通常是高度平台和编译器/链接器相关的(如 GCC/Clang 的 ld, MSVC 的 link.exe, Apple 的 ld64)。使用生成器表达式或条件判断(if())来确保选项只在适用的平台上添加。
  5. 生成器表达式: 强烈推荐与生成器表达式结合使用,以根据构建配置(Debug/Release)、编译器、平台等条件化地添加选项。
  6. 作用域传播: 仔细考虑使用 PRIVATE, INTERFACE 或 PUBLIC。错误地使用 INTERFACE 或 PUBLIC 可能导致选项传播到不需要它的目标,造成链接错误或意外行为。默认优先使用 PRIVATE。

使用示例:

示例 1:基本用法 (PRIVATE)

add_executable(my_app main.cpp)
# 仅对 my_app 添加链接选项:强制所有符号必须有定义 (GCC/Clang)
target_link_options(my_app PRIVATE "-Wl,--no-undefined")

示例 2:传递选项给依赖者 (INTERFACE)

add_library(my_shared_lib SHARED lib.cpp)
# 告诉链接 my_shared_lib 的目标要使用 -Bsymbolic (GCC/Clang)
target_link_options(my_shared_lib INTERFACE "-Wl,-Bsymbolic")

add_executable(app_using_lib app.cpp)
# 链接 app_using_lib 到 my_shared_lib 时,app_using_lib 会自动获得 -Wl,-Bsymbolic 选项
target_link_libraries(app_using_lib PRIVATE my_shared_lib)

示例 3:使用生成器表达式 (条件化选项)

add_executable(my_app main.cpp)
# 只在 GCC 或 Clang 上添加 -fsanitize=address (并自动链接相关库)
target_link_options(my_app PRIVATE
  "$<$<CXX_COMPILER_ID:GNU,Clang>:-fsanitize=address>"
)
# 只在 MSVC 的 Debug 配置下添加 /DEBUG:FULL
target_link_options(my_app PRIVATE
  "$<$<AND:$<C_COMPILER_ID:MSVC>,$<CONFIG:Debug>>:/DEBUG:FULL>"
)

示例 4:处理静态库依赖 (PUBLIC/BEFORE - Whole Archive)

# 假设 static_lib 是一个你想强制包含其所有符号的静态库
add_library(static_lib STATIC ...)
add_library(another_static_lib STATIC ...)

add_executable(my_app main.cpp)
# 链接静态库
target_link_libraries(my_app PRIVATE static_lib another_static_lib)

# 只对 static_lib 应用 --whole-archive (GCC/Clang),确保它所有符号都被包含
target_link_options(my_app PRIVATE
  # 在链接 static_lib 之前开启 --whole-archive
  "-Wl,--whole-archive"
  # 确保 static_lib 被包裹在 --whole-archive 和 --no-whole-archive 之间
  "$<LINK_LIBRARY:WHOLE_ARCHIVE,static_lib>"
  # 在链接 static_lib 之后关闭 --whole-archive
  "-Wl,--no-whole-archive"
)
# 使用 BEFORE 确保这些选项出现在链接 static_lib 的命令之前

示例 5:跨平台选项 (使用 if() 和生成器表达式)

add_executable(my_app main.cpp)

# 定义一个跨平台的链接选项变量
set(MY_LINK_OPTIONS "")
if(APPLE)
    set(MY_LINK_OPTIONS "-Wl,-dead_strip") # macOS 的 dead code stripping
elseif(UNIX AND NOT APPLE)
    set(MY_LINK_OPTIONS "-Wl,--gc-sections") # Linux/Unix 的垃圾回收节
elseif(MSVC)
    set(MY_LINK_OPTIONS "/OPT:REF") # MSVC 的优化掉未引用函数/数据
endif()

# 添加跨平台选项
target_link_options(my_app PRIVATE ${MY_LINK_OPTIONS})

# 或者更精确地使用生成器表达式 (推荐)
target_link_options(my_app PRIVATE
  "$<$<PLATFORM_ID:Darwin>:-Wl,-dead_strip>"
  "$<$<AND:$<PLATFORM_ID:Linux>,$<NOT:$<PLATFORM_ID:Darwin>>>:-Wl,--gc-sections>"
  "$<$<C_COMPILER_ID:MSVC>:/OPT:REF>"
)

总结:

  • 当需要传递特定于链接器的标志(而不是库本身)时,使用 target_link_options()。
  • 明确指定选项的作用域 (PRIVATE, INTERFACE, PUBLIC)。
  • 优先使用生成器表达式 ($<...>) 来处理平台、编译器、配置等差异。
  • 对于指定要链接的库文件或 CMake 目标,始终使用 target_link_libraries()。
  • 查阅你所使用的编译器/链接器的文档,了解可用的选项及其确切含义。
  • 注意 CMake 版本要求 (>= 3.13)。

更多推荐