CMake中target_link_libraries的使用

2023-05-16

      CMake中的target_link_libraries命令用于指定链接给定目标和/或其依赖项时要使用的库或标志来自链接库目标的使用要求将被传播(propagated)。目标依赖项的使用要求会影响其自身源代码的编译。其格式如下:

target_link_libraries(<target> ... <item>... ...) # general form
target_link_libraries(<target>
                      <PRIVATE|PUBLIC|INTERFACE> <item>...
                     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) # Libraries for a Target and/or its Dependents
target_link_libraries(<target> <item>...) # Libraries for both a Target and its Dependents
target_link_libraries(<target>
                      <LINK_PRIVATE|LINK_PUBLIC> <lib>...
                     [<LINK_PRIVATE|LINK_PUBLIC> <lib>...]...) # Libraries for a Target and/or its Dependents (Legacy)
target_link_libraries(<target> LINK_INTERFACE_LIBRARIES <item>...) # Libraries for Dependents Only (Legacy)

      1.general form: 命名的<target>必须由诸如add_executable或add_library之类的命令创建,并且不能是ALIAS target。如果策略CMP0079未设置为NEW,则target必须已在当前目录中创建。重复调用相同的<target>会按调用顺序追加项目(Repeated calls for the same <target> append items in the order called)。
      <target>不必在与target_link_libraries调用相同的目录中定义
      每个<item>可能是:
      (1).A library target name:生成的链接行(link line)将具有与target关联的可链接库文件的完整路径。如果库文件发生更改,构建系统将具有重新链接<target>的依赖性。
      命名的target必须由项目中的add_library命令创建或作为IMPORTED库创建。如果它是在项目中创建的,则会在构建系统中自动添加一个排序依赖项,以确保指定的库目标在<target>链接之前是最新的。
      如果导入的库设置了IMPORTED_NO_SONAME目标属性,CMake可能会要求链接器搜索库而不是使用完整路径(例如/usr/lib/libfoo.so变为-lfoo)。
      目标工件(target's artifact)的完整路径将自动为shell加引号/转义(quoted/escaped).
      (2).A full path to a library file:生成的链接行通常会保留文件的完整路径。如果库文件更改,构建系统将具有重新链接<target>的依赖性。
      在某些情况下,CMake可能会询问链接器搜索的库(例如/usr/lib/libfoo.so变为-lfoo),例如,当检测到共享库没有SONAME字段时。
      如果库文件在macOS框架(framework)中,框架的Headers目录也会作为使用需求处理。
      在VS2010及更高版本的Visual Studio Generators上,以.targets结尾的库文件将被视为MSBuild目标文件并导入到生成的项目文件中。
      库文件的完整路径将自动为shell加引号/转义(quoted/escaped).
      (3).A plain library name:生成的链接行将要求链接器搜索库(例如,foo变为-lfoo或foo.lib).
      库名称/标志被视为命令行字符串片段,将在没有额外引号或转义的情况下使用。
      (4).A link flag:以-开头但不是-l或-framework的项目名称被视为链接器标志。
      链接标志被视为命令行字符串片段,将在没有额外引号或转义的情况下使用。
      (5).A generator expression:$<...>生成器表达式可以评估上述任何items或以分号分割的列表。如果...包含任何;字符,则必须使用显式引用的参数"$<...>",以便此命令将其作为单个<item>接收。
      此外,生成器表达式可以用作上述任何items的片段,例如foo$<1:_d>
      注意:生成器表达式将不会用于策略CMP0003或策略CMP0004的OLD处理中。
      (6).A debug, optimized, or general keyword immediately followed by another <item>:这样的关键字后面的item将仅用于相应的构建配置。debug关键字对应于Debug配置。optimized关键字对应于所有其它配置。general关键字对应所有配置,完全是可选的。通过创建和链接到IMPORTED库目标,可以为每个配置规则实现更高的粒度。这些关键字由该命令立即解释,因此在由生成器表达式生成时,它们没有特殊含义。
      包含::的项目,如Foo::Bar,被假定为IMPORTED或ALIAS库目标名称,如果不存在这样的目标,将导致错误。

cmake_policy(GET CMP0079 var) # 3.22
message("var: ${var}") # var: NEW

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(add_shared SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp) # 将会在build目录下生成libadd_shared.so

add_executable(sample_add ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp)
target_link_libraries(sample_add add_shared) # 若注释掉此句,则会报 error: sample_add.cpp:(.text+0x25): undefined reference to 'add(int, int)'

      2.Libraries for a Target and/or its Dependents:PUBLIC, PRIVATE和INTERFACE关键字可用于在一个命令中指定link dependencies and the link interface。
      PUBLIC之后的库和目标链接到链接接口,并成为链接接口的一部分。PRIVATE之后的库和目标被链接到,但不是链接接口的一部分。INTERFACE之后的库被附加到链接接口,不用于链接<target>.

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(add_shared SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp) # 将会在build目录下生成libadd_shared.so

add_executable(sample_add ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp)
target_link_libraries(sample_add PRIVATE add_shared) # 也可以为PUBLIC;但不能为INTERFACE,若为INTERFACE,则会报error: sample_add.cpp:(.text+0x25): undefined reference to `add(int, int)'

      3.Libraries for both a Target and its Dependents:默认情况下,库依赖使用此签名是可传递(transitive)的。当这个target链接到另一个target时,链接到该target的库也将出现在另一个target的链接行上。这个可传递的"链接接口"存储在INTERFACE_LINK_LIBRARIES target属性中,可以通过直接设置该属性来覆盖。当CMP0022未设置为NEW时,传递链接是内置的,但可能会被LINK_INTERFACE_LIBRARIES属性覆盖。

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(add_shared SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp) # 将会在build目录下生成libadd_shared.so
add_library(subtraction_shared SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/subtraction.cpp)

target_link_libraries(subtraction_shared INTERFACE add_shared) # 也可以为PUBLIC;但不可以为PRIVATE,若为PRIVATE,则会报error:sample_add.cpp:(.text+0x25): undefined reference to `add(int, int)'

add_executable(sample_add ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp)
target_link_libraries(sample_add subtraction_shared) # 注意:此处sample_add链接的是subtraction_shared而不是add_shared
                                                        # 可查看build/CMakeFiles/sample_add.dir/link.txt文件

      4.Libraries for a Target and/or its Dependents (Legacy):该签名仅用于兼容性,请改用PUBLIC或PRIVATE关键字。

      5.Libraries for Dependents Only (Legacy):该签名仅用于兼容性,请改用INTERFACE模式。

      6.Linking Object Libraries:Object Libraries可用作target_link_libraries的<target>的(第一个)参数,以指定其源对其他库的依赖。
      OBJECT library类型定义了编译给定源文件所产生的目标文件的非归档集合(The OBJECT library type defines a non-archival collection of object files resulting from compiling the given source files).通过使用语法$<TARGET_OBJECTS:name>,目标文件集合(object files collection)可以用作其它target的源输入。

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(Add SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp)
# target_compile_definitions(Add PUBLIC Add) # 有无此句好像都能执行

# compiles subtraction.cpp(obj.cpp) with -DAdd -DOBJ
add_library(Obj OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/source/subtraction.cpp)
# target_compile_definitions(Obj PUBLIC Obj)
target_link_libraries(Obj PUBLIC Add)

# compiles multipy.cpp with -DAdd -DOBJ
add_library(Multipy SHARED ${CMAKE_CURRENT_SOURCE_DIR}/source/multipy.cpp)
target_link_libraries(Multipy PUBLIC Obj)

# compiles sample_add.cpp with -DAdd -DObj and links executable main to Multipy and Add
add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp)
target_link_libraries(main Multipy)

      7.Linking Object Libraries via $<TARGET_OBJECTS>:与object library关联的object file可以由$<TARGET_OBJECTS>生成器表达式引用(referenced).这样的object file放在所有库之前的链接行上,不管它们的相对顺序如何。
      这也可以通过静态库传递(transitively)。

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(Obj OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/source/subtraction.cpp)
# target_compile_definitions(Obj PUBLIC Obj) # 有无此句好像都能执行

