make是如何工作的
在默认的方式下,也就是我们只输入 make 命令。那么,
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
- 如果目标文件(target)不存在,或是目标文件(target)所依赖的后面的 .o 文件的文件修改时间要比 目标文件(target) 这个文件新,那么,他就会执行后面所定义的命令来生成 目标文件(target)这个文件。
- 如果 目标文件(target)所依赖的 .o 文件也不存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程)
- 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生成make的终极任务,也就是执行文件 目标文件(target)了。
若makefile中第一个目标文件(target)是伪目标,make将会仅执行第一个目标文件(target),然后退出,后续的伪目标不会被执行。如需要执行所有伪目标,可以:
.PHONY : all cs-build go-build cs-docker go-docker# 执行后续所有伪目标all: cs-build go-build cs-docker go-dockercs-build:@echo "start cs build"cd receive && dotnet clean && dotnet buildgo-build:@echo "start go build"cd send && go vetcs-docker:@echo "start cs docker build"docker build -t asd:8000/csharp-receive:latest ./receivego-docker:@echo "start go docker build"docker build -t asd:8000/go-sned:latest ./send
基本格式
<target> : <prerequisites>
[tab] <commands>
make执行分为两步:1.确认prerequisites文件存在。2.使用commads将prerequisites文件制作为target文件。
“target”是必需的,不可省略;”prerequisites”和”commands”都是可选的,但是两者之中必须至少存在一个。
target
通常是文件名。除了文件名,目标还可以是某个操作的名字,这称为”伪目标”(phony target)。
例
clean:
rm *.o
伪目标
当执行 make clean 时,rm *.o将会被执行,但如果当前文件夹内有一个名为clean的文件,指令将不会被执行。因此可以先声明伪目标:
.PHONY: clean
clean:
rm *.o temp
prerequisites
通常是一组文件名,之间用空格分隔。
Commands
每行命令之前必须有一个tab键。 :::tips 注意:每行命令在一个单独的shell中执行,因此,各行之间没有继承关系。 ::: 可以将命令写在一行,并用分号隔开
export foo=bar; echo "foo=[$$foo]"
或者使用反斜杠
export foo=bar; \
echo "foo=[$$foo]"
语法
回声
test:
# 这是测试
@# 关闭回声
@echo TODO
执行:
$ make test
# 这是测试
TODO
在命令的前面加上@,就可以关闭回声。在命令前加入@则不会输出执行的指令。
模式匹配
假定当前目录下有 f1.c 和 f2.c 两个文件
%.o: %.c
等同于
f1.o: f1.c
f2.o: f2.c
变量
txt = Hello World
test:
@echo $(txt)
调用Shell变量,需要在美元符号前,再加一个美元符号
test:
@echo $$HOME
自动变量
$@ 指代当前目标,就是Make命令当前构建的那个目标。
$< 指代第一个前置条件。
$? 指代比目标更新的所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,其中 p2 的时间戳比 t 新,$?就指代p2。
$^ 指代所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,那么 $^ 就指代 p1 p2 。
$* 指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,$* 就表示 f1。
