cmake-buildsystem
介绍
一个CMake构建系统由很多逻辑上的目标(target)组成。目标包括可执行程序、库、伪目标。通过在构建系统中设定目标依赖关系来确定目标的构建顺序以及当发生更改时哪些部分需要重新构建。
二进制目标
包括可执行程序和库。分别通过add_executable()
和add_library()
命令来定义。最终生成的二进制文件名会被附加合适的前缀(PREFIX
)、后缀(SUFFIX
)和扩展名。通过target_link_libraries()
命令来指定二进制目标之间的依赖关系。
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
archive
是一个静态库,是一个由archive.cpp
、zip.cpp
和lzma.cpp
编译得到的目标文件组成的归档。
zipapp
是一个可执行程序,它通过zipapp.cpp
编译链接而成。在链接zipapp
时,会链入archive
静态库。
可执行程序
使用add_executable()
命令定义一个可执行程序。
add_executable(mytool mytool.cpp)
在某些命令中可以使用可执行目标作为待执行的命令,比如add_custom_command()
,这会产生一个依赖规则,构建系统会在执行该程序之前先构建它。
库
普通库
使用add_library()
命令定义一个静态库(缺省情况下),在使用这个命令时也可以明确指定库类型:
add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
通过启用BUILD_SHARED_LIBS
变量,可以改变add_library()
缺省的库类型为共享库。
整体而言,在构建系统的上下文中,一个特定的库是共享的还是静态的很大程度上是无关紧要的。命令、依赖关系以及其他一些API在工作的时候不理会库的类型。如果一个库没有导出任何非托管符号(比如Windows资源DLL,C++/CLI DLL),这个库不能指定为共享库,CMake中共享库至少需要导出一个符号。
还有一种库类型MODULE
不太一样,它不能被用于链接,即它不能用在target_link_libraries()
命令中,它是一种通过运行时技术加载的插件。
add_library(archive MODULE 7z.cpp)
Apple框架
使用FRAMEWORK
目标属性将一个共享库标识为macOS/iOS Framework Bundle。可通过MACOSX_FRAMEWORK_IDENTIFIER
来设置CFBundleIdentifier
,它唯一的标识了这个 Framework Bundle。
add_library(MyFramework SHARED MyFramework.cpp)
set_target_properties(MyFramework PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework
)
目标文件集
目标文件集定义了一个从源文件编译得到的目标文件的非归档集合。目标文件集可被当做源文件输入其他目标:
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)
add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)
当链接或归档其他目标的时候,将编译目标文件集对应的源文件,然后作为输入。
此外,目标文件集也可被链接入其他目标:
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)
add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)
当连接或归档其他目标的时候,将把目标文件集编译的目标文件直接链接进来。在编译其他目标的时候会采用目标文件集的使用要求,并且也会传递目标文件集的使用要求。
目标文件集不可以被当做TARGET
用在add_custom_command(TARGET)
命令中。不过,目标文件列表可以通过$<TARGET_OBJECTS:objlib>
的形式用在add_custom_command(OUTPUT)
和file(GENERATE)
命令中。
watermark: this document is translated by xianchen.peng
构建规范(Specification)和使用要求
使用target_include_directories()
、target_compile_definitions()
和target_compile_options()
命令指定二进制目标的构建规范和使用要求。这些命令会分别往INCLUDE_DIRECTORIES
、COMPILE_DEFINITIONS
和COMPILE_OPTIONS
目标属性和INTERFACE_INCLUDE_DIRECTORIES
、INTERFACE_COMPILE_DEFINITIONS
和INTERFACE_COMPILE_OPTIONS
目标属性追加值。
这些命令都有PRIVATE
、PUBLIC
和INTERFACE
三种模式。PRIVATE
模式仅向非INTERFACE_
的目标属性追加值,INTERFACE
模式仅向INTERFACE_
目标属性追加值,PUBLIC
模式同时向两种目标属性追加。调用这些命令的时候可以同时指定多种模式:
target_compile_definitions(archive
PRIVATE BUILDING_WITH_LZMA
INTERFACE USING_ARCHIVE_LIB
)
使用要求这种机制不仅是为了方便传递COMPILE_OPTIONS
或COMPILE_DEFINITIONS
等设计的。使用要求中的内容是必需满足的条件,而不仅仅是建议或为了方便。
参阅make-packages(7)
手册中的创建可重定位包
章节,查看关于创建分发包时必须额外关心的使用要求的讨论。
目标属性
目标的INCLUDE_DIRECTORIES
、COMPILE_DEFINITIONS
和COMPILE_OPTIONS
属性会在编译目标的源文件时使用。
INCLUDE_DIRECTORIES
中的项会被加上-I
或-isystem
前缀,然后以原顺序添加到编译命令中。
COMPILE_DEFINITIONS
中的项会被加上-D
或/D
前缀,然后以未定义顺序添加到编译命令中。对于SHARED
或MODULE
库类型,DEFINE_SYMBOL
目标属性也会被当做宏定义添加到编译命令中。
COMPILE_OPTIONS
中的项会经过转义,然后以原顺序添加到编译命令中。某些编译选项有单独的处理,比如POSITION_INDEPENDENT_CODE
目标属性。
INTERFACE_INCLUDE_DIRECTORIES
、INTERFACE_COMPILE_DEFINITIONS
和INTERFACE_COMPILE_OPTIONS
目标属性的内容将会作为使用要求,他们指定了消费者正确编译、链接本目标所须满足的规范。一个目标通过target_link_libraries()
依赖的目标的每个INTERFACE_
属性都被消费:
set(srcs archive.cpp zip.cpp)
if (LZMA_FOUND)
list(APPEND srcs lzma.cpp)
endif()
add_library(archive SHARED ${srcs})
if (LZMA_FOUND)
# The archive library sources are compiled with -DBUILDING_WITH_LZMA
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA)
endif()
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
add_executable(consumer)
# Link consumer to archive and consume its usage requirements. The consumer
# executable sources are compiled with -DUSING_ARCHIVE_LIB.
target_link_libraries(consumer archive)
通常情况下,需要将源码目录和对应的构建目录添加到INCLUDE_DIRECTORIES
目标属性。CMAKE_INCLUDE_CURRENT_DIR
变量可用于方便的添加对应的目录到INCLUDE_DIRECTORIES
中。CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
可用于添加对应的目录到INTERFACE_INCLUDE_DIRECTORIES
。这使得在不同目录中通过target_link_libraries()
命令使用目标更方便。
传递使用要求
目标的使用要求可以通过依赖者层层传递,在target_link_libraries()
命令中使用PRIVATE
、INTERFACE
和PUBLIC
关键词来控制传递。
add_library(archive archive.cpp)
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
add_library(serialization serialization.cpp)
target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB)
add_library(archiveExtras extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)
target_link_libraries(archiveExtras PRIVATE serialization)
# archiveExtras is compiled with -DUSING_ARCHIVE_LIB
# and -DUSING_SERIALIZATION_LIB
add_executable(consumer consumer.cpp)
# consumer is compiled with -DUSING_ARCHIVE_LIB
target_link_libraries(consumer archiveExtras)
因为archive
是archiveExtras
的PUBLIC
依赖,所以,它把archiveExtras
的使用要求传递到了consumer
。因为serialization
是archiveExtras
的PRIVATE
依赖,所以它没有把archiveExtras
的使用要求传递到consumer
。
通常情况下,如果一个库仅在实现中使用了依赖项,而不是头文件中,在target_link_libraries()
中要使用PRIVATE
指定依赖。如果一个库同时还在头文件中使用了依赖项,那么需要使用PUBLIC
指定依赖。如果一个库仅仅在头文件中使用了依赖项,那么需要使用INTERFACE
指定依赖。可以在target_link_libraries()
中同时指定多种依赖类型:
target_link_libraries(archiveExtras
PUBLIC archive
PRIVATE serialization
)
通过读取依赖目标中的INTERFACE_
目标属性的值,然后附加到当前目标的非INTERFACE_
目标属性上实现使用要求的传递。例如:读取依赖目标的INTERFACE_INCLUDE_DIRECTORIES
目标属性的值,附加到当前目标的INCLUDE_DIRECTORIES
目标属性。有些情况下target_link_libraries()
中依赖项的顺序是紧密相关的,其中的顺序可能导致无法正确编译,这时,使用合适的命令直接设置目标属性可以解决顺序问题。
例如:如果一个目标链接的库必须按lib1
、lib2
、lib3
的顺序指定,但是头文件却必须按lib3
、lib1
、lib2
指定:
target_link_libraries(myExe lib1 lib2 lib3)
target_include_directories(myExe
PRIVATE $<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>)
当使用install(EXPORT)
将目标导出为安装时,在为它指定使用要求的时候,必须关注一些事情。请参阅“创建包”获取更多信息。
兼容的接口属性
有些目标属性需要在目标和每个依赖项的接口之间兼容。例如,POSITION_INDEPENDENT_CODE
目标属性指定了一个目标是否编译为位置无关代码。另一个目标可能指定了INTERFACE_POSITION_INDENPENDENT_CODE
使用要求来通知那个消费者必须编译为位置无关代码。
add_executable(exe1 exe1.cpp)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE ON)
add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1)
上面这个例子中,exe1
和exe2
都会被编译为位置无关代码。lib1
也会编译为位置无关代码,因为SHARED
库的缺省设定就是位置无关代码。如果依赖项中的位置无关代码选项有冲突,cmake(1)
将报出一个使用要求不兼容的诊断错误:
add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(lib2 SHARED lib2.cpp)
set_property(TARGET lib2 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE OFF)
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1 lib2)
上面这个例子中,lib1
的INTERFACE_POSITION_INDEPENDENT_CODE
使用要求与exe1
的POSITION_INDEPENDENT_CODE
目标属性不兼容。lib1
要求消费者使用POSITION_INDENPENDENT_CODE
目标属性编译,但是exe1
指定了不要编译为位置无关代码,所以将会报出一个诊断错误。
lib1
和lib2
的使用要求不兼容,其中一个要求消费者编译为位置无关代码,但是另一个要求消费者不要编译为位置无关代码。exe2
同时链接了两个库,发生了冲突,所以将会报出一个诊断错误。
为了保持兼容,如果目标设置了POSITION_INDEPENDENT_CODE
属性,必须和所有依赖项传递过来的INTERFACE_POSITION_INDEPENDENT_CODE
属性相同。
可以通过设置COMPATIBLE_INTERFACE_BOOL
目标属性,把这种接口要求的兼容扩展到其他的属性。每个在其中指定的属性都必须在消费者和依赖者对应的INTERFACE_
属性之间兼容:
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CUSTOM_PROP ON)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_BOOL CUSTOM_PROP
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CUSTOM_PROP OFF)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # CUSTOM_PROP will be ON
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
非布尔类型的属性也可以参与“接口兼容性”检测。在COMPATIBLE_INTERFACE_STRING
属性中指定的属性,必须在当前目标中未指定或者与依赖项中传递过来的属性值相同。这个机制可以确保一个目标不会通过依赖传递链接同一个库的多个不兼容版本:
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING LIB_VERSION
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2"
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
在COMPATIBLE_INTERFACE_NUMBER_MAX
目标属性中指定的属性,将会以数字来赋值,该属性所有值中的最大值将会被采用:
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000)
add_executable(exe1 exe1.cpp)
# CONTAINER_SIZE_REQUIRED will be "200"
target_link_libraries(exe1 lib1Version2)
add_executable(exe2 exe2.cpp)
# CONTAINER_SIZE_REQUIRED will be "1000"
target_link_libraries(exe2 lib1Version2 lib1Version3)
同样,COMPATIBLE_INTERFACE_NUMBER_MIN
可用来计算一个属性在依赖传递中的最小值。
每个已完成计算的“兼容”属性值可以在消费者中被以生成器表达式的方式读取。
注意,对于每个依赖者,“兼容接口属性”中指定的属性必须不能与任何其他“兼容接口属性”中指定的交叉。
属性源头调试
因为构建规范是通过依赖关系确定的,如果无法定位创建目标的代码和设置构建规范的代码,会使得代码很难推理。cmake(1)
提供了打印属性值源头的功能。CMAKE_DEBUG_TARGET_PROPERTIES
变量的文档中列出了可进行调试属性:
set(CMAKE_DEBUG_TARGET_PROPERTIES
INCLUDE_DIRECTORIES
COMPILE_DEFINITIONS
POSITION_INDEPENDENT_CODE
CONTAINER_SIZE_REQUIRED
LIB_VERSION
)
add_executable(exe1 exe1.cpp)
对于在COMPATIBLE_INTERFACE_BOOL
和COMPATIBLE_INTERFACE_STRING
中指定的属性,调试输出中将会显示那个目标负责设置属性,那个依赖项同样也定义了该属性。对于在COMPATIBLE_INTERFACE_NUMBER_MAX
和COMPATIBLE_INTERFACE_NUMBER_MIN
中指定的属性,调试输出中将会显示每个依赖项中该属性的值,以及该值是否更新了最大(小)值。
生成器表达式指定构建规范
可以使用生成器表达式来指定构建规范。例如,可以通过读取TARGET_PROPERTY
表达式的值来获取一个计算好的兼容的属性值:
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY
INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2)
target_compile_definitions(exe1 PRIVATE
CONTAINER_SIZE=$<TARGET_PROPERTY:CONTAINER_SIZE_REQUIRED>
)
在这个例子中,exe1
源文件将会使用-DCONTAINER_SIZE=200
编译。
可以很方便地使用CONFIG
生成器表达式来设置基于配置的构建规范。
target_compile_definitions(exe1 PRIVATE
$<$<CONFIG:Debug>:DEBUG_BUILD>
)
The CONFIG
parameter is compared case-insensitively with the configuration being built. In the presence of IMPORTED
targets, the content of MAP_IMPORTED_CONFIG_DEBUG
is also accounted for by this expression.
Some buildsystems generated by cmake(1)
have a predetermined build-configuration set in the CMAKE_BUILD_TYPE
variable. The buildsystem for the IDEs such as Visual Studio and Xcode are generated independent of the build-configuration, and the actual build configuration is not known until build-time. Therefore, code such as
string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
if (_type STREQUAL debug)
target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()
may appear to work for Makefile Generators and Ninja
generators, but is not portable to IDE generators. Additionally, the IMPORTED
configuration-mappings are not accounted for with code like this, so it should be avoided.
The unary TARGET_PROPERTY
generator expression and the TARGET_POLICY
generator expression are evaluated with the consuming target context. This means that a usage requirement specification may be evaluated differently based on the consumer:
add_library(lib1 lib1.cpp)
target_compile_definitions(lib1 INTERFACE
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LIB1_WITH_EXE>
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:LIB1_WITH_SHARED_LIB>
$<$<TARGET_POLICY:CMP0041>:CONSUMER_CMP0041_NEW>
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)
cmake_policy(SET CMP0041 NEW)
add_library(shared_lib shared_lib.cpp)
target_link_libraries(shared_lib lib1)
The exe1
executable will be compiled with -DLIB1_WITH_EXE
, while the shared_lib
shared library will be compiled with -DLIB1_WITH_SHARED_LIB
and -DCONSUMER_CMP0041_NEW
, because policy CMP0041
is NEW
at the point where the shared_lib
target is created.
The BUILD_INTERFACE
expression wraps requirements which are only used when consumed from a target in the same buildsystem, or when consumed from a target exported to the build directory using the export()
command. The INSTALL_INTERFACE
expression wraps requirements which are only used when consumed from a target which has been installed and exported with the install(EXPORT)
command:
add_library(ClimbingStats climbingstats.cpp)
target_compile_definitions(ClimbingStats INTERFACE
$<BUILD_INTERFACE:ClimbingStats_FROM_BUILD_LOCATION>
$<INSTALL_INTERFACE:ClimbingStats_FROM_INSTALLED_LOCATION>
)
install(TARGETS ClimbingStats EXPORT libExport ${InstallArgs})
install(EXPORT libExport NAMESPACE Upstream::
DESTINATION lib/cmake/ClimbingStats)
export(EXPORT libExport NAMESPACE Upstream::)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 ClimbingStats)
In this case, the exe1
executable will be compiled with -DClimbingStats_FROM_BUILD_LOCATION
. The exporting commands generate IMPORTED
targets with either the INSTALL_INTERFACE
or the BUILD_INTERFACE
omitted, and the *_INTERFACE
marker stripped away. A separate project consuming the ClimbingStats
package would contain:
find_package(ClimbingStats REQUIRED)
add_executable(Downstream main.cpp)
target_link_libraries(Downstream Upstream::ClimbingStats)
Depending on whether the ClimbingStats
package was used from the build location or the install location, the Downstream
target would be compiled with either -DClimbingStats_FROM_BUILD_LOCATION
or -DClimbingStats_FROM_INSTALL_LOCATION
. For more about packages and exporting see the cmake-packages(7)
manual.
头文件目录和使用要求
Include directories require some special consideration when specified as usage requirements and when used with generator expressions. The target_include_directories()
command accepts both relative and absolute include directories:
add_library(lib1 lib1.cpp)
target_include_directories(lib1 PRIVATE
/absolute/path
relative/path
)
Relative paths are interpreted relative to the source directory where the command appears. Relative paths are not allowed in the INTERFACE_INCLUDE_DIRECTORIES
of IMPORTED
targets.
In cases where a non-trivial generator expression is used, the INSTALL_PREFIX
expression may be used within the argument of an INSTALL_INTERFACE
expression. It is a replacement marker which expands to the installation prefix when imported by a consuming project.
Include directories usage requirements commonly differ between the build-tree and the install-tree. The BUILD_INTERFACE
and INSTALL_INTERFACE
generator expressions can be used to describe separate usage requirements based on the usage location. Relative paths are allowed within the INSTALL_INTERFACE
expression and are interpreted relative to the installation prefix. For example:
add_library(ClimbingStats climbingstats.cpp)
target_include_directories(ClimbingStats INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
$<INSTALL_INTERFACE:/absolute/path>
$<INSTALL_INTERFACE:relative/path>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/$<CONFIG>/generated>
)
Two convenience APIs are provided relating to include directories usage requirements. The CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
variable may be enabled, with an equivalent effect to:
set_property(TARGET tgt APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>
)
for each target affected. The convenience for installed targets is an INCLUDES DESTINATION
component with the install(TARGETS)
command:
install(TARGETS foo bar bat EXPORT tgts ${dest_args}
INCLUDES DESTINATION include
)
install(EXPORT tgts ${other_args})
install(FILES ${headers} DESTINATION include)
This is equivalent to appending ${CMAKE_INSTALL_PREFIX}/include
to the INTERFACE_INCLUDE_DIRECTORIES
of each of the installed IMPORTED
targets when generated by install(EXPORT)
.
When the INTERFACE_INCLUDE_DIRECTORIES
of an imported target is consumed, the entries in the property are treated as SYSTEM
include directories, as if they were listed in the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
of the dependency. This can result in omission of compiler warnings for headers found in those directories. This behavior for Imported Targets may be controlled by setting the NO_SYSTEM_FROM_IMPORTED
target property on the consumers of imported targets.
If a binary target is linked transitively to a macOS FRAMEWORK
, the Headers
directory of the framework is also treated as a usage requirement. This has the same effect as passing the framework directory as an include directory.
连接库和生成器表达式
Like build specifications, link libraries
may be specified with generator expression conditions. However, as consumption of usage requirements is based on collection from linked dependencies, there is an additional limitation that the link dependencies must form a “directed acyclic graph”. That is, if linking to a target is dependent on the value of a target property, that target property may not be dependent on the linked dependencies:
add_library(lib1 lib1.cpp)
add_library(lib2 lib2.cpp)
target_link_libraries(lib1 PUBLIC
$<$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>:lib2>
)
add_library(lib3 lib3.cpp)
set_property(TARGET lib3 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1 lib3)
As the value of the POSITION_INDEPENDENT_CODE
property of the exe1
target is dependent on the linked libraries (lib3
), and the edge of linking exe1
is determined by the same POSITION_INDEPENDENT_CODE
property, the dependency graph above contains a cycle. cmake(1)
issues a diagnostic in this case.
Output Artifacts
The buildsystem targets created by the add_library()
and add_executable()
commands create rules to create binary outputs. The exact output location of the binaries can only be determined at generate-time because it can depend on the build-configuration and the link-language of linked dependencies etc. TARGET_FILE
, TARGET_LINKER_FILE
and related expressions can be used to access the name and location of generated binaries. These expressions do not work for OBJECT
libraries however, as there is no single file generated by such libraries which is relevant to the expressions.
There are three kinds of output artifacts that may be build by targets as detailed in the following sections. Their classification differs between DLL platforms and non-DLL platforms. All Windows-based systems including Cygwin are DLL platforms.
Runtime Output Artifacts
A runtime output artifact of a buildsystem target may be:
- The executable file (e.g.
.exe
) of an executable target created by theadd_executable()
command. - On DLL platforms: the executable file (e.g.
.dll
) of a shared library target created by theadd_library()
command with theSHARED
option.
The RUNTIME_OUTPUT_DIRECTORY
and RUNTIME_OUTPUT_NAME
target properties may be used to control runtime output artifact locations and names in the build tree.
Library Output Artifacts
A library output artifact of a buildsystem target may be:
- The loadable module file (e.g.
.dll
or.so
) of a module library target created by theadd_library()
command with theMODULE
option. - On non-DLL platforms: the shared library file (e.g.
.so
or.dylib
) of a shared library target created by theadd_library()
command with theSHARED
option.
The LIBRARY_OUTPUT_DIRECTORY
and LIBRARY_OUTPUT_NAME
target properties may be used to control library output artifact locations and names in the build tree.
Archive Output Artifacts
An archive output artifact of a buildsystem target may be:
- The static library file (e.g.
.lib
or.a
) of a static library target created by theadd_library()
command with theSTATIC
option. - On DLL platforms: the import library file (e.g.
.lib
) of a shared library target created by theadd_library()
command with theSHARED
option. This file is only guaranteed to exist if the library exports at least one unmanaged symbol. - On DLL platforms: the import library file (e.g.
.lib
) of an executable target created by theadd_executable()
command when itsENABLE_EXPORTS
target property is set. - On AIX: the linker import file (e.g.
.imp
) of an executable target created by theadd_executable()
command when itsENABLE_EXPORTS
target property is set.
The ARCHIVE_OUTPUT_DIRECTORY
and ARCHIVE_OUTPUT_NAME
target properties may be used to control archive output artifact locations and names in the build tree.
Directory-Scoped Commands
The target_include_directories()
, target_compile_definitions()
and target_compile_options()
commands have an effect on only one target at a time. The commands add_compile_definitions()
, add_compile_options()
and include_directories()
have a similar function, but operate at directory scope instead of target scope for convenience.
伪目标
有些目标不代表构建系统的产物,只作为依赖本目标的外部目标的输入,伪目标不会生成的原生构建系统中体现。
可导入目标
IMPORTED
目标代表一个已经存在的依赖关系。通常这种目标是由上游包定义的,并且应当做不可修改的。当声明了一个IMPORTED
目标之后,可以通过target_compile_definitions()
、target_include_directories()
、target_compile_options()
和target_link_libraries()
命令调整其目标属性,就像调整其他目标的目标属性一样。
IMPORTED
目标和二进制目标一样拥有使用要求,比如INTERFACE_INCLUDE_DIRECTORIES()
、INTERFACE_COMPILE_DEFINITIONS
、INTERFACE_COMPILE_OPTIONS
和INTERFACE_LINK_LIBRARIES
和INTERFACE_POSITION_INDEPENDENT_CODE
。
可以从IMPORTED
目标读取LOCATION
,尽管很少有需要这么做。
add_custom_command
之类的命令可以透明的使用一个IMPORTED
EXECUTABLE
目标作为命令的执行程序。
一个IMPORTED
目标的范围是它被定义的那个文件夹。它可以在子目录中被访问和使用,但不能在父目录或同级目录中被访问和使用。这个范围类似于cmake变量的范围。
支持定义一个全局的IMPORTED
目标,这个目标可以在构建系统中全局访问。
参阅make-packages(7)
手册获取更多关于创建含有IMPORTED
目标的包的信息。
别名目标
一个ALIAS
目标可以被当做一个二进制目标的别名,以只读的方式使用。ALIAS
目标的一个主要应用是作为一个库的样例代码或单元测试的可执行程序,作为构建系统的一部分或独立构建。
add_library(lib1 lib1.cpp)
install(TARGETS lib1 EXPORT lib1Export ${dest_args})
install(EXPORT lib1Export NAMESPACE Upstream:: ${other_args})
add_library(Upstream::lib1 ALIAS lib1)
In another directory, we can link unconditionally to the Upstream::lib1
target, which may be an IMPORTED
target from a package, or an ALIAS
target if built as part of the same buildsystem.
if (NOT TARGET Upstream::lib1)
find_package(lib1 REQUIRED)
endif()
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Upstream::lib1)
ALIAS
targets are not mutable, installable or exportable. They are entirely local to the buildsystem description. A name can be tested for whether it is an ALIAS
name by reading the ALIASED_TARGET
property from it:
get_target_property(_aliased Upstream::lib1 ALIASED_TARGET)
if(_aliased)
message(STATUS "The name Upstream::lib1 is an ALIAS for ${_aliased}.")
endif()
接口库
An INTERFACE
target has no LOCATION
and is mutable, but is otherwise similar to an IMPORTED
target.
It may specify usage requirements such as INTERFACE_INCLUDE_DIRECTORIES
, INTERFACE_COMPILE_DEFINITIONS
, INTERFACE_COMPILE_OPTIONS
, INTERFACE_LINK_LIBRARIES
, INTERFACE_SOURCES
, and INTERFACE_POSITION_INDEPENDENT_CODE
. Only the INTERFACE
modes of the target_include_directories()
, target_compile_definitions()
, target_compile_options()
, target_sources()
, and target_link_libraries()
commands may be used with INTERFACE
libraries.
A primary use-case for INTERFACE
libraries is header-only libraries.
add_library(Eigen INTERFACE)
target_include_directories(Eigen INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include/Eigen>
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Eigen)
Here, the usage requirements from the Eigen
target are consumed and used when compiling, but it has no effect on linking.
Another use-case is to employ an entirely target-focussed design for usage requirements:
add_library(pic_on INTERFACE)
set_property(TARGET pic_on PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(pic_off INTERFACE)
set_property(TARGET pic_off PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)
add_library(enable_rtti INTERFACE)
target_compile_options(enable_rtti INTERFACE
$<$<OR:$<COMPILER_ID:GNU>,$<COMPILER_ID:Clang>>:-rtti>
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 pic_on enable_rtti)
This way, the build specification of exe1
is expressed entirely as linked targets, and the complexity of compiler-specific flags is encapsulated in an INTERFACE
library target.
The properties permitted to be set on or read from an INTERFACE
library are:
- Properties matching
INTERFACE_*
- Built-in properties matching
COMPATIBLE_INTERFACE_*
EXPORT_NAME
EXPORT_PROPERTIES
IMPORTED
MANUALLY_ADDED_DEPENDENCIES
NAME
- Properties matching
IMPORTED_LIBNAME_*
- Properties matching
MAP_IMPORTED_CONFIG_*
INTERFACE
libraries may be installed and exported. Any content they refer to must be installed separately:
add_library(Eigen INTERFACE)
target_include_directories(Eigen INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include/Eigen>
)
install(TARGETS Eigen EXPORT eigenExport)
install(EXPORT eigenExport NAMESPACE Upstream::
DESTINATION lib/cmake/Eigen
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/eigen.h
${CMAKE_CURRENT_SOURCE_DIR}/src/vector.h
${CMAKE_CURRENT_SOURCE_DIR}/src/matrix.h
DESTINATION include/Eigen
)