项目中的源文件不计其数,按照类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。
make安装的指令为:sudo apt-get install make
一、makefile的书写规则
在Unix系统中,make会默认在当前目录下找到Makefile、GNUmakefile、makefile三者的其中一个作为makefile文件,如果你的makefile文件恰好是这三者之一的话,就可以直接用下面的命令:
lyr@ubuntu:~/Desktop/GCCStudy$ make
gcc hello.c -o hello
如果想要使用其他文件作为makefile文件,则可以利用下面的make命令选项指定makefile文件,命令如下(这里我换一个目录测试):
1. 通用格式
makefile文件通过vim编辑器进行编写,通用格式如下:

- Targets:目标文件,一般Targets文件只有一个,如果有多个的话,那么Target中的第一个文件就是最终目标,make完成的也就是这个目标。
- prerequisites:依赖条件
- command:命令
- command行前必须一个tab键起行
本次试验创建的makefile文件就参照了上面的通用格式:
1 hello:hello.c
2 gcc hello.c -o hello
- 其中,“ : ”前面的文件就是Target,是make操作的最终目标,
2. 不常用格式
![]()
如果这样命令太长,可以使用反斜杠“\”作为换行符,make对一行上有多少字符是没有限制的。
二、make的基本原理
1. 若想生成目标文件,检查规则中的依赖条件是否存在,如果不存在,就需要寻找是否有规则来生成该依赖文件。其实就类似于“向深扎根寻找水源”。
这里我在本目录下创建了hello.c文件,内容如下:
1 #include <stdio.h>
2
3 int main()
4 {
5 printf("Hello, makefile!\n");
6 return 0;
7 }
makefile文件中的总目标是生成Hello可执行文件,步骤为:hello.c→hello.o→hello,makefile中的代码如下:
1 hello:hello.o
2 gcc hello.o -o hello
3
4 hello.o:hello.c
5 gcc -c hello.c -o hello.o
执行1-2行时,发现hello的依赖文件hello.o不存在,这时候开始寻找,发现4-5行进行make操作后会得到hello.o的文件,且hello.o的依赖条件hello.c是存在的。
保存后,执行make:
lyr@ubuntu:~/Desktop/GCCStudy$ ls
hello.c makefile staticStore
lyr@ubuntu:~/Desktop/GCCStudy$ make
gcc -c hello.c -o hello.o
gcc hello.o -o hello
lyr@ubuntu:~/Desktop/GCCStudy$ ls
hello hello.c hello.o makefile staticStore
lyr@ubuntu:~/Desktop/GCCStudy$ ./hello
Hello, makefile!
上述运行结果也能印证,make的执行顺序为“先4-5行,后1-2行”,原因就是得到hello可执行文件的前提是要有hello.o,而hello.o只能通过4-5行的代码才能获得(在hello.c存在的前提下)。
2. 检查规则中的目标是否需要更新,必须先检查他的所有依赖,依赖中有任一个被更新,则规则必须更新。
这里创建一个main.c,函数中会调用hello.c中的内容:
1 #include <stdio.h>
2 void hello();
3 int main(){
4 hello();
5 return 0;
6 }
更改后的makefile如下:
app: main.o hello.o
gcc main.o hello.o -o app
main.o: main.c
gcc -c main.c -o main.o
hello.o: hello.c
gcc -c hello.c -o hello.o
如果执行make时,我使用touch命令对部分依赖文件进行更新,这时候观察结果的变化:
lyr@ubuntu:~/Desktop/GCCStudy$ make
gcc -c main.c -o main.o
gcc -c hello.c -o hello.o
gcc main.o hello.o -o app
lyr@ubuntu:~/Desktop/GCCStudy$ ls
app hello.c hello.o main.c main.o makefile staticStore
lyr@ubuntu:~/Desktop/GCCStudy$ ./app
Hello, makefile!
lyr@ubuntu:~/Desktop/GCCStudy$ touch main.c
lyr@ubuntu:~/Desktop/GCCStudy$ make
gcc -c main.c -o main.o
gcc main.o hello.o -o app
lyr@ubuntu:~/Desktop/GCCStudy$ ./app
Hello, makefile!
在未对main.c文件更新时间戳时,make命令下来,会运行全部的三个命令,而更新时间戳之后,make操作发现main.c文件更新了,就又会重新更新main.c对应的规则;而hello.c没有更新,所以规则不变,这样make操作的运行结果和原来相比就少了一个规则。
三、通配符(*)
如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符,make 最常用的通配符就是*。
通配符代替了一系列的文件,如:*.c表示所有后缀为c的文件。需要注意的是,如果文件名中有通配符,如:*,那么可以用转义字符\,如\*来表示真实的*字符,而不是任意长度的字符串。
如下,可用作对所有同后缀的文件进行rm操作。:
rm -f *.o
四、伪目标
常见的伪目标有clean、install、help、run等,为人为定义。
伪目标并不是一个文件,而是一种“标签”,所以make无法生成其依赖文件和决定其是否要执行。为了避免伪目标和当前路径下的文件重名,可以使用“.PHONY:”标记来显示地指眀一个目标是伪目标,向make说明,不管是否拥有该文件,这个目标就是伪目标。
1 .PHONY: clean
2
3 clean:
4 rm -f *.o
上述命令指眀了“clean”为伪目标,即“非目标文件的命令”,而执行"make clean"时会触发伪目标下的命令,即将“.o”后缀的文件全部强制删除。
lyr@ubuntu:~/Desktop/GCCStudy$ ls
app hello.c hello.o main.c main.o makefile staticStore
lyr@ubuntu:~/Desktop/GCCStudy$ make clean
rm -f *.o
lyr@ubuntu:~/Desktop/GCCStudy$ ls
app hello.c main.c makefile staticStore
五、变量的定义与使用
makefile文件中定义变量的基本语法如下:

