http://www.ruanyifeng.com/blog/2015/02/make.html https://gist.github.com/isaacs/62a2d1825d04437c6f08

0x1d0a62f5e4a2afd2be8fefbc8da0a38224f0ba708c0da9a5a0a9b3275a697f8d

  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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# Hello, and welcome to makefile basics.
#
# You will learn why `make` is so great, and why, despite its "weird" syntax,
# it is actually a highly expressive, efficient, and powerful way to build
# programs.
#
# Once you're done here, go to
# http://www.gnu.org/software/make/manual/make.html
# to learn SOOOO much more.

# To do stuff with make, you type `make` in a directory that has a file called
# "Makefile".  You can also type `make -f <makefile>` to use a different
# filename.
#
# 为了使用 make 完成一些任务, 你需要在有一个叫做 `Makefile` 文件的目录里面键入 `make` . 
# 你可以使用 `make -f <makefile>` 去使用不同的文件名 
# 
# A Makefile is a collection of rules.  Each rule is a recipe to do a specific
# thing, sort of like a grunt task or an npm package.json script.
# 
# Makefile 是系列规则的集合。 每个规则都是做特定事情的秘诀。
#
#
# A rule looks like this:
#
# <target>: <prerequisites...>
# 	<commands>
#
# The "target" is required.  The prerequisites are optional, and the commands
# are also optional, but you have to have one or the other.
#
# Type "make" and see what happens:

tutorial:
	@# todo: have this actually run some kind of tutorial wizard?
	@echo "Please read the 'Makefile' file to go through this tutorial"

# By default, the first target is run if you don't specify one.  So, in this
# dir, typing "make" is the same as typing "make tutorial"
#
# 默认情况下, 如果你没有指定一个 target, 那么个第一个 target 将会被运行。  
# 
# By default, make prints out the command before it runs it, so you can see
# what it's doing.  This is a departure from the "success should be silent"
# UNIX dogma, but without that default, it'd be very difficult to see what
# build logs etc are actually doing.
#
# To suppress the output, we've added @ signs before each line, above.
#
# 为了去压缩输出内容, 我们能够添加 @ 符号到上面的每一行。
# 
# Each line of the command list is run as a separate invocation of the shell.
# So, if you set a variable, it won't be available in the next line! To see
# this in action, try running `make var-lost`
# 
# 命令列表的每一行都作为单独的 shell 调用运行。 
# 因此, 如果你设置了一个变量, 那么这个变量在下一行的命中中将是不可见的。 

var-lost:
	export foo=bar
	echo "foo=[$$foo]"

# Notice that we have to use a double-$ in the command line.  That is because
# each line of a makefile is parsed first using the makefile syntax, and THEN
# the result is passed to the shell.
# Let's try running both of the commands in the *same* shell invocation, by
# escaping the \n character.  Run `make var-kept` and note the difference.
# 
# 注意, 在命令行中我们使用两个 $, 这是因为在 makefile 中的每一行首先会使用 makefile 语法去解析, 
# 然后把结果传递给shell 
# 通过转义 \n 字符, 可以让多行命令在同一个 shell 调用中被运行。 


var-kept:
	export foo=bar; \
	echo "foo=[$$foo]"

# Now let's try making something that depends on something else.  In this case,
# we're going to create a file called "result.txt" which depends on
# "source.txt".

result.txt: source.txt
	@echo "building result.txt from source.txt"
	cp source.txt result.txt

# When we type `make result.txt`, we get an error!
# $ make result.txt
# make: *** No rule to make target `source.txt', needed by `result.txt'.  Stop.
#
# The problem here is that we've told make to create result.txt from
# source.txt, but we haven't told it how to get source.txt, and the file is
# not in our tree right now.
#
# Un-comment the next ruleset to fix the problem.
#
source.txt:
	@echo "building source.txt"
	@echo "this is the source" > source.txt
