makefile

makefile 作用

  • 项目代码编译管理
  • 节省编译项目时间
  • 一次编写终身受益

运行规则

  • 若想生成目标,检查规则中的依赖条件是否存在。如果不存在,则寻找是否有规则用来生成该依赖文件。
  • 检查规则中的目标是否需要被更新,必须先检查它的所有依赖,依赖中有任何一个被更新,则目标必须被更新。
    • 分析各个目标和依赖之间的关系
    • 根据依赖关系自底向上执行命令
    • 根据修改时间比目标新旧与否确定更新
    • 如果目标不依赖任何条件,则执行对应命令,以示更新

一个最简单的makefile:

1
2
hello:hello.c
gcc hello.c -o hello

考虑中间步骤:

1
2
3
4
hello:hello.o
gcc hello.c -o hello
hello.o:hello.c
gcc hello.c-o hello.o

makefile使用

一个规则

多文件联编:

1
2
hello:hello.o
gcc hello.c add.c sub.c div1.c -o hello

考虑到多文件编译的时间成本,应该先将个各个模块编译成.o目标文件,由目标文件链接成可执行文件。这样只有改动过的模块会被再次编译,其他的保持不变。

1
2
3
4
5
6
7
8
hello:hello.o
gcc hello.o add.o sub.o div1.o -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
sub.o:sub.c
gcc -c add.c -o add.o
div1.o:div1.c
gcc -c div1.c -o div1.o
  • 当依赖条件的时间比目标的时间还晚, 说明目标该更新了。

  • 依赖条件如果不存在, 找寻新的规则去产生依赖

make只会认为第一行是自己的最终目标, 如果最终目标没有写在第一行, 通过ALL来指定;

1
2
3
4
5
6
7
8
9
10
11
12
13
ALL:hello

hello.o:hello.c
gcc -c hello.c -o hello.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
div1.o:div1.c
gcc -c div1.c -o div1.o

hello:hello.o add.o sub.o div1.o
gcc hello.o add.o sub.o div1.o -o hello

两个函数和clean

  • src=$(wildcard ./*.c):匹配当前目录下的所有.c源文件, 赋值给变量src(与shell类似, 变量只有字符串类型)

  • obj=$(patsubst %.c,%.o,$(src)):将参数3中包含参数1的部分替换为参数2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))

ALL:hello
hello:$(obj)
gcc $(obj) -o hello

hello.o:hello.c
gcc -c hello.c -o hello.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
div1.o:div1.c
gcc -c div1.c -o div1.o

clean:
-rm -rf $(obj) hello
  • 执行make clean时务必加上-n参数检查, 避免把源码删掉
  • clean相当于一个没有依赖条件的规则
  • rm前面的横杠表示出错(文件不存在)仍然执行

三个自动变量和模式规则

三个自动变量:
  • $@:在规则的命令中, 表示规则中的目标
  • $^:在规则的命令中, 表示所有依赖条件
  • $<:在规则的命令中, 表示第一个依赖条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))

ALL:hello
hello:$(obj)
gcc $^ -o $@ #目标依赖于所有依赖条件

hello.o:hello.c
gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件
add.o:add.c
gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件
sub.o:sub.c
gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件
div1.o:div1.c
gcc -c $< -o $@ #目标依赖于第一个(唯一一个)依赖条件

clean:
-rm -rf $(obj) hello
模式规则:

鉴于上面的都是某个.o文件依赖于某个.c文件的形式, 可以将其总结为一个模式规则:

1
2
%.o:%.c
gcc -c $< -o $@

关于$<:如果将该变量应用在模式规则中, 它可将依赖条件列表中的依赖项依次取出, 套用模式规则:

1
2
3
4
5
6
7
8
9
10
11
12
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))

ALL:hello
hello:$(obj)
gcc $^ -o $@

%.o:%.c
gcc -c $< -o $@

clean:
-rm -rf $(obj) hello

加入了模式规则后, 当再加入新的模块, 比如mul模块, 不需要改动makefile就可以实现自动编译链接, 非常的方便.

扩展
  • 静态模式规则(制定了模式规则给谁用):
1
2
$(obj)%.o:%.c
gcc -c $< -o $@
  • 加入伪目标(为了防止目录下的与clean和ALL的同名文件的干扰):
1
.PHONY:clean ALL
  • 加入常用参数(-Wall, -I, -l, -L, -g), 形成最终版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
src=$(wildcard ./*.c)
obj=$(patsubst %.c,%.o,$(src))

myArgs=-Wall -g

ALL:hello
hello:$(obj)
gcc $^ -o $@ $(myArgs)

%.o:%.c
gcc -c $< -o $@ $(myArgs)
clean:
-rm -rf $(obj) hello

.PHONY:clean ALL
练习

文件存放形式

makefile文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
src=$(wildcard ./src/*.c)
obj=$(patsubst ./src/%.c,./obj/%.o,$(src)) #注意百分号的匹配和锁定作用

myArgs=-Wall -g
inc_path=./inc #头文件所在目录

ALL:hello
hello:$(obj)
gcc $^ -o $@ $(myArgs)

$(obj):./obj/%.o:./src/%.c #目标和依赖都需要改变
gcc -c $< -o $@ $(myArgs) -I $(inc_path)

.PHONY: ALL clean

clean:
-rm -rf ./obj/*.o hello

当文件名不叫makefile:

1
2
make -f m1
make -f m1 clean