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

1.3 构建静态库

静态库(static library),也称为静态链接库(statically-linked library),可以看作最简单直接的一种复用代码的形式。静态库可以被视作一系列目标文件的集合,甚至可以被解包软件打开。在静态库中,除了目标文件,可能还有一些文本文件,它们是静态库的符号索引。下面是一个非常简单的例子,用于展示静态库的用途。

该静态库包含两个源程序,分别提供了不同的功能函数,如代码清单1.8和代码清单1.9所示。

代码清单1.8 ch001/静态库/a.c

#include <stdio.h>
 
void a() { printf("a\n"); }

代码清单1.9 ch001/静态库/b.c

#include <stdio.h>
 
void b() { printf("b\n"); }

a.c源程序提供了函数a,可以输出“a”; b.c源程序则提供了函数b,可以输出“b”。 a和b这两个函数构成了静态库的全部功能。

另外,因为库总是要被其他开发者使用的,所以提供一个声明了全部功能函数的头文件十分有必要。这样,开发者只需引用提供的头文件,然后链接静态库,就可以使用该库开发好的实用功能了!头文件相当于对接口的声明,而静态库则在接口之下封装了功能的具体实现。对于本例实现的两个功能函数而言,头文件只需声明对应函数,如代码清单1.10所示。

代码清单1.10 ch001/静态库/libab.h

void a();
void b();

最后,编写主程序main.c,链接构建好的静态库,并调用上述两个函数来完成相应的功能,如代码清单1.11所示。

代码清单1.11 ch001/静态库/main.c

#include "libab.h"
#include <stdio.h>
 
int main() {
    a();
    b();
    return 0;
}

使用MSVC和NMake构建

鉴于我们已经了解过Makefile的书写方式,本章的后续实例将使用Makefile规则文件来整理构建过程中所需调用的命令,这样更加方便阅读和调用。本实例对应的NMake Makefile如代码清单1.12所示。

代码清单1.12 ch001/静态库/NMakefile

main.exe: main.obj libab.lib
    cl main.obj libab.lib /Fe"main.exe"
 
main.obj: main.c
    cl -c main.c /Fo"main.obj"
 
libab.lib: a.obj b.obj
    lib /out:libab.lib a.obj b.obj
 
a.obj: a.c
    cl /c a.c /Fo"a.obj"
 
b.obj: b.c
    cl /c b.c /Fo"b.obj"

我们从下往上看。最后两条规则用于生成a.c和b.c两份源程序对应的目标文件。

第三条是用于生成libab.lib静态库的规则,需要依赖目标文件a.obj和b.obj。这两个目标文件中包含了静态库所需功能函数a和b的目标代码。这条构建规则的命令部分通过调用MSVC的lib.exe来生成静态库。/out:参数后紧跟的是静态库名,然后是罗列的静态库所需的目标文件。lib.exe会对罗列的目标文件建立索引,并将索引文件与目标文件一起打包成指定名称的静态库libab.lib。当静态库被使用时,编译器就能通过索引文件高效地了解静态库提供了哪些符号。

第二条规则用于生成main.c源程序对应的目标文件。

第一条规则依赖main.obj(即主程序的目标文件)和libab.lib(即静态库)。该规则把这两个文件都作为参数输入MSVC编译器,并设置输出的可执行文件的名称,编译器会调用链接器将二者链接起来,并最终生成可执行文件。现在不妨运行一下NMake看看实际效果:

> cd CMake-Book\src\ch001\静态库
> nmake /F NMakefile
> main.exe
a
b

使用GCC和make构建

Makefile如代码清单1.13所示。

代码清单1.13 ch001/静态库/Makefile

main: main.o libab.a
    gcc main.o -o main -L. -lab
 
main.o: main.c
    gcc -c main.c -o main.o
 
libab.a: a.o b.o
    ar rcs libab.a a.o b.o
 
a.o: a.c
    gcc -c a.c -o a.o
 
b.o: b.c
    gcc -c b.c -o b.o

我们先来看第三条规则。它通过ar归档命令,将目标文件a.o和 b.o打包为静态库。这里的ar命令有三组参数,分别是rcs、输出的静态库(归档)文件名和输入的目标文件名。其中,rcs是三个参数的开关:r代表将目标文件归档,c代表创建新归档文件时不输出警告信息,s代表要为归档创建索引。

接着看第一条链接静态库并生成主程序的规则。编译主程序时,将链接器参数设置为-L.,就可以将当前目录作为链接库的搜索路径。-lab指链接名为ab 的库。此处并没有写静态库的完整文件名,因为GCC编译器会自动根据这个基本名称,加上前缀“lib”和扩展名“.a”去 搜索[5]


[5]如果想指定链接库的完整名称,可以在名称前加一个冒号,如-l:libab.a。

事实上,GCC也可以按照类似MSVC的写法来链接静态库,即gcc main.o libab.a -omain。之所以在Makefile中选择了-l参数的写法,是因为这种写法还能同时用于链接动态库。统一采用这种写法,可以不必关注用到的链接库具体以什么形式链接(动态链接会在1.4节讲到)。当然了,这种写法也能够让我们少输入几个字母(“lib”和“.a”)。