#
# Run `make result.txt` and you'll see it first creates source.txt, and then
# copies it to result.txt.  Try running `make result.txt` again, and you'll see
# that nothing happens!  That's because the dependency, source.txt, hasn't
# changed, so there's no need to re-build result.txt.
#
# Run `touch source.txt`, or edit the file, and you'll see that
# `make result.txt` re-builds the file.
#
#
# Let's say that we were working on a project with 100 .c files, and each of
# those .c files we wanted to turn into a corresponding .o file, and then link
# all the .o files into a binary.  (This is effectively the same if you have
# 100 .styl files to turn into .css files, and then link together into a big
# single concatenated main.min.css file.)
#
# It would be SUPER TEDIOUS to create a rule for each one of those.  Luckily,
# make makes this easy for us.  We can create one generic rule that handles
# any files matching a specific pattern, and declare that we're going to
# transform it into the corresponding file of a different pattern.
# 
# 我们能够创建一个通用的规则, 它能够处理任何匹配特定模式的任何文件。 
#
# Within the ruleset, we can use some special syntax to refer to the input
# file and the output file.  Here are the special variables:
#
# 在规则的集合中, 我们能够使用的一些特别的语法去引用输入文件和输出文件。 下面是一些特别的变量。 
#
#
# $@  The file that is being made right now by this rule (aka the "target")
#     You can remember this because it's like the "$@" list in a
#     shell script.  @ is like a letter "a" for "arguments.
#     When you type "make foo", then "foo" is the argument.
# 
# $<  The input file (that is, the first prerequisite in the list)
#     You can remember this becasue the < is like a file input
#     pipe in bash.  `head <foo.txt` is using the contents of
#     foo.txt as the input.  Also the < points INto the $
#
# $^  This is the list of ALL input files, not just the first one.
#     You can remember it because it's like $<, but turned up a notch.
#     If a file shows up more than once in the input list for some reason,
#     it's still only going to show one time in $^.
#
# $?  All the input files that are newer than the target
#     It's like a question. "Wait, why are you doing this?  What
#     files changed to make this necessary?"
#
# $$  A literal $ character inside of the rules section
#     More dollar signs equals more cash money equals dollar sign.
#
# $*  The "stem" part that matched in the rule definition's % bit
#     You can remember this because in make rules, % is like * on
#     the shell, so $* is telling you what matched the pattern.
#
# You can also use the special syntax $(@D) and $(@F) to refer to
# just the dir and file portions of $@, respectively.  $(<D) and
# $(<F) work the same way on the $< variable.  You can do the D/F
# trick on any variable that looks like a filename.
#
# There are a few other special variables, and we can define our own
# as well.  Most of the other special variables, you'll never use, so
# don't worry about them.
#
# So, our rule for result.txt could've been written like this
# instead:

result-using-var.txt: source.txt
	@echo "buildling result-using-var.txt using the $$< and $$@ vars"
	cp $< $@

# Let's say that we had 100 source files, that we want to convert
# into 100 result files.  Rather than list them out one by one in the
# makefile, we can use a bit of shell scripting to generate them, and
# save them in a variable.
# 假如我们有100个源文件, 我们想要转换源文件为结果文件。 我们不要把他们一一列举在
# makefile 文件中, 我们可以使用一点shell脚本生成他们并保存在变量中。 
#
# Note that make uses := for assignment instead of =
# I don't know why that is.  The sooner you accept that this isn't
# bash/sh, the better.
#
# 注意 make 使用 := 代替 = 我不知道为什么会这样。 你越早接受这不是 bash/sh,更好。
#  
# Also, usually you'd use `$(wildcard src/*.txt)` instead, since
# probably the files would already exist in your project.  Since this
# is a tutorial, though we're going to generate them using make.
#
# This will execute the shell program to generate a list of files.
srcfiles := $(shell echo src/{00..99}.txt)

# How do we make a text file in the src dir?
# We define the filename using a "stem" with the % as a placeholder.
# What this means is "any file named src/*.txt", and it puts whatever
# matches the "%" bit into the $* variable.
#
# 我们如何在 src 目录中创建一个文件?
# 我们使用 “stem” 和 当作占位符“%” 定义了一个文件名。 这就意味着“任何名为 src/*.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 $* > $@

