GNU Make

Table of Contents

1. GNU Make 简介

In software development, Make is a utility that automatically builds executable programs and libraries from source code by reading files called makefiles which specify how to derive the target program.
Make can be used to manage any project where some files must be updated automatically from others whenever the others change.

GNU Make Manual: https://www.gnu.org/software/make/manual/

1.1. What a Rule Looks Like

A simple makefile consists of "rules" with the following shape:

target ... : prerequisites ...
	recipe
	...
	...

Please note: you need to put a TAB character at the beginning of every recipe line!
However, the GNU Make since version 3.82 allows to choose any symbol (one character) as the recipe prefix using the .RECIPEPREFIX special variable.

.RECIPEPREFIX = >
all:
> @echo "recipe prefix symbol is set to '$(.RECIPEPREFIX)'"

make 本身并不关心命令(recipe)是如何工作的,对目标文件的更新需要你在规则描述中提供正确的命令。 make 程序所做的就是当目标需要更新时执行规则所定义的命令。

1.2. How make Processes a Makefile

By default, make starts with the first target (not targets whose names start with '.'). This is called the default goal.
You can override this behavior using the command line or with the .DEFAULT_GOAL special variable.

2. Writing Makefiles

Makefiles contain five kinds of things: explicit rules, implicit rules, variable definitions, directives, and comments (begin with #).

2.1. 指定 Makefile 文件的名字

By default, when make looks for the makefile, it tries the following names, in order: GNUmakefile, makefile and Makefile.
If you want to use a nonstandard name for your makefile, you can specify the makefile name with the -f or --file option.

当 make 找不到任何 Makefile 文件时,根据 make 隐含规则的特性,可以通过命令行指定一个目标,如果当前目录下存在符合此目标的依赖文件,那么命令行指定的目标将会被创建或者更新。
如执行: make foo.o ,当前目录下如果有文件 foo.c,则会执行命令 cc -c -o foo.o ,如果没有 foo.c 则会报错。

2.2. Including Other Makefiles (include)

The include directive tells make to suspend reading the current makefile and read one or more other makefiles before continuing.
The directive is a line in the makefile that looks like this:

include filenames...

filenames can contain shell file name patterns. If the file names contain any variable or function references, they are expanded.

If the specified name does not start with a slash, and the file is not found in the current directory, several other directories are searched. First, any directories you have specified with the -I or --include-dir option are searched. Then the following directories (if they exist) are searched, in this order: prefix/include (normally /usr/local/include 1) /usr/gnu/include, /usr/local/include, /usr/include.

If you want make to simply ignore a makefile which does not exist or cannot be remade, with no error message, use the -include directive instead of include. For compatibility with some other make implementations, sinclude is another name for -include.

2.3. How GNU make Reads a Makefile (two phases)

GNU make does its work in two distinct phases. During the first phase (read-in phase) it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values, implicit and explicit rules, and constructs a dependency graph of all the targets and their prerequisites. During the second phase (target-update phase), make uses these internal structures to determine what targets will need to be rebuilt and to invoke the rules necessary to do so.

We say that expansion is immediate if it happens during the first phase: in this case make will expand any variables or functions in that section of a construct as the makefile is parsed.
We say that expansion is *deferred if expansion is not performed immediately. Expansion of a deferred construct is not performed until either the construct appears later in an immediate context, or until the second phase.

2.3.1. Two Phases and Rule Definition

The target and prerequisite sections are expanded immediately, and the recipe is always deferred.

immediate : immediate ; deferred
	deferred

3. Writing Rules

3.1. Rule Syntax

In general, a rule looks like this:

target ... : prerequisites ...
	recipe
	...

or like this:

target ... : prerequisites ; recipe
	recipe
	...

3.2. Searching Directories (VPATH variable and vpath directive)

If a file that is listed as a target or prerequisite does not exist in the current directory, make searches the directories listed in VPATH variable.
For example, following statement specifies a path containing two directories, src and ../headers, which GNU make searches in that order.

VPATH = src:../headers

Similar to the VPATH variable, but more selective, is the vpath directive, which allows you to specify a search path for a particular class of file names.

Table 1: Three forms of the vpath directive in GNU make
form Description
vpath pattern directories Specify the search path directories for file names that match pattern.
vpath pattern Clear out the search path associated with pattern.
vpath Clear all search paths previously specified with vpath directives.

For example, following statement tells GNU make to look for any prerequisite whose name ends in .h in the directory ../headers if the file is not found in the current directory.

vpath %.h ../headers

3.3. Static Pattern Rules ($(objects): %.o: %.c)

The syntax of a static pattern rule:

target ... : target-pattern: prereq-patterns
	recipe
	...

The target-pattern and prereq-patterns say how to compute the prerequisites of each target. Each target is matched against the target-pattern to extract a part of the target name, called the stem. This stem is substituted into each of the prereq-patterns to make the prerequisite names (one from each prereq-pattern).

Here is an example, which compiles each of foo.o and bar.o from the corresponding .c file:

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

上面的 Static Pattern Rules,相当于:

foo.o: foo.c
        $(CC) -c $(CFLAGS) $< -o $@
bar.o: bar.c
        $(CC) -c $(CFLAGS) $< -o $@

参考:https://www.gnu.org/software/make/manual/make.html#Static-Pattern

3.3.1. Static Pattern Rules versus Implicit Rules

Static Pattern Rules 和隐式规则和重要区别在于:什么时候应用规则。

An implicit rule can apply to any target that matches its pattern, but it does apply only when the target has no recipe otherwise specified, and only when the prerequisites can be found. If more than one implicit rule appears applicable, only one applies; the choice depends on the order of rules.

By contrast, a static pattern rule applies to the precise list of targets that you specify in the rule. It cannot apply to any other target and it invariably does apply to each of the targets specified. If two conflicting rules apply, and both have recipes, that's an error.

总结:Static Pattern Rules 的行为更加明确和可控,推荐使用 Static Pattern Rules

3.4. Double-Colon Rules

Double-colon rules are explicit rules written with '::' instead of ':' after the target names.

They are handled differently from ordinary rules when the same target appears in more than one rule. Pattern rules with double-colons have an entirely different meaning.

When a target appears in multiple rules, all the rules must be the same type: all ordinary, or all double-colon. If they are double-colon, each of them is independent of the others. Each double-colon rule's recipe is executed if the target is older than any prerequisites of that rule. If there are no prerequisites for that rule, its recipe is always executed (even if the target already exists).

Double-colon rules are somewhat obscure and not often very useful.
一般地,不要使用 Double-colon rules.

参考:https://www.gnu.org/software/make/manual/make.html#Double_002dColon

3.5. Generating Prerequisites Automatically (make depend)

In the makefile for a program, many of the rules you need to write often say only that some object file depends on some header file. For example, if main.c uses defs.h via an #include, you would write:

main.o: defs.h

You need this rule so that make knows that it must remake main.o whenever defs.h changes.

当程序有很多时,要把上面的规则写全是非常繁琐的!幸运的是,大多数 C 编译器(用选项 -M )能生成这些规则。
With the GNU C compiler, you may wish to use the '-MM' flag instead of '-M'. This omits prerequisites on system header files.
如:

cc -MM main.c

会产生下面类似输出:

main.o : main.c defs.h

把这些编译器生成的规则放入文件中,可用 make 的 include 指令包含这个文件。( make depend 往往干这个工作!)

参考:https://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites

3.6. Special Built-in Target Names (.PHONY .PRECIOUS etc.)

Table 2: Special Built-in Target Names in GNU make
Built-in Target Description
.PHONY The prerequisites of .PHONY are considered to be phony targets.
.SUFFIXES The prerequisites of .SUFFIXES are the list of suffixes to be used in checking for suffix rules.
.DEFAULT The recipe specified for .DEFAULT is used for any target for which no rules are found (either explicit rules or implicit rules).
.PRECIOUS The targets which .PRECIOUS depends on are given the following special treatment: if make is killed or interrupted during the execution of their recipes, the target is not deleted. Also, if the target is an intermediate file, it will not be deleted after it is no longer needed, as is normally done.
.INTERMEDIATE The targets which .INTERMEDIATE depends on are treated as intermediate files.
.SECONDARY The targets which .SECONDARY depends on are treated as intermediate files, except that they are never automatically deleted.
.SECONDEXPANSION If .SECONDEXPANSION is mentioned as a target anywhere in the makefile, then all prerequisite lists defined after it appears will be expanded a second time after all makefiles have been read in.
.ONESHELL If .ONESHELL is mentioned as a target, then when a target is built all lines of the recipe will be given to a single invocation of the shell rather than each line being invoked separately.

完整列表请参考:https://www.gnu.org/software/make/manual/make.html#Special-Targets

3.6.1. Phony Targets

A phony target is one that is not really the name of a file. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.

Here is an example:

clean:
	rm *.o temp

But, the clean target will not work properly if a file named clean is ever created in this directory. To avoid this problem you can explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY as follows:

.PHONY: clean
clean:
	rm *.o temp

The implicit rule search is skipped for .PHONY targets. This is why declaring a target as .PHONY is good for performance, even if you are not worried about the actual file existing.

参考:https://www.gnu.org/software/make/manual/make.html#Phony-Targets

3.6.2. .PRECIOUS VS .SECONDARY

.PRECIOUS.SECONDARY 都能阻止中间文件被删除。
.PRECIOUS 使用更方便,可以指定一个模式,如 .PRECIOUS: %.o 可以使以.o 结尾的中间文件不被删除。
.SECONDARY 必须指定具体文件,不能使用模式。

4. Writing Recipes in Rules

4.1. Recipe 中使用 shell 变量 (change $ to $$)

$ 在 makefile 中有特殊含义(用来引用 make 变量),如果要在 recipe 保留 $ ,则要使用两个美元符号 $$

For example:

LIST = one two three
all:
        for i in $(LIST); do \
            echo $$i; \
        done

results in the following command being passed to the shell:

for i in one two three; do \
    echo $i; \
done

参考:http://www.gnu.org/software/make/manual/make.html#Variables-in-Recipes

4.2. Recipe Echoing (命令前加'@'禁止回显)

Normally make prints each line of the recipe before it is executed. We call this echoing because it gives the appearance that you are typing the lines yourself.

When a line starts with '@', the echoing of that line is suppressed.

4.3. Errors in Recipes (命令前加'-'忽略错误)

If the shell completed successfully (the exit status is zero), the next line in the recipe is executed in a new shell; after the last line is finished, the rule is finished.
If there is an error (the exit status is nonzero), make gives up on the current rule, and perhaps on all rules.

To ignore errors in a recipe line, write a '-' at the beginning of the line's text (after the initial tab).

4.4. 命令前加'+'的作用

all:
	touch file1

用 make -n 执行上面的 makefile,文件 file1 并不会被创建。

all:
	+touch file1

用 make -n 执行上面的 makefile,文件 file1 会被创建!

总结:在 recipe 最前面加上 '+' 也能使命令在用 make -n 启动的情况下被执行!

4.5. Recursive Use of make (使用 $(MAKE) 而不是 make)

Suppose you have a sub-directory subdir which has its own makefile, and you would like the containing directory's makefile to run make on the sub-directory. You can do it by writing this:

subsystem:
	$(MAKE) -C subdir

注意:应该使用 $(MAKE) ,而不要直接使用 make。
当 recipe 中含有 $(MAKE) 时,最外层用 make -n (--just-print)启动程序时,GNU make 识别这种情况, $(MAKE) 所在的 recipe 会被执行。这符合用户的期望(如果直接在 makefile 中写死 make,则不会执行)。

Using the MAKE variable has the same effect as using a '+' character at the beginning of the recipe line.

参考:https://www.gnu.org/software/make/manual/make.html#Recursion

4.5.1. 使用 export 指令

默认地,只有环境变量和命令行中指定的变量会传递到 sub-make 中。可以用 export 指令显式地把变量传递到 sub-make 中。

参考:https://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion

4.6. Using One Shell (.ONESHELL)

When it is time to execute recipes to update a target, they are executed by invoking a new sub-shell for each line of the recipe, unless the .ONESHELL special target is in effect.

如,下面 makefile 中,ls 命令并不会列出/tmp 中的文件,而是会列出当前目录中的文件。

all:
	cd /tmp ;
	ls

如果指定一个特殊目标.ONESHELL,则后面的命令会在一个 shell 中执行,这样 ls 会列出/tmp 目录中的文件。

.ONESHELL:
all:
	cd /tmp ;
	ls

4.7. Parallel Execution (-j or --jobs)

Normally, make will execute only one recipe at a time, waiting for it to finish before executing the next. However, the -j or --jobs option tells make to execute many recipes simultaneously.

If the -j option is followed by an integer, this is the number of recipes to execute at once; this is called the number of job slots. If there is nothing looking like an integer after the -j option, there is no limit on the number of job slots.

如:并行地(最多 8 个 job)执行目标 all 的 recipe:

make -j 8 all

说明 1:并行执行时,可以输出会交错在一起,可以通过选项 --output-sync 来控制输出的行为,如 --output-sync=line 可以使不同 job 输出的行和行之间不会被打断。
说明 2:选项 --output-sync 是在 GNU Make 4.0 中增加的。参考:GNU Make 4.0 released

参考:
The Pitfalls and Benefits of GNU Make Parallelization: http://www.cmcrossroads.com/article/pitfalls-and-benefits-gnu-make-parallelization
Output During Parallel Execution: https://www.gnu.org/software/make/manual/html_node/Parallel-Output.html

4.8. Using Empty Recipes

It is sometimes useful to define recipes which do nothing. This is done simply by giving a recipe that consists of nothing but whitespace.
For example:

target : ;

defines an empty recipe for target. You could also use a line beginning with a recipe prefix character to define an empty recipe, but this would be confusing because such a line looks empty.

空 recipe 的唯一用处是:防止 make 在执行时试图为重建这个目标去执行隐含规则中的命令。

5. How to Use Variables

5.1. Recursively Expanded Variable (=) and Simply Expanded Variable (:= or ::=)

GNU make 支持两种变量:递归展开变量和直接展开变量。

5.1.1. 递归展开变量 (=)

递归展开变量用 = 定义。

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
	@echo foo is $(foo)

上面的 Makefile 会输出 foo is Huh? 这是因为: $(foo) expands to $(bar) which expands to $(ugh) which finally expands to 'Huh?'.

递归展开变量的好处:前面定义的变量可以引用后面定义的变量。
如:

CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
5.1.1.1. Infinite loop in recursively expanded variable

递归展开变量不能自己引用自己,这会导致死循环!
如下面的定义导致无限,GNU make 能检测这种情况并报告错误。

$ cat makefile
CFLAGS = $(CFLAGS) -O

all:
	@echo "${CFLAGS}"
$ make
makefile:1: *** Recursive variable 'CFLAGS' references itself (eventually).  Stop.

5.1.2. 直接展开变量 (:= or ::=)

Simply expanded variables are defined by lines using := or ::=. Both forms are equivalent in GNU make; however only the '::=' form is described by the POSIX standard (support for '::=' was added to the POSIX standard in 2012, so older versions of make won't accept this form either).
The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined.

直接展开变量可以使一个复杂的 Makefile 具有可预测性,因为它的使用方式和大部分编程语言中变量的使用方式相似。

5.1.3. Conditional variable (?=)

?= is called a conditional variable assignment operator, because it only has an effect if the variable is not yet defined.

This statement:

FOO ?= bar

is exactly equivalent to this:

ifeq ($(origin FOO), undefined)
  FOO = bar
endif

5.2. 如何引用变量

有两种方式引用之前定义的变量,如之前定义了变量 foo,则可以通过 $(foo)${foo} 来引用它。

注 1: 只有当变量名是单个字母时,在引用时才可以省略圆括号或花括号。 如之前定义了变量 x,可以通过 $x 来引用它(当然用 $(x)${x} 也行)。
注 2:和 makefile 中变量不同,shell 中不能用 $(foo) 引用变量,只能使用 ${foo} 或者直接省略花括号来引用变量。

5.3. 变量替换 ($(var:a=b) or ${var:a=b})

A substitution reference substitutes the value of a variable with alterations that you specify. It has the form $(var:a=b) (or ${var:a=b}) and its meaning is to take the value of the variable var, replace every a at the end of a word with b in that value, and substitute the resulting string.

For example:

foo := a.o b.o c.o
bar := $(foo:.o=.c)

sets 'bar' to 'a.c b.c c.c'.

For example:

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

sets 'bar' to 'a.c b.c c.c'.

参考:https://www.gnu.org/software/make/manual/make.html#Substitution-Refs

5.4. Appending More Text to Variables (+=)

Often it is useful to add more text to the value of a variable already defined. You do this with a line containing '+=', like this:

objects += another.o

This takes the value of the variable objects, and adds the text 'another.o' to it.

上例中,如果 objects 之前没有定义过,则相当于定义 objects 为递归展开变量;如果 objects 之前定义过,则 += 不会改变 objects 的变量类型。

5.5. 环境变量,命令行指定的变量和 makefile 中定义的变量

默认地,命令行指定的变量优先级最高,会覆盖同名的环境变量和 makefile 中定义的变量。
默认地,环境变量的优先级最低,命令行指定的变量和 makefile 中定义的变量都会覆盖同名的环境变量。

在命令行指定变量的实例:

make CFLAGS='-g -O'

5.5.1. 优先使用 makefile 中定义的变量 (override Directive)

If a variable has been set with a command argument, then ordinary assignments in the makefile are ignored. If you want to set the variable in the makefile even though it was set with a command argument, you can use an override directive.

需要注意的是:如果变量定义时指定了 override,则后续对变量值进行追加时,也需要指定 override,否则对此变量的追加不会生效。

override variable = value
override variable += more text

参考:https://www.gnu.org/software/make/manual/make.html#Override-Directive

5.5.2. 优先使用环境变量 (-e)

环境变量的优先级最低,命令行指定的变量和 makefile 中定义的变量都会覆盖同名的环境变量。如果启动 make 时指定参数 -e ,则可以优先使用环境变量而不是 makefile 中的变量。

注: -e 不会改变命令行指定的变量的优先级(最高优先级),要改变命令行指定的变量的优先级可以用前面介绍的 override 指令。

测试 GNU make 的 -e 参数:

$ cat makefile
var=value1
all:
	@echo $(var)
$ make
value1
$ var=value2 make                  # 环境变量优先级低,不会被使用。显示makefile中变量的值value1
value1
$ var=value2 make -e               # 指定了-e,会优先使用环境变量。显示环境变量中变量的值value2
value2
$ var=value2 make var=value3       # 有没有-e,都不会影响命令行指定的变量(它有最高优先级)
value3
$ var=value2 make -e var=value3    # 有没有-e,都不会影响命令行指定的变量(它有最高优先级)
value3

5.6. Undefining Variables (undefine or override undefine)

If you want to clear a variable, setting its value to empty is usually sufficient. Expanding such a variable will yield the same result (empty string) regardless of whether it was set or not. However, if you are using the flavor and origin functions, there is a difference between a variable that was never set and a variable with an empty value. In such situations you may want to use the undefine directive to make a variable appear as if it was never set. For example:

foo := foo
bar = bar

undefine foo
undefine bar

$(info $(origin foo))
$(info $(flavor bar))

This example will print "undefined" for both variables.

If you want to undefine a command-line variable definition, you can use the override directive together with undefine, similar to how this is done for variable definitions:

override undefine CFLAGS

5.7. Automatic Variables ($@, $< etc.)

See table 3.

Table 3: Automatic Variables in GNU make
Automatic Variables Description
$@ The file name of the target of the rule. If the target is an archive member, then '$@' is the name of the archive file.
$% The target member name, when the target is an archive member. '$%' is empty when the target is not an archive member.
$< The name of the first prerequisite.
$? The names of all the prerequisites that are newer than the target, with spaces between them.
$^ The names of all the prerequisites, with spaces between them. (无重复)
$+ This is like '$^', but prerequisites listed more than once are duplicated in the order they were listed in the makefile. It's usefull for linker. (可以有重复)
$| The names of all the order-only prerequisites, with spaces between them.
$* The stem with which an implicit rule matches. If the target is 'dir/a.foo.b' and the target pattern is 'a.%.b' then the stem is 'dir/foo'.
$(@D) The directory part of the file name of the target, with the trailing slash removed. If the value of $@ is dir/foo.o then $(@D) is dir.
$(@F) The file-within-directory part of the file name of the target. If the value of $@ is dir/foo.o then $(@F) is foo.o. $(@F) is equivalent to $(notdir $@).
$(*D) The directory part of the stem; dir in this example.
$(*F) The file-within-directory part of the stem; foo in this example.
$(%D) The directory part of the target archive member name.
$(%F) The file-within-directory part of the target archive member name.
$(<D) The directory part of the first prerequisite.
$(<F) The file-within-directory part of the first prerequisite.
$(^D) Lists of the directory parts parts of all prerequisites.
$(^F) Lists of the file-within-directory parts of all prerequisites.
$(+D) Lists of the directory parts of all prerequisites, including multiple instances of duplicated prerequisites.
$(+F) Lists of the file-within-directory parts of all prerequisites, including multiple instances of duplicated prerequisites.
$(?D) Lists of the directory parts of all prerequisites that are newer than the target.
$(?F) Lists of the file-within-directory parts of all prerequisites that are newer than the target.

参考:https://www.gnu.org/software/make/manual/make.html#Automatic-Variables

5.8. Target-specific Variable

Target-specific Variable allows you to define different values for the same variable, based on the target that make is currently building.
Set a target-specific variable value like this:

target : variable-assignment

There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc.
For example, a statement like this:

prog : CFLAGS = -g
prog : prog.o foo.o bar.o

will set CFLAGS to '-g' in the recipe for prog, but it will also set CFLAGS to '-g' in the recipes that create prog.o, foo.o, and bar.o, and any recipes which create their prerequisites.

总结:Target-specific Variable 的作用域不仅仅是生成这个目标,还包括这个目标的依赖和依赖的依赖等等。

参考:https://www.gnu.org/software/make/manual/make.html#Target_002dspecific

5.9. Pattern-specific Variable

In addition to target-specific variable values, GNU make supports pattern-specific variable values. In this form, the variable is defined for any target that matches the pattern specified.

For example:

%.o : CFLAGS = -O

will assign CFLAGS the value of '-O' for all targets matching the pattern %.o.

参考:https://www.gnu.org/software/make/manual/make.html#Pattern_002dspecific

6. 条件控制

The syntax of a simple conditional with no else is as follows:

conditional-directive
text-if-true
endif

The syntax of a complex conditional is as follows:

conditional-directive
text-if-true
else
text-if-false
endif

or:

conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else conditional-directive-three
text-if-three-is-true
else
text-if-one-and-two-are-false
endif

参考:https://www.gnu.org/software/make/manual/make.html#Conditionals

注意:makefile 中并没有 elseif 关键字!

其中,conditional-directive 可以是 ifeq, ifneq, ifdef, ifndef。

6.1. ifeq, ifneq

ifeq, ifneq 可以用来作相等测试。

下面的形式都是合法的:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

6.2. ifdef, ifndef

ifdef, ifndef 可以用来测试变量是否定义。

ifdef variable-name
ifndef variable-name

6.3. 实例

ifeq ($(TARGET_CPU),x86)
  TARGET_CPU_IS_X86 := 1
else ifeq ($(TARGET_CPU),x86_64)
  TARGET_CPU_IS_X86 := 1
else
  TARGET_CPU_IS_X86 := 0
endif

7. Functions

可以用下面方式调用内置函数(自定义的“函数”需要用内置函数 call 来间接调用):

$(function arguments)

or like this:

${function arguments}

函数名和参数用空格隔开,多个参数之间用逗号隔开。

说明:当需要用逗号或空格作为函数参数时,必须先把它们赋值给一个变量,再引用这个变量来实现。

参考:https://www.gnu.org/software/make/manual/make.html#Functions

7.1. 处理字符串的相关函数

Table 4: GNU make 中处理字符串的相关函数
Function Description
$(subst from,to,text) 字符串替换
$(patsubst pattern,replacement,text) 模式替换
$(strip string) 去空格
$(findstring find,in) 查找字符串
$(filter pattern...,text) 过滤
$(filter-out pattern...,text) 反向过滤
$(sort list) 排序
$(word n,text) 取单词,首个单词从 1 开始
$(wordlist start,end,text) 取单词串
$(words text) 返回单词数目
$(firstword names...) 取第一个单词
$(lastword names...) 取最后一个单词

7.2. 文件名相关函数

Table 5: GNU make 中文件名相关函数
Function Description
$(dir names...) 取目录
$(notdir names...) 取文件名
$(suffix names...) 取后缀
$(basename names...) 取前缀。$(basename src/foo.c src-1.0/bar hacks)会得到 'src/foo src-1.0/bar hacks'.
$(addsuffix suffix,names...) 加后缀
$(addprefix prefix,names...) 加前缀
$(join list1,list2) 单词连接。$(join a b,.c .o)会得到 'a.c b.o'.
$(wildcard pattern) The result of wildcard is a space-separated list of the names of existing files that match the pattern.
$(realpath names...) 返回 canonical absolute name
$(abspath names...) 返回 absolute name

7.3. 条件测试相关函数

Table 6: GNU make 中条件测试相关函数
Function Description
$(if condition,then-part[,else-part]) provides support for conditional expansion in a functional context.
$(or condition1[,condition2[,condition3…]]) provides a "short-circuiting" OR operation.
$(and condition1[,condition2[,condition3…]]) provides a "short-circuiting" AND operation.

7.4. The foreach Function

The syntax of the foreach function is:

$(foreach var,list,text)

首先展开变量"var"和"list"的引用,而"text"中的变量引用不展开。
然后把"list"中使用空格分割的单词依次取出赋值给变量"var",再执行"text"表达式。重复直到"list"的最后一个单词。
foreach 的返回值是"text"参数多次展开结果的连结(以空格分开)。

参考:https://www.gnu.org/software/make/manual/make.html#Foreach-Function

7.4.1. foreach 实例

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

上面 foreach 语句和下面例子的作用相同:

files := $(wildcard a/* b/* c/* d/*)

7.5. The call Function

You can write a complex expression as the value of a variable, then use call to expand it with different values.

The syntax of the call function is:

$(call variable,param,param,...)

When make expands this function, it assigns each param to temporary variables $(1), $(2), etc.

参考:https://www.gnu.org/software/make/manual/make.html#Call-Function

7.5.1. call 实例

reverse = $(2) $(1)
foo = $(call reverse,a,b)

Here foo will contain 'b a'.

7.6. The value Function

The value function provides a way for you to use the value of a variable without having it expanded.
The syntax of the value function is:

$(value variable)

Note that variable is the name of a variable, not a reference to that variable.

7.6.1. value 实例

FOO = $PATH

all:
	@echo $(FOO)         # 会输出 ATH,只有单字母变量可以省略{}或(),所以$PATH会认为是$P和ATH的结合
	@echo $(value FOO)   # 会输出系统中PATH变量的内容,如/usr/local/bin:/usr/bin:/bin

7.7. The eval Function

The eval function is very special: it allows you to define new makefile constructs that are not constant; which are the result of evaluating other variables and functions. The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax. The expanded results can define new make variables, targets, implicit or explicit rules, etc.

The result of the eval function is always the empty string; thus, it can be placed virtually anywhere in a makefile without causing syntax errors.

It's important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for "$" characters when using eval.

eval 执行时会对它的参数进行两次展开:第一次展开过程由函数 eval 本身完成,第二次是函数展开后的结果被作为 makefile 内容时由 make 解析时展开的。

参考:https://www.gnu.org/software/make/manual/make.html#Eval-Function

7.7.1. eval 实例

PROGRAMS    = server client

server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol

client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol

# Everything after this is generic

.PHONY: all
all: $(PROGRAMS)

define PROGRAM_template =
 $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
 ALL_OBJS   += $$($(1)_OBJS)
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
# 上面这行会展开为:
# server: $(server_OBJS) $(server_LIBS:%=-l%)
# ALL_OBJS   += $(server_OBJS)
# client: $(client_OBJS) $(client_LIBS:%=-l%)
# ALL_OBJS   += $(client_OBJS)

$(PROGRAMS):
	$(LINK.o) $^ $(LDLIBS) -o $@

clean:
	rm -f $(ALL_OBJS) $(PROGRAMS)

7.8. The origin, flavor Function

Function origin or flavor tells you something about a variable.

7.9. The shell Function

The shell function performs the same function that backquotes (`) perform in most shells: it does command expansion.

说明:把 shell 命令输出保存到变量时,shell 变量的真正执行时机和变量声明方式有关。

FILES := $(shell ls)  # expand now; FILES is now the result of $(shell ls)
FILES = $(shell ls)   # expand later: FILES holds the syntax $(shell ls)

7.10. Functions That Control Make (error, warning, info)

Table 7: Functions That Control Make
Function Description
$(error text...) 展开 text 并输出(包含文件名和行号),make 停止执行
$(warning text...) 展开 text 并输出(包含文件名和行号),make 不会停止执行
$(info text...) 展开 text 并输出(但没有文件名和行号)到 stdout

8. Using Implicit Rules

Implicit rules tell make how to use customary techniques so that you do not have to specify them in detail when you want to use them. For example, C compilation typically takes a .c file and makes a .o file.

You can define your own implicit rules by writing pattern rules.
Suffix rules are a more limited way to define implicit rules. Pattern rules are more general and clearer, but suffix rules are retained for compatibility.

8.1. List of Built-in Rules

Here is a catalogue of predefined implicit rules which are always available unless the makefile explicitly overrides or cancels them.
The '-r' or '--no-builtin-rules' option cancels all predefined rules.

参考:https://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules

8.2. Variables Used by Implicit Rules ($(AS) $(CC) $(CXX) $(CPP) etc.)

The recipes in built-in implicit rules make liberal use of certain predefined variables. You can alter the values of these variables in the makefile, with arguments to make, or in the environment to alter how the implicit rules work without redefining the rules themselves.
You can cancel all variables used by implicit rules with the '-R' or '--no-builtin-variables' option.

Table 8: Variables used as names of programs in built-in rules
Variables Description
AR Archive-maintaining program; default 'ar'.
AS Program for compiling assembly files; default 'as'.
CC Program for compiling C programs; default 'cc'.
CXX Program for compiling C++ programs; default 'g++'.
CPP Program for running the C preprocessor, with results to standard output; default '$(CC) -E'.
FC Program for compiling or preprocessing Fortran and Ratfor programs; default 'f77'.
M2C Program to use to compile Modula-2 source code; default 'm2c'.
PC Program for compiling Pascal programs; default 'pc'.
CO Program for extracting a file from RCS; default 'co'.
GET Program for extracting a file from SCCS; default 'get'.
LEX Program to use to turn Lex grammars into source code; default 'lex'.
YACC Program to use to turn Yacc grammars into source code; default 'yacc'.
LINT Program to use to run lint on source code; default 'lint'.
MAKEINFO Program to convert a Texinfo source file into an Info file; default 'makeinfo'.
TEX Program to make TeX DVI files from TeX source; default 'tex'.
TEXI2DVI Program to make TeX DVI files from Texinfo source; default 'texi2dvi'.
WEAVE Program to translate Web into TeX; default 'weave'.
CWEAVE Program to translate C Web into TeX; default 'cweave'.
TANGLE Program to translate Web into Pascal; default 'tangle'.
CTANGLE Program to translate C Web into C; default 'ctangle'.
RM Command to remove a file; default 'rm -f'.

Here is a table of variables whose values are additional arguments for the programs above. The default values for all of these is the empty string, unless otherwise noted.

Table 9: Additional arguments for the programs above
Variables (argument) Description
ARFLAGS Flags to give the archive-maintaining program; default 'rv'.
ASFLAGS Extra flags to give to the assembler (when explicitly invoked on a '.s' or '.S' file).
CFLAGS Extra flags to give to the C compiler.
CXXFLAGS Extra flags to give to the C++ compiler.
COFLAGS Extra flags to give to the RCS co program.
CPPFLAGS Extra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers).
FFLAGS Extra flags to give to the Fortran compiler.
GFLAGS Extra flags to give to the SCCS get program.
LDFLAGS Extra flags to give to compilers when they are supposed to invoke the linker, 'ld', such as -L. Libraries (-lfoo) should be added to the LDLIBS variable instead.
LDLIBS Library flags or names given to compilers when they are supposed to invoke the linker, 'ld'. LOADLIBES is a deprecated (but still supported) alternative to LDLIBS. Non-library linker flags, such as -L, should go in the LDFLAGS variable.
LFLAGS Extra flags to give to Lex.
YFLAGS Extra flags to give to Yacc.
PFLAGS Extra flags to give to the Pascal compiler.
RFLAGS Extra flags to give to the Fortran compiler for Ratfor programs.
LINTFLAGS Extra flags to give to lint.

8.3. Pattern Rules (%.o : %.c ; recipe...)

A pattern rule contains the character '%' (exactly one of them) in the target; otherwise, it looks exactly like an ordinary rule. The target is a pattern for matching file names; the '%' matches any nonempty substring, while other characters match only themselves.

8.3.1. Pattern Rule 实例

The rule that compiles '.c' files into '.o' files (actually predefined in make):

%.o : %.c
	$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

8.3.2. Canceling Implicit Rules (pattern rule without recipe)

You can override a built-in implicit rule (or one you have defined yourself) by defining a new pattern rule with the same target and prerequisites, but a different recipe. When the new rule is defined, the built-in one is replaced. The new rule's position in the sequence of implicit rules is determined by where you write the new rule.

You can cancel a built-in implicit rule by defining a pattern rule with the same target and prerequisites, but no recipe. For example, the following would cancel the rule that runs the assembler:

%.o : %.s

8.4. Suffix Rules (.c.o)

Suffix rules are the old-fashioned way of defining implicit rules for make. Suffix rules are obsolete because pattern rules are more general and clearer. They are supported in GNU make for compatibility with old makefiles.

Here is the old-fashioned way to define the rule for compiling a C source file:

.c.o:
	$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

上面的后缀规则和下面的 pattern rule 作用相同:

%.o : %.c
	$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

注意:后缀规则不能指定依赖,否则会被当作一般的规则!
Suffix rules cannot have any prerequisites of their own. If they have any, they are treated as normal files with funny names, not as suffix rules. Thus, the rule:

.c.o: foo.h
	$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

tells how to make the file .c.o from the prerequisite file foo.h

9. 命令行参数

完整的命令行参数说明,请参考:https://www.gnu.org/software/make/manual/make.html#Options-Summary

9.1. 命令行选项 -ik 的区别

-i (or --ignore-errors) 和 -k (or --keep-going) 有下面区别:
-i 简单地忽略命令的错误(当作执行成功了一样);
-k 如果某个目标出错,依赖这个目标的规则都不执行,和这个目标无关的规则会执行。

-k 的主要用途:当修改了工程中的多个文件后,重新编译时,指定-k 能一次性找出哪些文件不能被编译。

假设有下面 makefile

t1: t2 t3
	command1

t2:
	command2

t3:
	command3

其中 command2 会执行出错,当指定-i 执行时,command3 和 command1 都仍然会执行;当指定-k 执行时,仅会执行 command3,而 command1 不会执行。

10. Makefile Conventions

10.1. 只保留需要的后缀规则

Different make programs have incompatible suffix lists and implicit rules, and this sometimes creates confusion or misbehavior. So it is a good idea to set the suffix list explicitly using only the suffixes you need in the particular Makefile, like this:

.SUFFIXES:
.SUFFIXES: .c .o

The first line clears out the suffix list, the second introduces all suffixes which may be subject to implicit rules in this Makefile.

10.2. 使用$(CC),而不使用 cc

The Makefile rules for building and installation can also use compilers and related programs, but should do so via make variables so that the user can substitute alternatives. Here are some of the programs we mean:
ar bison cc flex install ld ldconfig lex
make makeinfo ranlib texi2dvi yacc

Use the following make variables to run those programs:
$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)
$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)

11. Tips

11.1. 调试 makefile

有三个对调试 makefile 很有帮助的选项:
--just-print(简写为-n)
--print-data-base(简写为-p)
--debug=a(相当于-d)

还有一个调试技巧:
在命令行中指定 SHELL 为 sh -x,让 subshell 启动调试功能。如:

$ make SHELL="sh -x"

这样,makefile 中$(shell ....)中的命令就会显示出来。

11.2. @, - and + as prefixes of recipe

@, - and + can be used as prefixs of recipe:

  • @ suppresses the normal 'echo' of the command that is executed.
  • - means ignore the exit status of the command that is executed (normally, a non-zero exit status would stop that part of the build).
  • + means 'execute this command even under make -n' (commands are not normally executed when make -n).

参考:http://stackoverflow.com/questions/3477292/what-do-and-do-as-prefixes-to-recipe-lines-in-make

11.3. 特殊字符的 escaping 规则

String(in file name) used by gmake escape by the following rule:

$ replace with $$
# replace with \#

参考:http://www.cmcrossroads.com/article/gnu-make-escaping-walk-wild-side

11.4. 注释导致的变量包含多余空格

假设有下面 makefile,定义了一个变量 dir,并有行尾写有注释:

dir := /foo/bar    # directory to put the frobs in

但实际上,dir 变量的内容为'/foo/bar '(后面有几个空格!这很可能并不是你想要的。)

11.5. 变量中引号

先看个 makefile 中的例子:

$ cat makefile
v1=test
v2="test"
v3='test'
$(warning v1 is $(v1))
$(warning v2 is $(v2))
$(warning v3 is $(v3))

$ make -f makefile
makefile:4: v1 is test
makefile:5: v2 is "test"
makefile:6: v3 is 'test'
make: *** No targets.  Stop.

这和 shell 中(如 bash)很不相同:

$ cat 1.sh
v1=test
v2="test"
v3='test'
echo "v1 is ${v1}"
echo "v2 is ${v2}"
echo "v3 is ${v3}"

$ bash 1.sh
v1 is test
v2 is test
v3 is test

11.6. 显示所有内置变量和隐含规则

在一个没有 makefile 的目录中运行 make -p 可以查看所有内置变量(如 CC,CXX,CPP 等等)和隐含规则。

11.7. 对多个目标使用相同的规则

如何对多个目标使用相同的规则呢?
比较笨的方法是重复相同的规则,如:

foo.o: foo.c
        $(CC) -c $(CFLAGS) $< -o $@
bar.o: bar.c
        $(CC) -c $(CFLAGS) $< -o $@

上面的方法不优雅,当目标比较多时有较多的重复代码。
更好的方法是使用 Static Pattern Rules,如:

objects = foo.o bar.o

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

参考:https://www.gnu.org/software/make/manual/make.html#Static-Pattern

Author: cig01

Created: <2013-03-02 Sat>

Last updated: <2018-01-03 Wed>

Creator: Emacs 27.1 (Org mode 9.4)