声明:本文记录本人看网络上关于Makefile的文档《跟我一起写Makefile》(陈皓 写),其中主要讲述了GUN Make 自动化编译工具的运用。本人以linux/C++角度学习Make,博文中加入了一些我个人的一些理解。
1.编译、链接、中间文件、库文件
编译: 将C++源码 使用g++编译器 生成 .o 文件的过程称为编译。
链接: 将多个.o文件 生成一个可执行程序的过程称为链接。
中间文件: 从源代码 到最后的可执行文件中间的 文件形态称为中间文件。比较典型的有.o 文件
库文件: 将多个.o 文件进行打包,以便于在生成可执行程序时,不需要显示的将每个.o文件的名字都打出来。
2.Make 执行规则
file1:main.h
#pragma once
void print_hello();
void print_nihao();
file2:main.cpp
#include <iostream>
#include "main.h"
int main()
{
print_hello();
print_nihao();
return 0;
}
file3:print_hello.cpp
#include <iostream>
using namespace std;
void print_hello()
{
cout << "hello3" << endl;
}
file4:print_nihao,cpp
#include <iostream>
using namespace std;
void print_nihao()
{
cout << "nihao" << endl;
}
file5:makefile
main:main.o print_hello.o print_nihao.o #规则1
g++ -o main main.o print_hello.o print_nihao.o #command1
main.o:main.cpp #规则2
g++ -c -o main.o main.cpp #command2
print_hello.o:print_hello.cpp #规则3
g++ -c -o print_hello.o print_hello.cpp #command3
print_nihao.o:print_nihao.cpp #规则4
g++ -c -o print_nihao.o print_nihao.cpp #command4
以上面的简单代码举例:
-
(1)首先make会在当前文件夹中去找 Makefile 或者 makefile文件,作为make的执行文件
-
(2) 在make找到其执行的文件后,make会在文件中寻找第一个需要生成的目标文件,作为最终执行的目标。(上述示例中的main会作为最终目标)
-
(3) 如果main 文件存在且依赖文件存在
make会检查 依赖文件(.o文件) 和 生成依赖文件的 依赖文件(cpp文件)的时间戳
如果cpp 文件比.o文件的时间戳新,那么make会依据时间戳会执行command2 command3 command4其中的某些,重新生成.o 文件。这就导致 生成main文件的依赖文件的时间戳比main文件的时间戳更新,于是执行command1 重新生成main 文件。如果*.o 文件是最新的,那么make回去检查 main 文件和*.o文件的时间戳,如果main文件也是最新的,那么make什么也不做。如果main文件不是最新的,那么会执行comman1 重新生成 main文件。 -
(4) 如果 main文件存在且依赖文件不存在
首先,make 会寻找生成 main.o 的规则 ,找到了规则2, 然后会执行command2,于是生成了main.o。接着,make 会寻找生成 print_hello.o 的规则,找到了规则3,然后执行command3,生成了print_hello.o。最后,make 会寻找生成 print_nihao.o 的规则,找到了规则4,然后执行command4,生成了print_nihao.o。 -
(5) 如果main 文件不存在 但是依赖文件存在
make会检查 依赖文件(.o文件) 和 生成依赖文件的 依赖文件(cpp文件)的时间戳,如果cpp 文件比.o文件的时间戳新,那么make会依据时>间戳会执行command2 command3 command4其中的某些,重新生成.o 文件。这就导致 生成main文件的依赖文件的时间戳比main文件的时间戳更新,于是执行command1 重新生成main 文件。 -
(6)如果main 文件不存在 且依赖文件不存在
首先,make 会寻找生成 main.o 的规则 ,找到了规则2, 然后会执行command2,于是生成了main.o。接着,make 会寻找生成 print_hello.o 的规则,找到了规则3,然后执行command3,生成了print_hello.o。最后,make 会寻找生成 print_nihao.o 的规则,找到了规则4,然后执行command4,生成了print_nihao.o。
总之,无论最终的目标的依赖文件是否存在,make都会层层的检查依赖关系的时间戳。
3、make引入变量
为了便于 相同批量的 .o 文件的名字 多次使用,可以在make中使用变量,make中变量名对于大小写不敏感。
#举例
#原始写法
main:main.o a.o b.o c.o
g++ -o main main.o a.o b.o c.o
#定义变量后的写法
obj = main.o a.o b.o c.o
main:$(obj)
g++ -o main $(obj)
make引入变量的好处:
- 避免批量的名字 写多次
- 当批量名字需要追加时 只需要改动一个位置
4. make的自动推导
- (1)make看到.o文件,会自动的将.cpp文件加到依赖关系中。
- (2)当将.o文件对应的.cpp 文件添加到依赖关系后,make会自动的推导出 .cpp 生成 .o的命令:
cc -o x -c x.cpp
因此我们可以将make的规则 简化。(不需要再写其依赖的源文件,也不需要在写生成.o的命令)
于是我们例子中的代码可以简化为:
file5:makefile
main:main.o print_hello.o print_nihao.o #规则1
g++ -o main main.o print_hello.o print_nihao.o
规则2、规则3、规则4、规则5 make会自动的推导出来。make 的这种自动推导的规则被称为隐晦规则。
5、 伪目标
make可以定义伪目标,伪目标类似于C语言中的label, 只有显示的声明生成该目标,make才会执行生成该目标的命令。在makefile中通常要有一个clean伪目标,用于清空编译过程中生成的中间文件,通常的写法如下:
.PHONY:clean
clean:
-rm -rf *.o main
注意:
- rm 前的
-
号是指 忽略发生的问题,继续执行。 - clean目标有一个不成文的规定,一般clean目标放在makefile文件的最末尾。