由于Cmake是一种自动化构建工具,在了解Cmake前我们首先需要了解什么是构建,以及make和makefile的使用方法。
在软件工程中,构建是一种将源码文件和资源文件转换成可执行文件的过程。
当项目变得庞大复杂,有成百上千个源文件、头文件和库文件时,手动编译就变得不太现实了。
这时候就可以利用一些自动化的构建工具来完成整个构建过程
make和cmake是c语言中最常用的构建工具。cmake是自动化的构建工具。
一个最简单的c语言项目的构建过程:
gcc编译器编译main.c源文件生成可执行文件hello
了解Makefile
使用make和makefile构建一个简单项目
如图:
makefile基本格式规则
目标文件 : 依赖文件
要执行的命令
...
根据上面的格式我们可以写一个简单的makefile文件如下:
hello : main.c message.c
gcc main.c message.c -o hello
之后可以使用make hello或make来构建得到hello可执行文件。
注意,make一次后,再次使用make时会检查依赖文件是否有更新,如果没有更新就不进行任何操作,并给予提示。
这样一来,我们每次执行make都只会重新编译修改的源文件,节约了时间和计算资源。
然而上面的写法并不能达到这样的效果。(任何一个源文件更新都会把所有源文件都重新编译)
所以,规范的写法是把 编译 和 链接 分开写
hello : main.o message.o
gcc main.o message.o -o hello
main.o : main.c
gcc -c main.c
message.o : message.c
gcc -c message.c
这样写的好处就是当我们修改了其中一个源文件后,再次make时,只需要重新编译修改了的源文件,然后再链接就可以了,而不是把所有源文件都重新编译一遍。
执行make后可以看到,先把两个源文件编译成目标文件,最后再链接。如图:
伪目标(.PHONY)
有的时候我们需要进行一些不生成文件的操作,例如清理临时文件,生成文档,打包等。这时可以使用伪目标来定义这些规则。
clean
clean是最常见的伪目标,用于清理编译过程中生成的临时文件和可执行文件。
修改makefile为如下:
hello : main.o message.o
gcc main.o message.o -o hello
main.o : main.c
gcc -c main.c
message.o : message.c
gcc -c message.c
clean :
rm -f *.o hello
以下是使用makefile中预设的clean实现清理-o目标文件和hello文件的操作
但是如果此时目录下已经有名为clean的文件存在,make clean命令会判定这里的clean是一个文件名,从而出现下面的结果(clean已经是最新的了,不会再编译):
然而,我们不能保证makefile文件中的所有 规则名 都不和当前目录下的 文件名 有重名发生。
为了避免这种现象,我们使用伪目标.PHONY来显式地告诉make clean就是一个伪目标。
.PHONY: clean
hello : main.o message.o
gcc main.o message.o -o hello
main.o : main.c
gcc -c main.c
message.o : message.c
gcc -c message.c
clean :
rm -f *.o hello
再次使用make clean,结果如下:
all
除了clean,还有一个常见地伪目标all,当我们执行make地时候,make会默认执行第一个目标(如果没有指定目标的话),但是如果我们想生成的文件不止一个的话就可以使用all。
.PHONY: clean all
all : hello world
@echo "all done."
hello : main.o message.o
gcc main.o message.o -o hello
world : main.- message.o
gcc main.o message.o -o world
main.o : main.c
gcc -c main.c
message.o : message.c
gcc -c message.c
clean :
rm -f *.o hello
重新编写makefile如上图。由此可以使用make all(或者如果all在makefile中是第一个目标的话,可以直接执行make,上述代码就是)直接创建两个可执行文件(否则要分别执行make hello 和make world)。
得到的结果如下:
执行make,一次创建了hello和world两个可执行文件。
变量
定义变量
在makefile中还可以定义变量,于是在后面编译命令时就可以直接使用这个变量,而不用重复写。
例如,我们使用CFLAG把一些编译选项定义成一个变量CFLAGS:-Wall -g -O2
一般也会把目标文件、源文件、中间文件等也都定义为变量,如我们可以把上面的
hello和world定义为targets
main.c message.c定义为sources
main.o message.o定义为objects
(-Wall -g -O2 是一个常见的编译器选项组合,它旨在在不牺牲调试能力的情况下,生成警告信息并优化程序性能。这是一个适合开发和测试阶段的编译选项,因为它提供了足够的调试信息,同时还能提高程序的运行效率。)
接下来我们就可以直接使用这些变量了,使用的方法为
$(变量名)
具体使用方法如下
.PHONY: clean all
CFLAGS = -Wall -g -O2
targets = hello world
sources = main.c message.c
objects = main.o message.o
all : $(targets)
@echo "all done."
$(targets) : $(objects)
gcc $(CFLAGS) $(objects) -o $@
main.o : main.c
gcc $(CFLAGS) -c main.c
message.o : message.c
gcc $(CFLAGS) -c message.c
clean :
rm -f *.o hello
结果如下:
可以发现是可以正常编译运行的
makefile中还有一种特殊的变量叫自动变量
$@:目标文件
$<:第一个依赖文件
$^:所有依赖文件
变量的使用方法都类似,就不再演示。
利用通配符简化规则
main.o : main.c
gcc $(CFLAGS) -c main.c
message.o : message.c
gcc $(CFLAGS) -c message.c
这两个规则生成规则相同,只是目标文件和依赖文件不同,
而目标文件和依赖文件之间只是拓展名不相同,文件名是相同的
此时我们可以把上述规则简化为
%.o : %.c
gcc $(CFLAGS) -c $<
执行make得到
可见修改后,make使用依然正常。