# Try running `make src/00.txt` and `make src/01.txt` now.


# To not have to run make for each file, we define a "phony" target that
# depends on all of the srcfiles, and has no other rules.  It's good
# practice to define your phony rules in a .PHONY declaration in the file.
# (See the .PHONY entry at the very bottom of this file.)
#
# 为了不必为每个文件运行 make, 我定义一个依赖于所有的 srcfile 的虚假目标, 并且没有任何规则。 
# 更好的做法使用 `.PHONY`  声明定义你自己的虚假的规则。 
#
#
# Running `make source` will make ALL of the files in the src/ dir.  Before
# it can make any of them, it'll first make the src/ dir itself.  Then
# it'll copy the "stem" value (that is, the number in the filename matched
# by the %) into the file, like the rule says above.
#
# Try typing "make source" to make all this happen.
source: $(srcfiles)


# So, to make a dest file, let's copy a source file into its destination.
# Also, it has to create the destination folder first.
#
# The destination of any dest/*.txt file is the src/*.txt file with
# the matching stem.  You could just as easily say that %.css depends
# on %.styl
dest/%.txt: src/%.txt
	[ -d dest ] || mkdir dest
	cp $< $@

# So, this is great and all, but we don't want to type `make dest/#.txt`
# 100 times!
#
# Let's create a "phony" target that depends on all the destination files.
# We can use the built-in pattern substitution "patsubst" so we don't have
# to re-build the list.  This patsubst function uses the same "stem"
# concept explained above.
#  
# 让我创建一个依赖所有的 destfiles 的虚假目标。 
# 
destfiles := $(patsubst src/%.txt,dest/%.txt,$(srcfiles))
destination: $(destfiles)

# Since "destination" isn't an actual filename, we define that as a .PHONY
# as well (see below).  This way, Make won't bother itself checking to see
# if the file named "destination" exists if we have something that depends
# on it later.
#
# 因为 “destination” 不是一个真正的文件, 我们可以把他定义为 `.PHONY` 
# 
# Let's say that all of these dest files should be gathered up into a
# proper compiled program.  Since this is a tutorial, we'll use the
# venerable feline compiler called "cat", which is included in every
# posix system because cats are wonderful and a core part of UNIX.

kitty: $(destfiles)
	@# Remember, $< is the input file, but $^ is ALL the input files.
	@# Cat them into the kitty.
	cat $^ > kitty

# Note what's happening here:
#
# kitty -> (all of the dest files)
# Then, each destfile depends on a corresponding srcfile
#
# If you `make kitty` again, it'll say "kitty is up to date"
#
# NOW TIME FOR MAGIC!
#
# Let's update just ONE of the source files, and see what happens
#
# Run this:  touch src/25.txt; make kitty
#
# Note that it is smart enough to re-build JUST the single destfile that
# corresponds to the 25.txt file, and then concats them all to kitty.  It
# *doesn't* re-generate EVERY source file, and then EVERY dest file,
# every time


# It's good practice to have a `test` target, because people will come to
# your project, and if there's a Makefile, then they'll expect `make test`
# to do something.
#
# We can't test the kitty unless it exists, so we have to depend on that.
test: kitty
	@echo "miao" && echo "tests all pass!"

# Last but not least, `make clean` should always remove all of the stuff
# that your makefile created, so that we can remove bad stuff if anything
# gets corrupted or otherwise screwed up.
clean:
	rm -rf *.txt src dest kitty

# What happens if there's an error!?  Let's say you're building stuff, and
# one of the commands fails.  Make will abort and refuse to proceed if any
# of the commands exits with a non-zero error code.
# To demonstrate this, we'll use the `false` program, which just exits with
# a code of 1 and does nothing else.
badkitty:
	$(MAKE) kitty # The special var $(MAKE) means "the make currently in use"
	false # <-- this will fail
	echo "should not get here"

.PHONY: source destination clean test badkitty