背景
● 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
● 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的 规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
● makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
● make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一 种在工程方面的编译方法。
● make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。(makefile的首字母大小写都可以)
理解
● 依赖例子
实例代码
c代码
#include<stdio.h>
#define column 10
int main()
{
int n = column;
while(n--)
{
printf("Hello Linux!%d\n",n);
}
return 0;}
makefile文件
test:test.o
gcc -o test test.o
test.o:test.s
gcc -c test.c -o test.o
test.i:test.c
gcc -E test.c -o test.i
test.s:test.i
gcc -S test.i -o test.s
.PHONY:clean (后面讲解)
clean:
rm -f test.i test.s test.o test
依赖关系
● 上面的文件 test ,它依赖 tets.o
● test.o , 它依赖 test.s
test.s , 它依赖 test.i
● test.i , 它依赖test.c
依赖方法
● gcc test.* -option test.* ,就是与之对应的依赖关系
原理
● make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么,
1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件, 并把这个文件作为最终的目标文件。
3. 如果test文件不存在,或是test所依赖的后面的test.o文件的文件修改时间要比test这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成test这个文件。
4. 如果test所依赖的test.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
5. 当然,你的C文件和H文件是存在的啦,于是make会生成 test.o 文件,然后再用 test.o 文件声明 make的终极任务,也就是执行文件hello了。
6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就不工作啦。
项目清理
● 工程是需要被清理的
● 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行, 不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
● 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。
● 可以将我们的 test 目标文件声明成伪目标,测试一下。
.PHONY伪目标
".PHONY"是Makefile的一个特殊目标。它用于声明一个伪目标,告诉make不要去查找同名的文件或目录,而是执行该目标下的命令。这通常用于定义一些不产生任何文件的命令,如"clean"、"all"等。在Makefile中,".PHONY"通常被用于声明伪目标,以方便使用make命令执行相应的操作,而不必判断是否存在同名的文件或目录。例如:
```
.PHONY: all cleanall: main.o func.o
gcc -o main main.o func.omain.o: main.c
gcc -c main.cfunc.o: func.c
gcc -c func.cclean:
rm -f *.o main
```在上述Makefile中,".PHONY"被用于声明"all"和"clean"是伪目标。这样做的优势在于,即使已经存在名为"all"或"clean"的文件或目录,make仍然能够执行相应的操作。
实际操作中的问题和技巧
1.技巧
1.1依赖方法中:
$@:代表依赖关系中冒号左边
$^:代表依赖关系中冒号右边1.2gcc前加@执行时不会回显
2.使用make
1.为什么?
提供编译效率
2.可以执行的条件一定是源文件形成可执行,现有源文件,才有可执行,一般而言, 源文件的最近修改时间比可执行文件要早的!如果我们更改了源文件,历史上曾经还有可执行,那么源文件的最近修改时间,一定要比可执行程序要新!
3.怎么做到的?
只需要比较,可执行程序的最近修改时间和源文件的最近修改时间
.exe新于.c源文件是老的,不需要重新编译
.exe老于.c源文件是老的,需要重新编译
而在Linux比较的最好的方法就是比较时间戳的大小。
3.那么要比较哪个时间呢?
stat是一个命令使用程序,用于显示给定文件或文件系统的详细信息。
由于本地Linux虚拟机使用的是中文,可能跟大家不同,这里说明一下
Access - 最近访问 :access更新策略,根据modify和change一定的更新次数才能更新,每个操作系统的更新策略不同
Modify - 最近更改 :修改文件内容时连同Change的时间一起修改,make比较的就是这个时间。
Change - 最近改动 :修改文件信息时,更新时间,如名字,路径等。
4.手动更新文件时间-touch
4.1更新Access和Change时间
touch -m test.c
4.2更新Modify和change时间
touch -a test.c