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的同名文件的干扰):
- 加入常用参数(-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
|
练习
![文件存放形式](https://api2.mubu.com/v3/document_image/d1182118-df4b-4b46-8829-eff04eb8168a-11197877.jpg)
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
|