这里的“变量”的作用一般是起到减少重复书写的作用,调用变量的方法如下:

示例如下:
1 obj = main.o hello.o #所有的依赖条件
2 main:$(obj)
3 gcc $(obj) -o main
其中在makefile里“#”表示注释。
六、make中的常用函数
1. wildcard
wildcard会按照给定参数匹配文件名,在makefile规则中,通配符“*”会自动展开,但在变量的定义中,通配符“*”会失效,这时候就可以用wildcard展开通配符。
1 obj = $(wildcard *.o)
2 .PHONY: clean
3
4 clean:
5 rm -f obj
这样执行“make clean”就可以将后缀为“.o”的文件强制删除。
2. patsubst
patsubst可以用于匹配替换,常见用法如下:
![]()
含义是将参数3中,参数1的模式替换为参数2的模式,修改的makefile文件如下:
1 src = $(wildcard *.c)
2 obj = $(patsubst %.c, %.o, $(src))
3
4 a.out: $(src)
5 gcc $(src) -o a.out
6
7 main.o: main.c
8 gcc -c main.c -o main.o
9
10 hello.o: hello.c
11 gcc -c hello.c -o hello.o
12
13 .PHONY: clean
14
15 clean:
16 rm -f $(src) a.out
其中,上述makefile的大致逻辑是:src作为变量用来定义“.o”后缀的全部文件,obj是用来将全部“.o”后缀文件的“.o”全部替换为“.c”,通过make命令将两个“.c”文件(main.c和hello.c)编译成“.o”文件,再将两个“.o”文件一起编译成a.out可执行文件。
执行结果如下:
lyr@ubuntu:~/Desktop/GCCStudy/wc$ make
gcc hello.c main.c -o a.out
lyr@ubuntu:~/Desktop/GCCStudy/wc$ ls
a.out hello.c main.c makefile
lyr@ubuntu:~/Desktop/GCCStudy/wc$ ./a.out
Hello World
七、自动变量
- $@:在规则的命令中,表示规则中的目标

- $^:在规则的命令中,表示所有依赖条件

- $<:在规则的命令中,第一个依赖条件(如果将该变量应用在模式规则中它可将依赖条件列表中的依赖依次取出,套用模式规则)

八、模式规则
根据自动变量对目标文件和依赖文件的替换,可以得到makefile如下:
1 src = $(wildcard *.c)
2 obj = $(patsubst %.c, %.o, $(src))
3
4 a.out:$(obj)
5 gcc $^ -o $@
6
7 main.o:main.c
8 gcc -c $< -o $@
9
10 hello.o:hello.c
11 gcc -c $< -o $@
12
13 .PHONY:clean
14 clean:
15 rm -f $(obj) a.out
这样的话,就会发现存在main.o:main.c和hello.o:hello.c的命令是一样的,存在冗余,这时就可以通过模式规则,使用“%.o:%.c”将“目标文件:依赖文件”的原格式替换。
1 src = $(wildcard *.c)
2 obj = $(patsubst %.c, %.o, $(src))
3
4 a.out:$(obj)
5 gcc $^ -o $@
6
7 %.o : %.c
8 gcc -c $< -o $@
9
10 .PHONY:clean
11 clean:
12 rm -f $(obj) a.out
3222

被折叠的 条评论
为什么被折叠?



