make是如何工作的

在默认的方式下,也就是我们只输入 make 命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
  3. 如果目标文件(target)不存在,或是目标文件(target)所依赖的后面的 .o 文件的文件修改时间要比 目标文件(target) 这个文件新,那么,他就会执行后面所定义的命令来生成 目标文件(target)这个文件。
  4. 如果 目标文件(target)所依赖的 .o 文件也不存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生成make的终极任务,也就是执行文件 目标文件(target)了。

若makefile中第一个目标文件(target)是伪目标,make将会仅执行第一个目标文件(target),然后退出,后续的伪目标不会被执行。如需要执行所有伪目标,可以:

  1. .PHONY : all cs-build go-build cs-docker go-docker
  2. # 执行后续所有伪目标
  3. all: cs-build go-build cs-docker go-docker
  4. cs-build:
  5. @echo "start cs build"
  6. cd receive && dotnet clean && dotnet build
  7. go-build:
  8. @echo "start go build"
  9. cd send && go vet
  10. cs-docker:
  11. @echo "start cs docker build"
  12. docker build -t asd:8000/csharp-receive:latest ./receive
  13. go-docker:
  14. @echo "start go docker build"
  15. 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。