cmake构建系统的基理

概要

cmake构建系统中最核心的概念是target。 一个target可能是:

target之间的依赖关系是指两个方面:

binary target

主要包括两类:一类是binary executable,一类是binary library。 Dependencies between binary targets are expressed using the target_link_libraries() command。 这一句是个重点,表明了target之间的依赖关系要通过函数target_link_libraries函数来传递。

binary executables

这跟通常理解的可执行文件是一致的,主要通过add_executable()函数来生成。

binary library

库的分类主要有静态库、动态库、object库三类。

使用函数add_library()函数生成的库文件主要是静态库和动态库两类, 前者传入的是static参数,后者传入的是shared参数。 这二者一般也称为常规库。

object库是一个新颖的概念,它不同于静态库,可以简单理解为一个object files的集合。object library可以作为静态库生成的source列表,也可以作为可执行文件生成的source列表。

构建规格和使用条件

与编译构建相关的头文件目录、编译宏、编译选项(对应函数为target_include_directories(), target_compile_definitions() and target_compile_options() )操作函数明确了binary target的构建规格和使用条件。这些命令会相应地将target属性中与头文件目录、编译宏、编译选项传播出去,也可能包括INTERFACE_*相关的属性。

这些命令有三种模式:private; public; interface. private模式只会传播非INTERFACE_的属性。 interface模式只会传播INTERFACE_的属性。 而public模式会同时将两种属性都传播出去。 每一个命令都可以将每种模式多次使用。

要注意的是,使用条件并不是为了方便使用,也不是建议,而是必要条件。

target属性

INCLUDE_DIRECTORIES传入的参数是与头文件目录相关的,传入顺序是重要的、要讲究的。

COMPILE_DEFINITIONS传入的参数是编译宏定义,传入顺序并不重要。

COMPILE_OPTIONS传入的参数是编译优化选项相关的, 其传入顺序也是要讲究的。

这三个函数影响的是源文件的编译。 而 INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS and INTERFACE_COMPILE_OPTIONS里的内容则是使用条件; 使用者需要使用它们来正确地编译和链接自己的target。 让这一点起作用的桥梁,是函数target_link_libraries()的调用。

使用条件的传递

通过函数target_link_libraries()的调用,可以使得使用条件得以传递给被依赖者target。 同样,传递模式也是三种:private; interface; public。

private模式表示,使用条件不会被传递,仅是链接要使用的库文件 。

public模式表示,使用条件全部会传递给被依赖者target,包括了头文件和库文件。

interface模式表示,仅使用头文件的依赖关系,不使用库文件的依赖关系。

这里要稍微区别一下两处的模式(或者也可称为权限控制)。 前面与include/defination/options相关的权限控制,是从被依赖者的角度来讲,我控制哪些属性允许别人使用; 后面与link相关的权限控制,是从依赖者的角度来讲,我控制我使用哪些属性。

接口属性冲突了怎么办?

在属性传递过程中,如果发生了冲突怎么办? 基本想法就是Compatible Interface Properties。

此外,还有部分属性是用于调试的。

生成表达式

生成表达式的理念很简单,就是运行时生成条件或集合。 当前生成表达式在头文件目录和链接库的设置函数中各有区分,在使用的时候可以参考手册。

输出文件

输出文件主要是指与target对应的、磁盘上生成的动态库文件、静态库文件、可执行文件。这几类文件都可以通过接口中属性参数的值来指定。

目录范围的命令

以target_为前缀的三个函数影响范围是target,而不带target_前缀的add_compile_definitions(), add_compile_options() ,include_directories()作用相似,只是影响范围仅在目录范围之内。

伪target

伪target有三种:imported target; alias target; interface library。 以下三种target还没有实践经验,使用后继续补充。

imported target

表示一个预先存在的依赖项。 一般而言,这些target是由上游包定义的,一般可以认定为不可变的。当然,你也可以通过相应的函数接口来个性imported target的编译、链接属性。

这类target有一个比较重要的属性是location。 默认情况下,imported target的定义范围是它定义的目录,它可以从子目录进行访问和使用,但却不能从父目录和兄妹目录进行访问。 当前,你也可以定义一个global imported target,以供整个构建系统全局访问。

alias target

相当于给已有的target起了个别名。 这类target整个构建系统来讲,是局部的。

interface library

特别类似于imported target,主要区别在于:

这类库主要有几个使用场景:

Table of Contents