基本概念
- 需要得到什么就make什么,比如
make abc.txt
得到一个文本文件,make image
得到一个docker 镜像 , 等等。 - 一般来说使用 make命令的目录下应该一个名为 Makefile的文件。
make -f <makefile>
使用自定义的文件名。 - Makefile 是一组规则的集合,他们给出了得到目标(target)所需的所有步骤,好比做一道菜需要的所有原料和工艺流程。
- 规则的基本语法为:
<target>: <prerequisites...>
<commands>
其中 target 是必须的,prerequisites和commands非必需,但二者必有其一。
- 如果只打 make,默认构建第一个目标。
- make 默认会输出其将要执行的命令,如果不想看到这些输出,可以在命令前加@。
- make命令列表中的每一行命令,都会在一个独立的shell中执行,因此上一行命令中设置的变量在下一行中不可用。
- make 的赋值符号是 := 而不是 =
make中的特殊变量,美元符号
- $@ 表示当前正在制作(make)的目标文件。比如
make foo
, 那么 $@ 就是 foo。 助记:@表示argument。 - $< 表示一个输入文件(先决条件中的第一个)。 助记:< 是bash中的文件输入符。
- $^ 所有输入文件, 多次出现的文件在其中仅出现一次。 助记:< 竖起来就是 ^。
- $? 所有比target新的输入文件。 助记:? 好比一个问题:“你为什么做这个? 更改了哪些文件以使其必需?”。
- $$ 真正的美元符号。
- $* 规则中百分号%匹配的东西。 助记: 在make中,百分号%相当于shell中的星号*。
- $(@D) 和 $(@F) 分别匹配$@ 的路径部分和文件部分。以上其他变量同理,比如$(<D) 和 $(<F)。
- 使用make特殊变量的例子:
之前:
result.txt: source.txt
@echo "building result.txt from source.txt"
cp source.txt result.txt
source.txt:
@echo "building source.txt"
echo "this is the source" > source.txt
终端输出:
building source.txt
echo "this is the source" > source.txt
building result.txt from source.txt
cp source.txt result.txt
现在:
result-using-var.txt: source.txt
@echo "buildling result-using-var.txt using the $$< and $$@ vars"
cp $< $@
终端输出:
buildling result-using-var.txt using the $< and vars
cp source.txt result-using-var.txt
使用通配符%批量生成文件
src/%.txt:
@# First things first, create the dir if it doesn't exist.
@# Prepend with @ because srsly who cares about dir creation
@[ -d src ] || mkdir src
@# then, we just echo some data into the file
@# The $* expands to the "stem" bit matched by %
@# So, we get a bunch of files with numeric names, containing their number
echo $* > $@
在终端输入: make src/01.txt
终端输出: echo 01 > src/01.txt
。 src目录下生成 01.txt 文件, 内容为:01。
这样只能一个个文件依次生成。现在添加下面几行:
srcfiles := $(shell echo src/{00..99}.txt)
source: $(srcfiles)
在终端输入: make source
, 此时批量生成了100个文件。
echo 0 > src/0.txt
echo 1 > src/1.txt
echo 2 > src/2.txt
echo 3 > src/3.txt
echo 4 > src/4.txt
echo 5 > src/5.txt
echo 6 > src/6.txt
echo 7 > src/7.txt
echo 8 > src/8.txt
echo 9 > src/9.txt
echo 10 > src/10.txt
echo 11 > src/11.txt
echo 12 > src/12.txt
echo 13 > src/13.txt
echo 14 > src/14.txt
echo 15 > src/15.txt
echo 16 > src/16.txt
echo 17 > src/17.txt
echo 18 > src/18.txt
echo 19 > src/19.txt
echo 20 > src/20.txt
echo 21 > src/21.txt
echo 22 > src/22.txt
echo 23 > src/23.txt
echo 24 > src/24.txt
echo 25 > src/25.txt
echo 26 > src/26.txt
echo 27 > src/27.txt
echo 28 > src/28.txt
echo 29 > src/29.txt
echo 30 > src/30.txt
echo 31 > src/31.txt
echo 32 > src/32.txt
echo 33 > src/33.txt
echo 34 > src/34.txt
echo 35 > src/35.txt
echo 36 > src/36.txt
echo 37 > src/37.txt
echo 38 > src/38.txt
echo 39 > src/39.txt
echo 40 > src/40.txt
echo 41 > src/41.txt
echo 42 > src/42.txt
echo 43 > src/43.txt
echo 44 > src/44.txt
echo 45 > src/45.txt
echo 46 > src/46.txt
echo 47 > src/47.txt
echo 48 > src/48.txt
echo 49 > src/49.txt
echo 50 > src/50.txt
echo 51 > src/51.txt
echo 52 > src/52.txt
echo 53 > src/53.txt
echo 54 > src/54.txt
echo 55 > src/55.txt
echo 56 > src/56.txt
echo 57 > src/57.txt
echo 58 > src/58.txt
echo 59 > src/59.txt
echo 60 > src/60.txt
echo 61 > src/61.txt
echo 62 > src/62.txt
echo 63 > src/63.txt
echo 64 > src/64.txt
echo 65 > src/65.txt
echo 66 > src/66.txt
echo 67 > src/67.txt
echo 68 > src/68.txt
echo 69 > src/69.txt
echo 70 > src/70.txt
echo 71 > src/71.txt
echo 72 > src/72.txt
echo 73 > src/73.txt
echo 74 > src/74.txt
echo 75 > src/75.txt
echo 76 > src/76.txt
echo 77 > src/77.txt
echo 78 > src/78.txt
echo 79 > src/79.txt
echo 80 > src/80.txt
echo 81 > src/81.txt
echo 82 > src/82.txt
echo 83 > src/83.txt
echo 84 > src/84.txt
echo 85 > src/85.txt
echo 86 > src/86.txt
echo 87 > src/87.txt
echo 88 > src/88.txt
echo 89 > src/89.txt
echo 90 > src/90.txt
echo 91 > src/91.txt
echo 92 > src/92.txt
echo 93 > src/93.txt
echo 94 > src/94.txt
echo 95 > src/95.txt
echo 96 > src/96.txt
echo 97 > src/97.txt
echo 98 > src/98.txt
echo 99 > src/99.txt
养成添加伪目标(phony)的好习惯
伪目标是为了防止这样一种情况出现: 比如上文出现的source,其本身并不是为了生成什么文件,只是执行一条命令或操作,此时假设当前目录下恰好有一个文件名为source,那么这条make命令就不会被执行,因为make认为需要生成的目标已经存在。
添加伪目标: .PHONY: source
, 使得无论什么情况下输入 make source
, make都会执行source下的操作。
生成目标文件
添加以下内容:
dest/%.txt: src/%.txt
@[ -d dest ] || mkdir dest
cp $< $@
此时输入 make dest/02.txt
, 终端输出:
echo 02 > src/02.txt
cp src/02.txt dest/02.txt
rm src/02.txt
src目录下无文件, dest目录下有一个文件 02.txt
但是现在这样只能一次生成一个目标文件,如何批量生成呢?
加入以下内容:
.PHONY: destination
destfiles := $(patsubst src/%.txt,dest/%.txt,$(srcfiles))
destination: $(destfiles)
输入 make destination
, 终端输出
echo 0 > src/0.txt
cp src/0.txt dest/0.txt
echo 1 > src/1.txt
cp src/1.txt dest/1.txt
echo 2 > src/2.txt
cp src/2.txt dest/2.txt
echo 3 > src/3.txt
cp src/3.txt dest/3.txt
echo 4 > src/4.txt
cp src/4.txt dest/4.txt
echo 5 > src/5.txt
cp src/5.txt dest/5.txt
echo 6 > src/6.txt
cp src/6.txt dest/6.txt
echo 7 > src/7.txt
cp src/7.txt dest/7.txt
echo 8 > src/8.txt
cp src/8.txt dest/8.txt
echo 9 > src/9.txt
cp src/9.txt dest/9.txt
echo 10 > src/10.txt
cp src/10.txt dest/10.txt
echo 11 > src/11.txt
cp src/11.txt dest/11.txt
echo 12 > src/12.txt
cp src/12.txt dest/12.txt
echo 13 > src/13.txt
cp src/13.txt dest/13.txt
echo 14 > src/14.txt
cp src/14.txt dest/14.txt
echo 15 > src/15.txt
cp src/15.txt dest/15.txt
echo 16 > src/16.txt
cp src/16.txt dest/16.txt
echo 17 > src/17.txt
cp src/17.txt dest/17.txt
echo 18 > src/18.txt
cp src/18.txt dest/18.txt
echo 19 > src/19.txt
cp src/19.txt dest/19.txt
echo 20 > src/20.txt
cp src/20.txt dest/20.txt
echo 21 > src/21.txt
cp src/21.txt dest/21.txt
echo 22 > src/22.txt
cp src/22.txt dest/22.txt
echo 23 > src/23.txt
cp src/23.txt dest/23.txt
echo 24 > src/24.txt
cp src/24.txt dest/24.txt
echo 25 > src/25.txt
cp src/25.txt dest/25.txt
echo 26 > src/26.txt
cp src/26.txt dest/26.txt
echo 27 > src/27.txt
cp src/27.txt dest/27.txt
echo 28 > src/28.txt
cp src/28.txt dest/28.txt
echo 29 > src/29.txt
cp src/29.txt dest/29.txt
echo 30 > src/30.txt
cp src/30.txt dest/30.txt
echo 31 > src/31.txt
cp src/31.txt dest/31.txt
echo 32 > src/32.txt
cp src/32.txt dest/32.txt
echo 33 > src/33.txt
cp src/33.txt dest/33.txt
echo 34 > src/34.txt
cp src/34.txt dest/34.txt
echo 35 > src/35.txt
cp src/35.txt dest/35.txt
echo 36 > src/36.txt
cp src/36.txt dest/36.txt
echo 37 > src/37.txt
cp src/37.txt dest/37.txt
echo 38 > src/38.txt
cp src/38.txt dest/38.txt
echo 39 > src/39.txt
cp src/39.txt dest/39.txt
echo 40 > src/40.txt
cp src/40.txt dest/40.txt
echo 41 > src/41.txt
cp src/41.txt dest/41.txt
echo 42 > src/42.txt
cp src/42.txt dest/42.txt
echo 43 > src/43.txt
cp src/43.txt dest/43.txt
echo 44 > src/44.txt
cp src/44.txt dest/44.txt
echo 45 > src/45.txt
cp src/45.txt dest/45.txt
echo 46 > src/46.txt
cp src/46.txt dest/46.txt
echo 47 > src/47.txt
cp src/47.txt dest/47.txt
echo 48 > src/48.txt
cp src/48.txt dest/48.txt
echo 49 > src/49.txt
cp src/49.txt dest/49.txt
echo 50 > src/50.txt
cp src/50.txt dest/50.txt
echo 51 > src/51.txt
cp src/51.txt dest/51.txt
echo 52 > src/52.txt
cp src/52.txt dest/52.txt
echo 53 > src/53.txt
cp src/53.txt dest/53.txt
echo 54 > src/54.txt
cp src/54.txt dest/54.txt
echo 55 > src/55.txt
cp src/55.txt dest/55.txt
echo 56 > src/56.txt
cp src/56.txt dest/56.txt
echo 57 > src/57.txt
cp src/57.txt dest/57.txt
echo 58 > src/58.txt
cp src/58.txt dest/58.txt
echo 59 > src/59.txt
cp src/59.txt dest/59.txt
echo 60 > src/60.txt
cp src/60.txt dest/60.txt
echo 61 > src/61.txt
cp src/61.txt dest/61.txt
echo 62 > src/62.txt
cp src/62.txt dest/62.txt
echo 63 > src/63.txt
cp src/63.txt dest/63.txt
echo 64 > src/64.txt
cp src/64.txt dest/64.txt
echo 65 > src/65.txt
cp src/65.txt dest/65.txt
echo 66 > src/66.txt
cp src/66.txt dest/66.txt
echo 67 > src/67.txt
cp src/67.txt dest/67.txt
echo 68 > src/68.txt
cp src/68.txt dest/68.txt
echo 69 > src/69.txt
cp src/69.txt dest/69.txt
echo 70 > src/70.txt
cp src/70.txt dest/70.txt
echo 71 > src/71.txt
cp src/71.txt dest/71.txt
echo 72 > src/72.txt
cp src/72.txt dest/72.txt
echo 73 > src/73.txt
cp src/73.txt dest/73.txt
echo 74 > src/74.txt
cp src/74.txt dest/74.txt
echo 75 > src/75.txt
cp src/75.txt dest/75.txt
echo 76 > src/76.txt
cp src/76.txt dest/76.txt
echo 77 > src/77.txt
cp src/77.txt dest/77.txt
echo 78 > src/78.txt
cp src/78.txt dest/78.txt
echo 79 > src/79.txt
cp src/79.txt dest/79.txt
echo 80 > src/80.txt
cp src/80.txt dest/80.txt
echo 81 > src/81.txt
cp src/81.txt dest/81.txt
echo 82 > src/82.txt
cp src/82.txt dest/82.txt
echo 83 > src/83.txt
cp src/83.txt dest/83.txt
echo 84 > src/84.txt
cp src/84.txt dest/84.txt
echo 85 > src/85.txt
cp src/85.txt dest/85.txt
echo 86 > src/86.txt
cp src/86.txt dest/86.txt
echo 87 > src/87.txt
cp src/87.txt dest/87.txt
echo 88 > src/88.txt
cp src/88.txt dest/88.txt
echo 89 > src/89.txt
cp src/89.txt dest/89.txt
echo 90 > src/90.txt
cp src/90.txt dest/90.txt
echo 91 > src/91.txt
cp src/91.txt dest/91.txt
echo 92 > src/92.txt
cp src/92.txt dest/92.txt
echo 93 > src/93.txt
cp src/93.txt dest/93.txt
echo 94 > src/94.txt
cp src/94.txt dest/94.txt
echo 95 > src/95.txt
cp src/95.txt dest/95.txt
echo 96 > src/96.txt
cp src/96.txt dest/96.txt
echo 97 > src/97.txt
cp src/97.txt dest/97.txt
echo 98 > src/98.txt
cp src/98.txt dest/98.txt
echo 99 > src/99.txt
cp src/99.txt dest/99.txt
体验make的美好
假设现在需要一个最终的目标程序,其将所有这些文件的内容合在一起输出到一个名为kitty的文件中:
kitty: $(destfiles)
@# Remember, $< is the input file, but $^ is ALL the input files.
@# Cat them into the kitty.
cat $^ > kitty
输入make kitty
cat kitty
, 终端输出:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
修改其中一个源文件: vi src/25.txt
, 将其内容改成: file 25.txt is changed!
再次执行 make kitty
cat kitty
, 发现终端输出的 25 变成了 file 25.txt is changed!
make的妙处就在于,其不会每次重新生成所有源文件,而只会重新构建源文件发生变化的那个目标文件。
通行做法:用于测试的目标 test
test: kitty
@echo "miao" && echo "tests all pass!"
通常都会在makefile中加入test。此处将kitty作为先决条件,也就是kitty必须存在才能通过测试。
通行做法:用于清理的目标 clean
clean:
rm -rf *.txt src dest kitty
将make生成的所有东西删除。
make 发生错误
make中只要有一条命令返回非零值,就会停止执行。加入以下内容:
.PHONY: badkitty
badkitty:
$(MAKE) kitty # The special var $(MAKE) means "the make currently in use"
false # <-- this will fail
echo "should not get here"
输入:make badkitty
, 终端输出:
/Library/Developer/CommandLineTools/usr/bin/make kitty # The special var /Library/Developer/CommandLineTools/usr/bin/make means "the make currently in use"
cat dest/0.txt dest/1.txt dest/2.txt dest/3.txt dest/4.txt dest/5.txt dest/6.txt dest/7.txt dest/8.txt dest/9.txt dest/10.txt dest/11.txt dest/12.txt dest/13.txt dest/14.txt dest/15.txt dest/16.txt dest/17.txt dest/18.txt dest/19.txt dest/20.txt dest/21.txt dest/22.txt dest/23.txt dest/24.txt dest/25.txt dest/26.txt dest/27.txt dest/28.txt dest/29.txt dest/30.txt dest/31.txt dest/32.txt dest/33.txt dest/34.txt dest/35.txt dest/36.txt dest/37.txt dest/38.txt dest/39.txt dest/40.txt dest/41.txt dest/42.txt dest/43.txt dest/44.txt dest/45.txt dest/46.txt dest/47.txt dest/48.txt dest/49.txt dest/50.txt dest/51.txt dest/52.txt dest/53.txt dest/54.txt dest/55.txt dest/56.txt dest/57.txt dest/58.txt dest/59.txt dest/60.txt dest/61.txt dest/62.txt dest/63.txt dest/64.txt dest/65.txt dest/66.txt dest/67.txt dest/68.txt dest/69.txt dest/70.txt dest/71.txt dest/72.txt dest/73.txt dest/74.txt dest/75.txt dest/76.txt dest/77.txt dest/78.txt dest/79.txt dest/80.txt dest/81.txt dest/82.txt dest/83.txt dest/84.txt dest/85.txt dest/86.txt dest/87.txt dest/88.txt dest/89.txt dest/90.txt dest/91.txt dest/92.txt dest/93.txt dest/94.txt dest/95.txt dest/96.txt dest/97.txt dest/98.txt dest/99.txt > kitty
false # <-- this will fail
make: *** [badkitty] Error 1