首页 makefile文件相关知识
文章
取消

makefile文件相关知识

makefile文件相关知识

文件命名

makefile或者Makefile

Makefile规则

一个makefile文件中可以有一个或者多个规则

1
2
3
目标 ...: 依赖...
	命令(Shell命令)
	...

目标:最终要生成的文件。(伪目标除外)

依赖:生成目标所需要用到的文件或者是目标

命令:通过执行命令对依赖操作生成目标(命令前必须用Tab缩进)

一般来说,makefile中的其它规则一般都是为第一条规则服务的

语法

自定义变量

1
2
变量名 = 变量值
var = hello

预定义变量

1
2
3
4
5
6
AR: 归档维护程序的名称,默认值为ar
CC: C编译器的名称,默认值为cc
CXX: C++编译器的名称,默认为g++
$@: 目标的完整名称
$<: 第一个依赖文件的名称
$^: 所有的依赖文件

获取变量的值

1
2
3
4
$(变量名)
例如:
var = hello
$(var)就会获取var的值

自动变量只能在规则的命令中使用

1
2
3
4
5
app:main.c a.c b.c
	$(CC) -c $^ -o $@
这里$^是获取所有的依赖。就是main.c a.c b.c
$@ 目标的完整名称就是app
注意这里第一行的依赖名称不可以使用自动变量如 $@ $< $^

makefile示例

尽量不要在第一个规则的命令里直接用一串的.c 因为makefile可以检测哪个文件改动了,针对那一个文件进行生成

如果都写在一起,会每次都重新生成所有文件。因为命令在一行。

如果按照下图这种.o然后分开写则会分步执行。没有更改过的文件不会重新生成

1
2
3
4
5
6
7
8
9
10
11
12
app:sub.o add.o mult.o div.o main.o //注意这里直接执行.o文件 但是我们没有,需要在下面规则写从.c到.o的方法
	gcc sub.o add.o mult.o div.o main.o -o app
sub.o:sub.c
	gcc -c sub.c -o sub.o
add.o:add.c
	gcc -c add.c -o add.o
mult.o:mult.c
	gcc -c mult.c -o mult.o
div.o:div.c
	gcc -c div.c -o div.o
main.o:main.c
	gcc -c main.c -o main.o

我们发现这样非常繁琐。我们可以换一种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
src = sub.o add.o mult.o div.o main.o
target = app
//app:sub.o add.o mult.o div.o main.o
	//gcc sub.o add.o mult.o div.o main.o -o app
$(target):$(src)
	$(CC) $(src) -o $(target)
sub.o:sub.c
	gcc -c sub.c -o sub.o
add.o:add.c
	gcc -c add.c -o add.o
mult.o:mult.c
	gcc -c mult.c -o mult.o
div.o:div.c
	gcc -c div.c -o div.o
main.o:main.c
	gcc -c main.c -o main.o

我们发现这样分开处理下面的依赖依然非常繁琐。我们可以引入模式匹配

1
2
3
4
5
6
7
8
9
%.o:%.c
这里的%通配符是匹配一个字符串。注意这里前后的两个%匹配的是同一个字符串。
所以我们可以开始这样写
%.o:%.c
	gcc -c $< -o $@ //这里$<指的是第一个依赖文件的名称。我们这儿只有一个所以就用这个

放入示例就是
%.o:%.c
	$(CC) -c $< -o $@

之后我们的makefile就会变成这样

1
2
3
4
5
6
src = sub.o add.o mult.o div.o main.o
target = app
$(target):$(src)
	$(CC) $(src) -o $(target)
%.o:%.c
	$(CC) -c $< -o $@

但是到这儿了,我们还是有一个问题。第一行的src依旧有一大串需要自己写。我们就引入函数功能

1
$(wildcard PATTERN...)

这个函数详细信息:

​ 功能:获取指定目录下指定类型的文件列表

​ 参数:PATTERN指的是某个或者多个目录下的对应的某种类型的文件。如果有多个目录,一般使用空格间隔

​ 返回:得到的若干个文件的文件列表。文件名之间使用空格间隔

​ 示例:

1
2
3
$(wildcard *.c ./sub/*.c) 这行指的是获取当前目录下所有的.c文件和 ./sub目录下所有的.c文件
返回值格式是
a.c b.c c.c d.c e.c f.c ...

但是这里我们需要.o文件。但是.o文件还没有被生成,需要靠着下面的子规则生成。我们怎么办?我们可以引入匹配替换函数

1
$(patsubst <pattern>, <replacement>, <text)

这个函数详细信息:

​ 功能:查找<text>中的单词(单词以空格,tab,回车或换行分割)是否符合模式<pattern>。如果匹配,则以<replacement>替换

<pattern>可以包括通配符%表示任意长度的子串。如果<replacement>中也包含% 那么<replacement>中的这个%和<pattern>中的%将是一个子串

​ 返回:函数返回被替换过后的子字符串

​ 示例

1
2
$(patsubst %.c, %.o, x.c bar.c)
意思是将x.c 和bar.c里面的.c替换成.o

在这之后,我们就可以这样写了

1
2
3
4
5
6
7
src = $(wildcard ./*.c) //先获取路径下所有.c文件
objs = $(patsubst %.c, %.o, $(src)) //把.c都换成.o
target = app
$(target):$(objs)
	$(CC) $(objs) -o $(target)
%.o:%.c
	$(CC) -c $< -o $@

清理阶段

我们发现编译后生成了很多无用的中间文件 比如.o文件。我们可以在上面的文件里面加入一个clean规则

1
2
clean:
	rm $(objs) -f

注意,这里直接运行makefile是不会自动执行clean规则,因为makefile不会执行任何与第一条规则无关的指令

所以我们需要显式使用即可

1
make clean

注意。这里我们会生成一个clean文件。这样做不仅没有意义,而且会导致报错如clean已是最新

我们可以使用生成伪目标来解决这个问题

1
2
3
.PHONY:clean
clean:
	rm $(objs) -f

这样做就解决了所有问题

整体文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//获取变量阶段
src = $(wildcard ./*.c) //先获取路径下所有.c文件
objs = $(patsubst %.c, %.o, $(src)) //把.c都换成.o
target = app
//第一条规则(主规则)
$(target):$(objs)
	$(CC) $(objs) -o $(target)
//子规则(处理依赖文件)	
%.o:%.c
	$(CC) -c $< -o $@
//清理阶段
.PHONY:clean
clean:
	rm $(objs) -f
本文由作者按照 CC BY 4.0 进行授权