CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>¶
在 3.24 版本中添加。
当使用 LINK_LIBRARY 生成器表达式,且目标的链接语言为 <LANG> 时,该变量定义了如何为指定的 <FEATURE> 链接库或框架。要使此变量生效,必须将关联的 CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED 变量设置为 true。
对于独立于链接语言的特性,应改为定义 CMAKE_LINK_LIBRARY_USING_<FEATURE> 变量。
功能名称区分大小写,并且只能包含字母、数字和下划线。所有大写字母的功能名称保留给 CMake 自身的内置功能(请参阅下面的 预定义功能)。
特性的某些行为方面可以通过 CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES 和 CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES 变量来定义。
功能定义¶
库特性定义是一个包含一个或三个元素的列表。
[<PREFIX>] <LIBRARY_EXPRESSION> [<SUFFIX>]
当指定了 <PREFIX> 和 <SUFFIX> 时,它们分别位于 LINK_LIBRARY 表达式中指定的所有库列表的前面和后面,而不是针对每个库项单独添加。由于不能保证指定的库列表会始终保持在一起,因此如果 CMake 为了满足其他约束而重组了库列表,<PREFIX> 和 <SUFFIX> 可能会出现多次。这意味着像 GNU ld 链接器所支持的 --start-group 和 --end-group 这样的结构不能以这种方式使用。对于此类结构,应改为使用 LINK_GROUP 生成器表达式。
<LIBRARY_EXPRESSION> 用于为每个库在链接器命令行上构造相应片段指定模式。表达式中可以使用以下占位符:
<LIBRARY>对于 CMake 目标,会扩展为库的完整路径;否则会扩展为基于平台的特定值(在 Windows 上与<LINK_ITEM>相同,在其他平台上为库的基本名称)。<LINK_ITEM>会扩展为库在链接器命令行上通常的链接方式。<LIB_ITEM>对于 CMake 目标,会扩展为库的完整路径;否则扩展为<LIBRARY_EXPRESSION>中精确指定的项本身。
除上述内容外,还可以为路径(CMake 目标和以文件路径指定的外部库)使用一种模式,为仅按名称指定的其他项使用另一种模式。可以使用 PATH{} 和 NAME{} 包装器分别提供这两种情况的扩展。使用包装器时,两者必须同时存在。例如:
set(CMAKE_LINK_LIBRARY_USING_weak_library
"PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}"
)
对于该变量的所有三个元素(<PREFIX>、<LIBRARY_EXPRESSION> 和 <SUFFIX>),都可以使用 LINKER: 前缀。
每个编译器驱动程序都有其自己的将选项传递给链接器工具的语法。LINKER: 前缀和 , 分隔符可用于以可移植的方式指定传递给链接器工具的选项。LINKER: 会被替换为相应的驱动程序选项,, 会被替换为相应的驱动程序分隔符。驱动程序前缀和分隔符由 CMAKE_<LANG>_LINKER_WRAPPER_FLAG 和 CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP 变量的值给出。
例如,对于 Clang,"LINKER:-z,defs" 会变成 -Xlinker -z -Xlinker defs;对于 GNU GCC,则会变成 -Wl,-z,defs。
LINKER: 前缀可以作为 SHELL: 前缀表达式的一部分指定。
LINKER: 前缀支持使用 SHELL: 前缀和空格作为分隔符来指定参数,作为一种替代语法。前面的示例将变为 "LINKER:SHELL:-z defs"。
注意
不支持在 LINKER: 前缀的开头以外的任何位置指定 SHELL: 前缀。
示例¶
加载整个静态库¶
一个常见的需求是防止链接器丢弃静态库中的任何符号。不同的链接器使用不同的语法来实现这一点。以下示例展示了如何为某些链接器实现这一点。注意,这仅用于演示目的。项目应改为使用内置的 WHOLE_ARCHIVE 特性(参见 预定义特性),它提供了更完整且更稳健的实现。
set(CMAKE_C_LINK_LIBRARY_USING_load_archive_SUPPORTED TRUE)
if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_C_LINK_LIBRARY_USING_load_archive "-force_load <LIB_ITEM>")
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CMAKE_C_LINK_LIBRARY_USING_load_archive
"LINKER:--push-state,--whole-archive"
"<LINK_ITEM>"
"LINKER:--pop-state"
)
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_C_LINK_LIBRARY_USING_load_archive "/WHOLEARCHIVE:<LIBRARY>")
else()
# feature not yet supported for the other environments
set(CMAKE_C_LINK_LIBRARY_USING_load_archive_SUPPORTED FALSE)
endif()
add_library(lib1 STATIC ...)
add_library(lib2 SHARED ...)
if(CMAKE_C_LINK_LIBRARY_USING_load_archive_SUPPORTED)
# The -force_load Apple linker option requires a file name
set(external_lib
"$<IF:$<LINK_LANG_AND_ID:C,AppleClang>,libexternal.a,external>"
)
target_link_libraries(lib2 PRIVATE
"$<LINK_LIBRARY:load_archive,lib1,${external_lib}>"
)
else()
target_link_libraries(lib2 PRIVATE lib1 external)
endif()
CMake 将生成以下链接表达式:
AppleClang:-force_load /path/to/lib1.a -force_load libexternal.aGNU:-Wl,--push-state,--whole-archive /path/to/lib1.a -lexternal -Wl,--pop-stateMSVC:/WHOLEARCHIVE:/path/to/lib1.lib /WHOLEARCHIVE:external.lib
将库链接为弱链接¶
在 macOS 上,可以将库以弱模式链接(库及其所有引用都被标记为弱导入)。对于通过文件路径指定的库和通过名称指定的库,必须使用不同的标志。此约束可以使用 PATH{} 和 NAME{} 包装器解决。同样,以下示例展示了如何为某些链接器实现这一点,但这仅用于演示目的。项目应改为使用内置的 WEAK_FRAMEWORK 或 WEAK_LIBRARY 特性(参见 预定义特性),它们提供了更完整且更稳健的实现。
if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_LINK_LIBRARY_USING_weak_library
"PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}"
)
set(CMAKE_LINK_LIBRARY_USING_weak_library_SUPPORTED TRUE)
endif()
add_library(lib SHARED ...)
add_executable(main ...)
if(CMAKE_LINK_LIBRARY_USING_weak_library_SUPPORTED)
target_link_libraries(main PRIVATE "$<LINK_LIBRARY:weak_library,lib,external>")
else()
target_link_libraries(main PRIVATE lib external)
endif()
当使用 AppleClang 工具链链接 main 时,CMake 将生成以下链接器命令行片段:
-weak_library /path/to/lib -Xlinker -weak-lexternal.
预定义功能¶
以下内置库特性由 CMake 预定义:
DEFAULT此功能对应于标准链接,本质上等同于不使用任何功能。它通常仅与
LINK_LIBRARY_OVERRIDE和LINK_LIBRARY_OVERRIDE_<LIBRARY>目标属性一起使用。WHOLE_ARCHIVE强制包含静态库的所有成员(当链接为 可执行文件、共享库 和 模块库 的依赖项时)。此特性仅在以下平台支持,并存在标注的限制。
Linux。
所有 BSD 变体。
SunOS。
所有 Apple 变体。库必须指定为 CMake 目标名称、库文件名(例如
libfoo.a)或库文件路径(例如/path/to/libfoo.a)。由于 Apple 链接器的限制,它不能指定为纯库名称,例如foo(其中foo不是 CMake 目标)。Windows。使用 MSVC 或类似 MSVC 工具链时,MSVC 版本必须大于 1900。
Cygwin。
MSYS。
FRAMEWORK此选项告诉链接器使用
-framework链接器选项搜索指定的框架。它只能在 Apple 平台上使用,并且只能与理解该选项的链接器一起使用(即 Xcode 提供的链接器,或与之兼容的链接器)。框架可以指定为 CMake 框架目标、裸框架名称或文件路径。如果给定一个目标,则该目标必须具有
FRAMEWORK目标属性设置为 true。对于文件路径,如果它包含目录部分,则该目录将被添加为框架搜索路径。add_library(lib SHARED ...) target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:FRAMEWORK,/path/to/my_framework>") # The constructed linker command line will contain: # -F/path/to -framework my_framework
文件路径必须符合以下模式之一(
*是通配符,可选部分显示为[...]):[/path/to/]FwName[.framework][/path/to/]FwName.framework/FwName[suffix][/path/to/]FwName.framework/Versions/*/FwName[suffix]
请注意,CMake 会识别并自动处理框架目标,即使不使用
$<LINK_LIBRARY:FRAMEWORK,...>表达式。如果项目希望明确说明,仍然可以将该生成器表达式与 CMake 目标一起使用,但这并非必需。使用或不使用生成器表达式时,链接器命令行可能会有一些差异,但最终结果应是一样的。另一方面,如果给出了文件路径,CMake 会自动识别某些路径,但不能识别所有情况。项目可能需要对文件路径使用$<LINK_LIBRARY:FRAMEWORK,...>,以便明确预期的行为。版本 3.25 中添加:
FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>目标属性以及框架库名称的suffix现在受FRAMEWORK功能支持。NEEDED_FRAMEWORK这类似于
FRAMEWORK功能,除了它强制链接器链接框架,即使没有使用它的任何符号。它使用-needed_framework选项,并且具有与FRAMEWORK相同的链接器约束。REEXPORT_FRAMEWORK这类似于
FRAMEWORK功能,除了它告诉链接器框架应该可供链接到正在创建的库的客户端使用。它使用-reexport_framework选项,并且具有与FRAMEWORK相同的链接器约束。WEAK_FRAMEWORK这类似于
FRAMEWORK功能,除了它强制链接器将框架及其所有引用标记为弱导入。它使用-weak_framework选项,并且具有与FRAMEWORK相同的链接器约束。NEEDED_LIBRARY这类似于
NEEDED_FRAMEWORK功能,除了它用于非框架目标或库(仅限 Apple 平台)。它使用适当的-needed_library或-needed-l选项,并且具有与NEEDED_FRAMEWORK相同的链接器约束。REEXPORT_LIBRARY这类似于
REEXPORT_FRAMEWORK功能,除了它用于非框架目标或库(仅限 Apple 平台)。它使用适当的-reexport_library或-reexport-l选项,并且具有与REEXPORT_FRAMEWORK相同的链接器约束。WEAK_LIBRARY这类似于
WEAK_FRAMEWORK功能,除了它用于非框架目标或库(仅限 Apple 平台)。它使用适当的-weak_library或-weak-l选项,并且具有与WEAK_FRAMEWORK相同的链接器约束。