抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

自动化编译流程

前置知识

C/C++语言的编译过程

不妨查看这里

为什么要自动化编译

用命令行一条指令地执行来编译也未尝不可,但对于大型项目,尤其是较多文件的项目来说,每次编译都要输入一大串命令是不可忍受的,聪明的孩子可能会想到:“那我写一个脚本来执行编译命令不就皆大欢喜了吗?”确实是这样。

对于一个简单的项目,我们可以编写一个脚本,在其中按顺序设定好编译所需的所有指令,每次编译只要执行它就好了,但如果你更改了其中一个文件,重新编译便要把所有文件重新编译一遍再链接,如果其它文件没有改动,那这不是白白浪费了时间?

使用makefile来管理我们的编译流程

makefile是一种特殊的脚本标记语言,它文件名就叫做makefile[或Makefile]

我们在项目的根部录下创建一个makefile文件

makefile的语法如下:

target1 : dependency1 [dependency2] ...
Command

target2 : dependency1 [dependency2] ...
Command

......

这是什么意思呢?

直接举一个例子

我们有一个多文件的项目,源代码为main.c、add.c、add.h,其中main.c,add.c都include了add.h

我们编译需要执行的命令有三条

#编译main.c
gcc -c -o main.o ./main.c
#编译add.c
gcc -c -o add.o ./add.c
#链接得到可执行文件
gcc -o main ./main.o ./add.o

其中第一条命令是得到main.c编译后得到的main.o,其中,main.o就是makefile中的target,而得到main.o所需的依赖为main.c文件,而main.c中引入了add.h头文件,故add.h也是main.o的依赖。所以makfile中,得到main.o所需的标记如下

main.o : main.c add.h
gcc -c -o main.o ./main.c
#注意上一行开头是一个tab符

同理,得到add.o的标记如下

add.o : add.c add.h
gcc -c -o add.o ./add.c
#注意上一行开头是一个tab符

而最终想得到的可执行文件main的标记如下

#使用all来指示target,这是执行make时默认生成的target
all : main.o add.o
gcc -o main ./main.o ./add.o
#注意上一行开头是一个tab符

将它们写在同一个makefile文件中,便得到了我们所需的makefile文件

all : main.o add.o 
gcc -o main ./main.o ./add.o

main.o : main.c add.h
gcc -c -o main.o ./main.c

add.o : add.c add.h
gcc -c -o add.o ./add.c

Windows下安装了gcc之后,有一个make.exe供我们使用,它便是用来解析并执行makefile文件中的命令的程序。(Linux使用make来构建更方便)

我们在makefile同一目录下执行

make 

短短的四个字,为我们带来了无尽的方便。

makefile的优势

但和普通脚本相比,makefile文件多写了很多字啊,这不是反而麻烦了吗?

其实不然,上文说到普通脚本会暴力的执行每一步操作,而makefile标记的编译过程,每次执行make,make会检查每一个target及其dependency。

如果target不存在,好的,执行下一行的Command来得到target;

如果target依赖于某个or某些dependency,好的,先去检查这些dependency的获取方式;

如果dependency不存在,好的,先去搜索如何得到dependency;

如果dependency被改变了,好的,重新构建该target。

这时makefile的优势便体现了出来,本例中,假设我已经编译了程序,结果发现我add.c中的add函数有bug!我把它写成减法了!我改动了add.c文件使其能完成正确的操作。再次执行make时,它进行检查:

all 依赖于 main.o 与 add.o,先去检查它们

main.o 依赖于 main.c 与 add.h,这两个文件都没有改变(相比与上一次make),且make.o已经存在(上一次make生成的),所以main.o是已经生成好了的。(跳过此target指示的Command)

add.o 依赖于 add.c 与 add.h,发现add.c被改变了!将重新生成add.o。(执行下一行的Command)

对all的依赖检查结束,发现至少有一个依赖不存在或已经改变。(此处为add.o)重新生成all(执行下一行的Command)

对于简单地执行脚本,我们少执行了一条编译main.c的指令。在此处优势还不太明显,但在大型项目组织中,这无疑是最好的构建方法,对于单个文件的改动,不会重复地去编译其他的源代码,从而大大减少了编译时间。(C/C++编译速度慢也是它们常年来被诟病的一个点)

评论