前言
CMake的构建系统是通过一个高度抽象的目标集合进行组织的。集合中的每个目标要么对应一个可执行文件或库,要么包含了自定义的命令行。构建系统根据目标之间的依赖关系确定目标的构建顺序和生成规则。
二进制目标
可执行文件和库可以通过add_executable()和add_library()指令添加。使用这两个指令生成的目标会根据平台使用不同的前缀,后缀和扩展名。二进制目标之间的依赖关系可以通过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编译而来,并链接了archive静态库)。
可执行二进制目标
使用add_executable()指令可以定义一个可执行二进制目标:
add_executable(mytool mytool.cpp)
add_custom_command()等生成构建规则的指令可以使用生成的可执行目标调用命令行。CMake的构建系统可以保证在命令行执行之前构建它所使用的可执行目标。
二进制库类型
普通库
默认情况下,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()指令默认生成静态库的行为为生成共享库。
如果不关心使用静态库还是动态库,可以使用MODULE库类型,这一类型不能作为target_link_libraries()指令的参数。通常对于在运行时,由程序自己负责加载的库使用这一类型。
add_library(archive MODULE 7z.cpp)
苹果框架
可以对共享库目标使用FRAMEWORK目标属性来生成macOS或iOS框架包。通过MACOSX_FRAMEWORK_IDENTIFIER可以设置框架包的标识符。
add_library(MyFramework SHARED MyFramework.cpp)set_target_properties(MyFramework PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework
)
Object库
Object库类型用来定义编译源代码生成的Object文件集合。它可以被作为其它目标的输入来源:
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)add_library(archiveExtras STATIC $ extras.cpp)add_executable(test_exe $ test.cpp)
在链接阶段,使用Object库的其它目标会直接链接这些Object文件。
Object库也可以被链接到其它目标中去:
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)
上面的代码在链接时,会直接链接来自Object库的Object文件,也就是说当一个使用Object库的目标编译时,Object库的构建需求满足后,之后其它使用Object库的目标有关Object库的构建需求在编译前就已经满足。
Object库不能被作为add_custom_command(TARGET)指令的TARGET参数。但Object文件列表可以被add_custom_command(OUTPUT)和file(GENERATE)指令通过$使用。
构建配置和构建需求
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模式下,参数只填入non-INTERFACE类目标属性,INTERFACE模式下只填入INTERFACE_类目标属性。PUBLIC模式下,填入所有类型的目标属性。每条指令可以多次使用模式关键字,比如:
target_compile_definitions(archive
PRIVATE BUILDING_WITH_LZMA
INTERFACE USING_ARCHIVE_LIB
)
需要注意构建需求的设计不仅仅是为了传递编译选项给下游。设置的参数必须是构建需求。
目标属性
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_