会不会写 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 可执行程序当你在运行指令 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 时,命令行会给我们回显自动化构建的步骤,如果不想回显,可在 依赖方法 最前面添加 @