CMake官方文档翻译(3) CMake构建系统

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.cppzip.cpplzma.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 $ extras.cpp)

add_executable(test_exe $ 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)命令中。不过,目标文件列表可以通过$的形式用在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_DIRECTORIESCOMPILE_DEFINITIONSCOMPILE_OPTIONS目标属性和INTERFACE_INCLUDE_DIRECTORIESINTERFACE_COMPILE_DEFINITIONSINTERFACE_COMPILE_OPTIONS目标属性追加值。

这些命令都有PRIVATEPUBLICINTERFACE三种模式。PRIVATE模式仅向非INTERFACE_的目标属性追加值,INTERFACE模式仅向INTERFACE_目标属性追加值,PUBLIC模式同时向两种目标属性追加。调用这些命令的时候可以同时指定多种模式:

 target_compile_definitions(archive
  PRIVATE BUILDING_WITH_LZMA
  INTERFACE USING_ARCHIVE_LIB
)

使用要求这种机制不仅是为了方便传递COMPILE_OPTIONSCOMPILE_DEFINITIONS等设计的。使用要求中的内容是必需满足的条件,而不仅仅是建议或为了方便。

参阅make-packages(7)手册中的创建可重定位包章节,查看关于创建分发包时必须额外关心的使用要求的讨论。

目标属性

目标的INCLUDE_DIRECTORIESCOMPILE_DEFINITIONSCOMPILE_OPTIONS属性会在编译目标的源文件时使用。

INCLUDE_DIRECTORIES中的项会被加上-I-isystem前缀,然后以原顺序添加到编译命令中。

COMPILE_DEFINITIONS中的项会被加上-D/D前缀,然后以未定义顺序添加到编译命令中。对于SHAREDMODULE库类型,DEFINE_SYMBOL目标属性也会被当做宏定义添加到编译命令中。

COMPILE_OPTIONS中的项会经过转义,然后以原顺序添加到编译命令中。某些编译选项有单独的处理,比如POSITION_INDEPENDENT_CODE目标属性。

INTERFACE_INCLUDE_DIRECTORIESINTERFACE_COMPILE_DEFINITIONSINTERFACE_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()命令中使用PRIVATEINTERFACEPUBLIC关键词来控制传递。

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)

因为archivearchiveExtrasPUBLIC依赖,所以,它把archiveExtras的使用要求传递到了consumer。因为serializationarchiveExtrasPRIVATE依赖,所以它没有把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()中依赖项的顺序是紧密相关的,其中的顺序可能导致无法正确编译,这时,使用合适的命令直接设置目标属性可以解决顺序问题。

例如:如果一个目标链接的库必须按lib1lib2lib3的顺序指定,但是头文件却必须按lib3lib1lib2指定:

target_link_libraries(myExe lib1 lib2 lib3)
target_include_directories(myExe
  PRIVATE $)

当使用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)

上面这个例子中,exe1exe2都会被编译为位置无关代码。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)

上面这个例子中,lib1INTERFACE_POSITION_INDEPENDENT_CODE使用要求与exe1POSITION_INDEPENDENT_CODE目标属性不兼容。lib1要求消费者使用POSITION_INDENPENDENT_CODE目标属性编译,但是exe1指定了不要编译为位置无关代码,所以将会报出一个诊断错误。

lib1lib2的使用要求不兼容,其中一个要求消费者编译为位置无关代码,但是另一个要求消费者不要编译为位置无关代码。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_BOOLCOMPATIBLE_INTERFACE_STRING中指定的属性,调试输出中将会显示那个目标负责设置属性,那个依赖项同样也定义了该属性。对于在COMPATIBLE_INTERFACE_NUMBER_MAXCOMPATIBLE_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=$
)

在这个例子中,exe1源文件将会使用-DCONTAINER_SIZE=200编译。

可以很方便地使用CONFIG生成器表达式来设置基于配置的构建规范。

target_compile_definitions(exe1 PRIVATE
    $<$: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
  $<$,EXECUTABLE>:LIB1_WITH_EXE>
  $<$,SHARED_LIBRARY>:LIB1_WITH_SHARED_LIB>
  $<$: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
  $
  $
)
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
  $
  $
  $
  $/$/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
  $
)

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
  $<$: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 the add_executable() command.
  • On DLL platforms: the executable file (e.g. .dll) of a shared library target created by the add_library() command with the SHARED 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 the add_library() command with the MODULE option.
  • On non-DLL platforms: the shared library file (e.g. .so or .dylib) of a shared library target created by the add_library() command with the SHARED 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 the add_library() command with the STATIC option.
  • On DLL platforms: the import library file (e.g. .lib) of a shared library target created by the add_library() command with the SHARED 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 the add_executable() command when its ENABLE_EXPORTS target property is set.
  • On AIX: the linker import file (e.g. .imp) of an executable target created by the add_executable() command when its ENABLE_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_DEFINITIONSINTERFACE_COMPILE_OPTIONSINTERFACE_LINK_LIBRARIESINTERFACE_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
  $
  $
)

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
  $<$,$>:-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
  $
  $
)

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
)

你可能感兴趣的:(CMake官方文档翻译(3) CMake构建系统)