add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_subtraction.cpp)
# links executable main with object files from sample_subtraction.cpp and subtraction.cpp followed by the pthread and dl libraries
target_link_libraries(main PRIVATE pthread $<TARGET_OBJECTS:Obj> dl)

add_library(iface_obj INTERFACE)
target_link_libraries(iface_obj INTERFACE Obj $<TARGET_OBJECTS:Obj>)

# compiles sample_subtraction.cpp with -DObj and links executable main2 with object files from sample_subtraction.cpp and subtraction.cpp
add_executable(main2 ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_subtraction.cpp)
target_link_libraries(main2 PRIVATE iface_obj)

# this also works transitively through a static library.
add_library(add STATIC ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp)
target_link_libraries(add PRIVATE iface_obj)

add_executable(main3 ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_subtraction.cpp)
target_link_libraries(main3 PRIVATE add)

      8.Cyclic Dependencies of Static Libraries(静态库的循环依赖):库依赖图通常是非循环的(DAG),但在相互依赖的STATIC库的情况下,CMake允许该图包含循环(强连接组件)。当另一个target链接到其中一个库时,CMake会重复整个连接的组件。
      虽然一次重复通常就足够了,但病理对象文件和符号排列(pathological object file and symbol arrangements)可能需要更多。可以通过使用LINK_INTERFACE_MULTIPLICITY target属性或在上次target_link_libraries调用中手动重复该组件来处理此类情况。然而,如果两个存档(archives)真的如此相互依赖,那么应该使用Object Libraries将它们组合成一个单一的存档。

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

