前言
首先应该知道 Makefile 是用来给 Linux(Unit)平台下 Make 工具描述源程序之间的相互关系并自动维护编译工作的文件。
Makefile 文件需要按照某种特定语法编写,文件中需要说明如何编译各个源文件并链接生成可执行文件,并要求定义源文件之间的依赖关系。
总而言之,Make 是工具,用户通过编写 Makefile 来告诉 Make 工具如何执行编译工作。
最后,需要指出的是,Windows 下也可以通过 MingW 来安装 Make 工具。
下面,以一个例子来说明如何编写 Makefile,此例子来源于李慧芹 - Makefile 工程文件的编写规则。
工程结构
这个例子的工程结构比较简单,直接看源码就可以知道对应的依赖关系:1
2
3
4
5
6
7
8
9
10
11
12
// 在 main.c 中引入两个头文件,编译 main.c 时,需要提前编译好这两个文件
int main() {
mytool1();
mytool2();
exit(0);
}
在.c
文件中写好函数的定义。1
2
3
4
5
6
void mytool1() {
printf("tool1 print\n\n");
}
在.h
文件中写好函数的声明和条件编译规则,以免重复编译。1
2
3
4
5
6
void mytool1();
tool2
如法炮制。1
2
3
4
5
6
void mytool2() {
printf("tool2 print\n");
}
1 |
|
编写
现在,开始编写 Makefile,需要说明的是,工程的目录下可以存在多个名称为Makefile
的文件,但 Make 工具认为小写的优先级更高。
Makefile 的编写规则比较简单,跟脚本有点类似,语法如下:1
2target: prerequisites
command
其中,target
是要生成的文件,prerequisites
就是编译target
所需要的依赖文件,而command
就是在终端输入的编译命令了。有一点需要注意的是,command
前需要有一个tab
符号,且不能用空格代替,不然 Make 识别不出来。
有了上面的知识,就可以写出下面的 Makefile 了:1
2
3
4
5
6
7
8
9mytool: main.o tool1.o tool2.o
gcc main.o tool1.o tool2.o -o mytool
main.o: main.c
gcc main.c -c -Wall -g -o main.o
tool1.o: tool1.c
gcc tool1.c -c -Wall -g -o tool1.o
tool2.o: tool2.c
gcc tool2.c -c -Wall -g -o tool2.o
此时,在终端输入$ make
后,Make 工具就会按照 Makefile 进行编译。
编译完成后,可以发现目录下,出现了很多.o
的临时文件,需要删除,手动删除太麻烦,同样借助 Makefile,只需要在下面再加上:1
2
3
4
5
6
7
8
9
10
11
12mytool: main.o tool1.o tool2.o
gcc main.o tool1.o tool2.o -o mytool
main.o: main.c
gcc main.c -c -Wall -g -o main.o
tool1.o: tool1.c
gcc tool1.c -c -Wall -g -o tool1.o
tool2.o: tool2.c
gcc tool2.c -c -Wall -g -o tool2.o
clean:
rm *.o mytool -rf
此时,使用 Make 命令编译时就会自动删除多余的.o
文件,并生成一个mytool
的可执行文件。当然,也可以直接在终端输入$ make clean
来清除.o
文件。
简化
观察 m2,可以发现有很多重复的部分,而 Makefile 允许我们定义变量来取代重复且冗长的部分,而变量的引用需要使用$
和()
来完成。
此时,我们可以下面的 Makefile 了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS+=-c -Wall -g
mytool: $(OBJS)
$(CC) $(OBJS) -o mytool
main.o: main.c
$(CC) main.c $(CFLAGS) -o main.o
tool1.o: tool1.c
$(CC) tool1.c $(CFLAGS) -o tool1.o
tool2.o: tool2.c
$(CC) tool2.c $(CFLAGS) -o tool2.o
clean:
$(RM) *.o mytool -r
注意,最下面使用RM
变量代替了rm -f
。
继续简化
实际上,Makefile 提供了一些自动变量运行我们进一步简化 Makefile。比如,在当前某一行的实现上,我们可以用$^
来代替所有的依赖文件,用$@
来代替目标文件。此时 Makefile 就可以写成:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS+=-c -Wall -g
mytool: $(OBJS)
$(CC) $^ -o $@
main.o: main.c
$(CC) $^ $(CFLAGS) -o $@
tool1.o: tool1.c
$(CC) $^ $(CFLAGS) -o $@
tool2.o: tool2.c
$(CC) $^ $(CFLAGS) -o $@
clean:
$(RM) *.o mytool -r
写到这里,你会发现,简化后的文件中,好像有几行都是一样的,是不是还可以简化呢?答案是肯定的...😂
在 Makefile 中%
是一个通配符,可以用来表示当前某行实现上的相同文件名。此时,Makefile 就可以写成:1
2
3
4
5
6
7
8
9
10
11OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS+=-c -Wall -g
mytool: $(OBJS)
$(CC) $^ -o $@
%.o:%.c
$(CC) $^ $(CFLAGS) -o $@
clean:
$(RM) *.o mytool -r
这样写,就更简单了。
结语
这么一通通简化下来,看起来好像是越写越简单了,可读性是越来越差了...😅
不过 Makefile 作为一个工具而言,学会这些也差不多了,剩下的,等遇到了再查书~