CMake构建实战:项目开发卷
上QQ阅读APP看书,第一时间看更新

1.6.2 核心的抽象概念:构建目标

目标文件虽然名叫“目标”,但终究不是我们最终想要的目标。因此有构建目标(target,简称目标)这个概念。构建目标是建立在源程序之上的更高层抽象。当我们将一系列源程序组织成一个构建目标,就相当于为这些源程序指定了一些共同的编译和链接参数。

一般来说,我们会将一些目标文件打包或链接成库文件或可执行文件,这样这些库文件和可执行文件就可以称作构建目标了。当然,具体一点的话,它们是二进制构建目标(binary target)——多了个“二进制”的前缀。一是因为构建产生的库文件和可执行文件都是二进制文件,二是为了区分不产生二进制文件的构建目标,也就是后面会提到的伪构建目标(pseudo-target)。

二进制构建目标

二进制构建目标基本上包括以下类型:

可执行文件;

一般库(包含静态库和动态库);

目标文件库。

目标文件库(object library)是个新概念,但非常好理解——它就是目标文件的集合。它类似静态库,只不过省去了索引和打包的步骤。因此,构建目标文件库并不会产生一个库文件,而只是将其包含的源程序编译成目标文件。

我们引入这样一个概念同样是为了实现更灵活的代码复用。例如,当我们想复用源程序,但不愿产生额外的静态库文件时,就可以使用目标文件库。可以说,目标文件库并非是一个传统意义上的库,它更像是一个逻辑上的概念。但它毕竟包含一系列源程序,并指导编译器将它们编译为目标文件。也就是说,它终究还是产生了一系列二进制文件,所以我们仍将其看作二进制构建目标中的一种类型。

伪构建目标

在介绍构建目标时说过,伪构建目标不会产生二进制文件。那么,我们为什么还需要它呢?

还记得头文件库吗?头文件库本身不需要编译或链接,那么如果将它当作一个构建目标的话,不正是一种不会产生二进制文件的构建目标嘛!可是既然不需要构建,为什么还把它当作一个构建目标呢?这是一个好问题。目前为止,我们在理解构建目标时,总想着它是如何被构建的,但实际上构建目标这个抽象概念还有另一大作用,那就是声明它应当如何被使用。

以头文件库为例。如果利用构建目标抽象表示一个头文件库,那么其他程序在使用头文件库时,只需引用这个构建目标,并不需要知道头文件库具体的存储位置。可见,这个构建目标本身隐含了对使用者的要求:请在编译参数中指定头文件搜索目录为本目标代表的头文件库所在的目录。

将头文件库这种伪构建目标推广一下:自身不需要编译,但对使用者有一定要求的构建目标。这个推广后的伪构建目标称作接口库(interface library)。

当然,伪构建目标不止接口库这一种类型,它包含以下三种类型:

接口库;

导入目标;

别名目标。

导入目标(imported target)一般用于抽象第三方库中的构建目标。第三方库要么是我们自己提前构建好的,要么是直接安装的预编译库,总之无须在使用它的时候再来构建。因此,导入目标尽管可能代表了某些二进制文件,但并不需要构建产生二进制文件,当然也是伪构建目标中的一种。与接口库类似,它自身无须编译,但对使用者提供了编译和链接的要求。

别名目标(alias target)就更加抽象了。顾名思义,它就是另一个构建目标的别名。既然是别名,也就没有必要再构建一次了,所以它同样是一种伪构建目标。别名目标通常用于隐藏实现细节。假设现在有一个自行构建的Boost库目标“boost”,一个预编译Boost库的导入目标“boost_prebuilt”,还有很多程序会链接Boost库。我们这时希望有一个开关能够切换这些程序是链接“boost”还是“boost_prebuilt”目标,那么可以创建一个别名目标“boost_alias”,根据设定作为“boost”或“boost_prebuilt”的别名。其他程序则无须关心设定,直接链接到“boost_alias”别名目标即可。