add_library(add STATIC ${CMAKE_CURRENT_SOURCE_DIR}/source/add.cpp)
add_library(subtraction STATIC ${CMAKE_CURRENT_SOURCE_DIR}/source/subtraction.cpp)
target_link_libraries(add subtraction)
target_link_libraries(subtraction add)

# links main to add subtraction add subtraction
add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/samples/sample_add.cpp)
target_link_libraries(main subtraction)

      执行测试代码需要多个文件

      build.sh内容如下:

#! /bin/bash

# supported input parameters(cmake commands)
params=(function macro cmake_parse_arguments \
		find_library find_path find_file find_program find_package \
		cmake_policy cmake_minimum_required project include \
		string list set foreach message option if while return \
		math file configure_file \
		include_directories add_executable add_library target_link_libraries install)

usage()
{
	echo "Error: $0 needs to have an input parameter"

	echo "supported input parameters:"
	for param in ${params[@]}; do
		echo "  $0 ${param}"
	done

	exit -1
}

if [ $# != 1 ]; then
	usage
fi

flag=0
for param in ${params[@]}; do
	if [ $1 == ${param} ]; then
		flag=1
		break
	fi
done

if [ ${flag} == 0 ]; then
	echo "Error: parameter \"$1\" is not supported"
	usage
	exit -1
fi

if [[ ! -d "build" ]]; then
	mkdir build
	cd build
else
	cd build
fi

echo "==== test $1 ===="

# test_set.cmake: cmake -DTEST_CMAKE_FEATURE=$1 --log-level=verbose ..
# test_option.cmake: cmake -DTEST_CMAKE_FEATURE=$1 -DBUILD_PYTORCH=ON ..
cmake -DTEST_CMAKE_FEATURE=$1 ..
# It can be executed directly on the terminal, no need to execute build.sh, for example: cmake -P test_set.cmake
make
# make install # only used in cmake files with install command

      CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.22)
project(cmake_feature_usage)

message("#### current cmake version: ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}")
include(test_${TEST_CMAKE_FEATURE}.cmake)
message("==== test finish ====")

      test_target_link_libraries.cmake内容为上面的所有测试代码段。

      另外还包括三个目录:include,source,samples,它们都是非常简单的实现,仅用于测试,如下:

      可能的执行结果如下图所示: 

      GitHub: https://github.com/fengbingchun/Linux_Code_Test

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake中target_link_libraries的使用 的相关文章

随机推荐