自动化编译流程
前置知识
C/C++语言的编译过程
不妨查看这里
为什么要自动化编译
用命令行一条指令地执行来编译也未尝不可,但对于大型项目,尤其是较多文件的项目来说,每次编译都要输入一大串命令是不可忍受的,聪明的孩子可能会想到:“那我写一个脚本来执行编译命令不就皆大欢喜了吗?”确实是这样。
对于一个简单的项目,我们可以编写一个脚本,在其中按顺序设定好编译所需的所有指令,每次编译只要执行它就好了,但如果你更改了其中一个文件,重新编译便要把所有文件重新编译一遍再链接,如果其它文件没有改动,那这不是白白浪费了时间?
使用makefile来管理我们的编译流程
makefile是一种特殊的脚本标记语言,它文件名就叫做makefile[或Makefile]
我们在项目的根部录下创建一个makefile文件
makefile的语法如下:
target1 : dependency1 [dependency2] ... |
这是什么意思呢?
直接举一个例子
我们有一个多文件的项目,源代码为main.c、add.c、add.h,其中main.c,add.c都include了add.h
我们编译需要执行的命令有三条
#编译main.c |
其中第一条命令是得到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 |
同理,得到add.o的标记如下
add.o : add.c add.h |
而最终想得到的可执行文件main的标记如下
#使用all来指示target,这是执行make时默认生成的target |
将它们写在同一个makefile文件中,便得到了我们所需的makefile文件
all : main.o add.o |
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++编译速度慢也是它们常年来被诟病的一个点)