环境准备
- Linux下自带make,如果没有sudo apt install make即可
- Windows下使用make比较麻烦,因为MinGW实际上是Linux的gcc在Windows下的移植版本,而make的名字不叫make,所以需要改exe的名字
在MinGW的bin目录下将mingw32-make.exe改成make,如果你配置好了MinGW的环境变量,这里理所应当的可以直接在terminal里调用make的,然后就可以从事makefile的编写了
makefile的编写:基础版
假设你有main.cpp, func1.cpp, func2.cpp三个独立的cpp文件,然后希望将其编译到成一个可执行文件res(这里和编译时的强类型和弱类型有关,实际上并不推荐这种写法)1
2
3
4res: main.cpp func1.cpp func2.cpp
g++ -g main.cpp func1.cpp func2.cpp -o res
clean:
del *.o res.exe
- 有两个重要的问题:
- 项目比较大的时候,一行规则不够
- 改一个文件就要整个项目重新编译,很蠢
makefile的编写:进阶版
这里采用了header.h文件让main.cpp能够比较靠谱地调用另外两个cpp文件里的函数。在header.h文件里声明他们,然后在func1.cpp和func2.cpp文件里实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// header.h
using namespace std;
class listNode{
public:
int val;
listNode* next;
listNode(int v):val(v), next(nullptr){}
};
void func1();// only declared
void func2();// only declared
// func1.cpp
using namespace std;
void func1(){
cout << "func1" << endl;
}
//fun2.cpp
...
// main.cpp
using namespace std;
int main(){
func1();
func2();
system("pause");
return 0;
}
然后makefile的编写如下所示:1
2
3
4
5
6
7
8
9
10res: main.o func1.o func2.o
g++ main.o func1.o func2.o -o res
main.o: main.cpp header.h
g++ -c main.cpp -o main.o
func1.o: func1.cpp
g++ -c func1.cpp -o func1.o
func2.o: func2.cpp
g++ -c func2.cpp -o func2.o
clean:
del *.o res.exe
- 优点:当有一个文件发生改动时,只需重新编译此文件即可,而无需重新编译整个项目
- 缺点:
- 里面存在一些重复的内容,可以考虑用变量代替
- 后面三条规则非常类似,可以考虑用一条模式规则代替
makefile的编写:高级版
1 | obj = main.o func1.o func2.o |
使用的规则如下:即所有的.o文件都由对应的.c文件生成。1
2%.o: %.c
$(CC) -c $< -o $@
- 自动变量有很多种:
- $<:第一个依赖文件
- $@:目标
- $^:所有不重复的依赖文件,以空格分开
- 优点:可以减少重复的内容
- 缺点:
- obj对应的文件需要一个个输入,工作量大
- 文件数目比较少时还好,文件数目一旦很多的话,obj将很长
- 而且每增加/删除一个文件,都需要修改Makefile
makefile的编写:究极版
有两个重要函数:wildcard和patsubst。
…
…
然后我就不会了,平时也没什么机会用到1
2
3
4
5
6
7
8
9
10
11
12
13
14
15src = $(wildcard ./*.c)
obj = $(patsubst %.c, %.o, $(src))
#obj = $(src:%.c=%.o)
target = app
CC = gcc
$(target): $(obj)
$(CC) $(obj) -o $(target)
%.o: %.c
$(CC) -c $< -o $@
clean:
rm -rf $(obj) $(target)