Linux 项目自动化构建工具 —— make/makefile
会不会写 makefile
,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile
定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile
带来的好处就是 —— “自动化编译” ,一旦写好,只需要一个 make
命令,整个工程完全 自动编译 ,极大的提高了软件开发的效率
make
是一个 命令工具 ,是一个解释 makefile
中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如: Delphi 的 make
, Visual C++ 的 nmake
,Linux 下 GNU 的 make
;可见 makefile
都成为了一种在工程方面的 编译方法
入门使用
首先: make
是一条命令,makefile
是一个文件,两个搭配使用,完成项目自动化构建
现在在当前路径下有 code.c
代码,需要手动 touch
一个名为 makefile
或 Makefile
的文件
在里面写上如下代码:
mycode:code.c gcc -o mycode code.c -std=c99 .PHONY:clean clean: rm -f mycode
- 构建可执行文件
mycode:code.c
中mycode
是指未来形成的 可执行程序 ,code.c
是指此可执行程序依赖code.c
文件gcc -o mycode code.c -std=c99
这一行前面有一个Tab
键,内容就是 编译 指令,不多说
- 清理
- 接下来的指令就都是清理所用
返回命令行,运行 make
指令,即可构建可执行程序 mycode
;运行 make clean
即可清理 mycode
文件
至此,你已经完成了入门使用 ^ ^
原理
自动化构建
先看下图:
这里的每一行都有其名称
- 依赖关系
:
右边是 依赖文件列表 ,图中就只有code.c
文件;可以为空,但若有多文件,以空格为分隔符,例:code1.c code2.c code3.c
:
左边是 目标文件 ,图中为mycode
可执行程序
- 依赖方法
- 在单纯语法上,这一行 必须以 Tab 键开头,也不可是 4 个空格
当你在运行指令 make
后,会在当前目录下寻找 makefile
文件,根据 makefile
文件里的 依赖关系 ,遵循 依赖方法 ,为你完成 依赖方法 里的工作,生成 目标文件
递归式自动推导
当你知道目标文件如何生成时,再来看看类似 递归式的连锁生成 :
先来看看 makefile
文件:
mycode:code.o gcc code.o -o mycode code.o:code.s gcc -c code.s -o code.o code.s:code.i gcc -S code.i -o code.s -std=c99 code.i:code.c gcc -E code.c -o code.i
这就是 gcc
编译的每一步明细,在这里不做详细解释(如不明白,请查阅过往 gcc
拙作)
- 我们知道最终的目标文件是
mycode
可执行程序,但:
后面的依赖文件code.o
在我们当前路径下并没有 - 于是
make
就会在makefile
文件里寻找名为code.o
的目标文件 - 找到之后会根据
:
后面的依赖文件,再根据依赖方法来生成code.o
目标文件 - 但当前路径下
code.o
的依赖文件code.s
也没有,那make
就会继续在makefile
文件里寻找名为code.s
的目标文件 - 于是一直找到
code.i
目标文件,它的依赖文件是存在的,那么就会生成code.i
,继而生成code.s
,code.o
直至最后的mycode
可执行程序
那么这就很类似 栈式结构的递归调用 ,而上述就是 自动推导 makefile 中的依赖关系 的过程,即使乱序也可构建执行
清理
先看看 makefile
里的 clean
部分
clean: rm -f code.i code.s code.o mycode
是否感觉和你要自动化构建的可执行程序写法十分相似?
实际上 clean
就是 makefile
里的 目标文件,只是它没有 依赖文件 而已(依赖文件列表可以为空),而下面就是 依赖方法 ,完成 依赖方法 里的工作,即为清理完成
而你要执行清理,就是生成 clean
这个目标文件,命令行也就需要在 make
后面指定要生成的目标文件,所以是:
make clean
注意
- 当你直接
make
时,make
会默认生成makefile
里的第一个目标文件 - 如果你要生成
makefile
里的其他的目标文件,就在make
后面带上你要生成的目标文件(clean
就是如此) - 当你第二次再运行
make
时,只要 源文件 没有被更新修改,就会告诉你要生成的 目标文件 是最新的,不让你继续make
,因为没有必要,为了提高效率(实现方法就是对比 源文件 和 目标文件 的最近修改时间) - 要想 目标文件总是被执行,需要在 目标文件 前添加
.PHONY
声明,相当于告诉make
不要管什么时间问题;而此时的 目标文件 也被称为 伪目标
特殊符号
在 依赖关系 里存在 目标文件 和 依赖文件列表
那么在 依赖方法 里,就可以利用 $@
、 $^
分别表示 目标文件 和整个 依赖文件列表
在我们成功 make
时,命令行会给我们回显自动化构建的步骤,如果不想回显,可在 依赖方法 最前面添加 @