Learn Makefiles (3)

Getting Advanced: Commands and execution, Variables Pt. 2

Posted by Zhang Jian on December 18, 2022

Commands and execution

Command Echoing/Silencing

在每一个命令之前加上@可以禁止打印输出,你也可以在运行make时加上-s实现在每一个命令之前加上@的效果。

1
2
3
all: 
	@echo "This make line will not be printed"
	echo "But this will"

Command Execution

每个命令运行在一个新的终端中(or at least the effect is as such)。

1
2
3
4
5
6
7
8
9
10
11
all: 
	cd ..
	# The cd above does not affect this line, because each command is effectively run in a new shell
	echo `pwd`

	# This cd command affects the next because they are on the same line
	cd ..;echo `pwd`

	# Same as above
	cd ..; \
	echo `pwd`

Default Shell

默认的终端是/bin/sh,你可以通过设置SHELL来改变终端

1
2
3
4
SHELL=/bin/bash

cool:
	echo "Hello from bash"

Double dollar sign

在字符串中,如果想要像bash/sh中那样使用$,在make中可以使用$$

1
2
3
4
5
6
7
make_var = I am a make variable
all:
	# Same as running "sh_var='I am a shell variable'; echo $sh_var" in the shell
	sh_var='I am a shell variable'; echo $$sh_var

	# Same as running "echo I am a amke variable" in the shell
	echo $(make_var)

Error handling with -k, -i, and -

在运行make时:加上-k,可以使得:make运行即使出现错误,也能够继续运行。这一般用在:需要一次性查看所有的错误

在运行命令时,加上-,可以忽略此命令的错误

在运行make时:加上-i,可以忽略所有错误

1
2
3
4
one:
	# This error will be printed but ignored, and make will continue to run
	-false
	touch one

Recursive use of make

如果要递归的调用makefile,应当使用$(MAKE)而不是make(because it will pass the make flags for you and won’t itself be affected by them)。

1
2
3
4
5
6
7
8
new_contents = "hello:\n\ttouch inside_file"
all:
	mkdir -p subdir
	printf $(new_contents) | sed -e 's/^ //' > subdir/makefile
	cd subdir && $(MAKE)

clean:
	rm -rf subdir

Export, environments, and recursive make

当make启动时,其会根据当时终端中的环境变量自动创建make变量。

1
2
3
4
5
6
7
# Run this with "export shell_env_var='I am an environment variable'; make"
all:
	# Print out the Shell variable
	echo $$shell_env_var

	# Print out the Make variable
	echo $(shell_env_var)

export可以将变量设置为终端环境可见

1
2
3
4
5
shell_env_var=Shell env var, created inside of Make
export shell_env_var
all:
	echo $(shell_env_var)
	echo $$shell_env_var

当在make中运行make命令时,可以通过export设置变量,从而让sub-make命令也获得此变量。在下面这个例子中,cooly正是如此。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new_contents = "hello:\n\techo \$$(cooly)"

all:
	mkdir -p subdir
	printf $(new_contents) | sed -e 's/^ //' > subdir/makefile
	@echo "---MAKEFILE CONTENTS---"
	@cd subdir && cat makefile
	@echo "---END MAKEFILE CONTENTS---"
	cd subdir && $(MAKE)

# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport cooly

clean:
	rm -rf subdir

变量必须通过export,才能在终端运行

1
2
3
4
5
6
7
8
one=this will only work locally
export two=we can run subcommands with this

all: 
	@echo $(one)
	@echo $$one
	@echo $(two)
	@echo $$two

可以使用.EXPORT_ALL_VARIABLESexport所有变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.EXPORT_ALL_VARIABLES:
new_contents = "hello:\n\techo \$$(cooly)"

cooly = "The subdirectory can see me!"
# This would nullify the line above: unexport cooly

all:
	mkdir -p subdir
	printf $(new_contents) | sed -e 's/^ //' > subdir/makefile
	@echo "---MAKEFILE CONTENTS---"
	@cd subdir && cat makefile
	@echo "---END MAKEFILE CONTENTS---"
	cd subdir && $(MAKE)

clean:
	rm -rf subdir

Arguments to make

make的命令参数可以参考list of options

你可以运行多个target,例如make clean run test,其会先运行目标clean,再运行run,再运行test

Variables Pt. 2

Flavors and modification

变量定义的两种形式

  • recursive(使用=):当命令使用到这个变量时,才会去寻找变量的定义(比如例子中的later_variable)
  • simply expanded(使用:=):变量定义时就直接进行展开(later_variable未被定义,直接输出为空)
1
2
3
4
5
6
7
8
9
10
# Recursive variable. This will print "later" below
one = one ${later_variable}
# Simply expanded variable. This will not print "later" below
two := two ${later_variable}

later_variable = later

all: 
	echo $(one)
	echo $(two)

Simply expanded (using :=)可以避免循环引用的错误

1
2
3
4
5
6
one = hello
# one gets defined as a simply expanded variable (:=) and thus can handle appending
one := ${one} there

all: 
	echo $(one)

?= 只会设置未被定义的变量

1
2
3
4
5
6
7
one = hello
one ?= will not be set
two ?= will be set

all: 
	echo $(one)
	echo $(two)

在末尾的空格不会被忽略,但是在开头的空格会被忽略。如果想要将变量设置为空格,可以使用$(nullstring)

1
2
3
4
5
6
7
8
9
with_spaces = hello   # with_spaces has many spaces after "hello"
after = $(with_spaces)there

nullstring =
space = $(nullstring) # Make a variable with a single space.

all: 
	echo "$(after)"
	echo start"$(space)"end

没有被定义的变量为空

1
2
3
all: 
	# Undefined variables are just empty strings!
	echo $(nowhere)

使用+=可以用来增加变量定义的内容

1
2
3
4
5
foo := start
foo += more

all: 
	echo $(foo)

String Substitution 也是一个很常见的修改变量的方式。可以参考 Text FunctionsFilename Functions

Command line arguments and override

可以通过override将make命令中的变量定义进行覆盖。比如我们在这里运行:make option_one=hi

1
2
3
4
5
6
7
# Overrides command line arguments
override option_one = did_override
# Does not override command line arguments
option_two = not_override
all: 
	echo $(option_one)
	echo $(option_two)

List of commands and define

define/endef可以将多个命令赋值给一个变量,下面这个例子比较了两种定义方式的不同,变量one同样也是被赋值了多个命令,但是变量two的多个命令时运行在不同的shell中的

1
2
3
4
5
6
7
8
9
10
11
12
one = export blah="I was set!"; echo $$blah

define two
export blah="I was set!"
echo $$blah
endef

all: 
	@echo "This prints 'I was set'"
	@$(one)
	@echo "This does not print 'I was set' because each command runs in a separate shell"
	@$(two)

Target-specific variables

变量可以仅仅被设置给某个target,如下面的例子所示

1
2
3
4
5
6
7
all: one = cool

all: 
	echo one is defined: $(one)

other:
	echo one is nothing: $(one)

Pattern-specific variables

类似的,也可以将变量设置给某种target patterns

1
2
3
4
5
6
7
%.c: one = cool

blah.c: 
	echo one is defined: $(one)

other:
	echo one is nothing: $(one)