[关闭]
@xiaohaizi 2022-10-12T08:19:34.000000Z 字数 16243 阅读 105

Make

make


2. Makefile 简介

2.1 规则(rule)是什么样的

一个简单的Makefile由若干条简单的规则组成,一个规则就像这样:

  1. target : prerequisites
  2. recipe

target:可以是实际文件或者虚假目标

prerequisites:target依赖的文件

recipe:是prerequisites比target新时,执行的命令,通常用于更新target。命令可以放在一行或多行中,多行命令每行以制表符开头。

3. 编写 Makefile

3.1 Makefile 包含什么

3.1.1分割长行

使用反斜杠\来分割长行,反斜杠\和换行符将被替换为空格,周围的空格也会被压缩为一个空格

3.1.2 拆分而不添加空格

  1. var := one$\
  2. word

它相当于

  1. var := one$ word

$后是一个空格,而没有名为空格的变量,所以"$ "会被解释称空字符串,上边的语句其实就相当于:

  1. var := oneword

3.2 Makefile名称

make查找Makefile时,会按照 GNUmakefile, makefile and Makefile的顺序进行查找。

推荐写为Makefile

若使用非标准名称,使用参数 ‘-f name’ or ‘--file=name’

3.3 包含其他 Makefile

include指令用于让make暂停读取当前的makefile,转而去读取一个或多个其他的makefile,读完了其他的再继续读取当前makefile中的剩余内容:

  1. include filenames

可以使用通配符。

比方说当前目录下有a.mk, b.mk, and c.mk三个mk文件,$(bar)被扩展为bish bash,那么:

  1. include foo *.mk $(bar)

就被解释为:

  1. include foo a.mk b.mk c.mk bish bash

使用include指令的情景:

从绝对路径或者当前目录下找include的文件,

或者从‘-I’ or ‘--include-dir’ 中制定的目录中查找,

或者从prefix/include(通常是/usr/local/include) 、 /usr/gnu/include, /usr/local/include, /usr/include中查找。

如果都找不到 ,则生成警告,并且在Makefile中寻找有没有该Makefile对应的target,如果有的话,执行该target的recipe,否则报告fatal error。

若不想要警告,则使用:

  1. -include filenames

3.4 MAKEFILES变量

MAKEFILES环境变量代表一系列的文件,当执行make时,会先读取MAKEFILES代表的一系列文件。

默认目标不会从MAKEFILES代表的文件中选取。

3.5 Makefile 是如何remake的

对于include的文件来说,会:

  1. 先remake一下include的Makefile
  2. 重新执行make,将第1步生成的makefile导入进来
  3. 并且再remake一下include的makefile

有点儿绕

  1. all:a c
  2. echo all MAKE_RESTARTS: $(MAKE_RESTARTS)
  3. a:
  4. touch a
  5. include haha
  6. haha: tmp
  7. # echo all: b > haha
  8. echo haha: MAKE_RESTARTS: $(MAKE_RESTARTS)
  9. include bili
  10. bili: tmp
  11. echo bili: MAKE_RESTARTS: $(MAKE_RESTARTS)
  12. touch bili
  13. b:
  14. echo bbbb
  15. c :
  16. echo cccc
  17. _tmp:
  18. echo tmpppp
  19. .PHONY: a tmp

输出:

  1. Makefile:7: haha: No such file or directory
  2. Makefile:14: bili: No such file or directory
  3. echo bili: MAKE_RESTARTS:
  4. bili: MAKE_RESTARTS:
  5. touch bili
  6. echo haha: MAKE_RESTARTS:
  7. haha: MAKE_RESTARTS:
  8. Makefile:7: haha: No such file or directory
  9. echo bili: MAKE_RESTARTS: 1
  10. bili: MAKE_RESTARTS: 1
  11. touch bili
  12. echo haha: MAKE_RESTARTS: 1
  13. haha: MAKE_RESTARTS: 1
  14. touch a
  15. echo cccc
  16. cccc
  17. echo all MAKE_RESTARTS: 1
  18. all MAKE_RESTARTS: 1

每次重新打开一个makefile,MAKE_RESTART变量就会自增1.

如果我们不想remake自己include的makefile,又想避免make利用隐式规则来remake它,可以给该makefile定义空recipe:

  1. makefile: ;

3.6 覆盖另一个Makefile的部分内容

假设Makefile中有一个foo目标,我们又写了一个GNUMakefile:

  1. foo:
  2. frobnicate > foo
  3. %: force
  4. @$(MAKE) -f Makefile $@
  5. force: ;

执行make foo时,将会先执行GNUMakefile中的foo目标,其他目标再去Makefile中找。

其中,force相当于一个虚假目标,不论make的目标是否存在,都会始终执行%: force目标的规则。

3.7 make如何读取 Makefile

分2个阶段:

在第1阶段扩展的变量称作immediate变量,在第2阶段扩展的称作deferred变量:

  1. immediate = deferred
  2. immediate ?= deferred
  3. immediate := immediate
  4. immediate ::= immediate
  5. immediate += deferred or immediate
  6. immediate != immediate
  7. define immediate
  8. deferred
  9. endef
  10. define immediate =
  11. deferred
  12. endef
  13. define immediate ?=
  14. deferred
  15. endef
  16. define immediate :=
  17. immediate
  18. endef
  19. define immediate ::=
  20. immediate
  21. endef
  22. define immediate +=
  23. deferred or immediate
  24. endef
  25. define immediate !=
  26. immediate
  27. endef

conditional Directives是被immediate解析的。

A rule is always expanded the same way

3.8 Makefile 是如何被解析的

make逐行解析Makefile:

  1. 读入完整的逻辑行,包括反斜杠转义的行
  2. 删除注释
  3. 若改行以制表符开头,并且当前处于规则上下文中,则把该行添加到当前recipe并读取下一行
  4. 进行immediate展开
  5. 分隔符,例如 ':' 或者 '=',以确定该行是宏赋值还是规则
  6. 以确定该行是宏赋值还是规则

4. 规则

规则顺序并不重要,除确认默认目标的情况下。

默认目标是第一条规则中的第一个目标,有两个例外:

通常将默认目标起名为all。

4.1 示例

  1. foo.o : foo.c defs.h # module for twiddling the frobs
  2. cc -c -g foo.c

这条规则说明2件事:
1. 如何判定目标foo.o是否过期:foo.o不存在或者foo.c、defs.h比foo.o更新一点。

  1. 怎么更新目标foo.o:执行recipe语句。

4.2 规则语法

两种方式:

4.3 prerequisites的类型

有2种:

  1. normal prerequisites

    • 规定了调用关系, prerequisites的recipe将在target的recipe运行之前先运行
    • 强加了依赖关系:如果prerequisites比target更新,则认为target是过时的,必须重新构建
  2. order-only prerequisites

    如果prerequisites发生了更新,只要target存在,就不更新target

语法:

  1. targets : normal-prerequisites | order-only-prerequisites

4.4 在文件名中使用通配符

文件名中使用通配符可以代表多个文件

‘*’, ‘?’ and ‘[…]’

~

在target和prerequisites中的通配符由make自身扩展;在recipe中的通配符由shell扩展;在其他场景中,显式使用wildcard函数。

通配符特殊意义可以通过在前边加上反斜杠\关闭。

4.4.1 通配符示例

recipe中的通配符(由shell扩展):

  1. clean:
  2. rm -f *.o

prerequisites中的通配符(由make扩展):

  1. print: *.c
  2. lpr -p $?
  3. touch print

定义变量时不会进行通配符扩展,比方说:

  1. objects = *.o

4.4.2 使用通配符的陷阱

当通配符不匹配任何文件时,它会保持原样,比方说:

  1. foo: *.o
  2. echo @^

若当前目录中不存在以.o为后缀的文件名,则目标foo依赖与*.o这个prerequisites,make时将报错:

  1. make: *** No rule to make target `*.o', needed by `foo'. Stop.

4.4.3 wildcard函数

通配符扩展自动在规则中发生,但是不会在定义变量或函数内部扩展,我们需要显式使用wildcard函数来达到目的:

  1. $(wildcard pattern…)

该函数可以在makefile中任何地方使用,将被替换成匹配模式的文件名列表,各个文件名之间用空格分割开来。若没有匹配的文件,则被替换为空字符串。

4.5 在目录中搜索先决条件

4.5.1 VPATH:所有先决条件的搜索路径

VPATH变量指定make要搜索的路径,路径之间由冒号(:)或者空格分隔,路径顺序就是搜索顺序。在这些路径下搜索prerequisites以及target。

例:

  1. VPATH = src:../headers
  2. foo.o : foo.c

当执行make时,会从当前目录、src/../headers/这几个目录中寻找有没有foo.c文件。

4.5.2 vpath指令

有3种形式:

pattern是一个包含%的字符串(若没有%,则必须和文件名完全匹配)。

例:

  1. vpath %.h ../headers

之后,如果在当前目录下找不到.h的prerequisite,则去../headers目录下找。

如果某个文件名符合多个vpath指令中的pattern,则依次在这些目录中查找,例:

  1. vpath %.c foo
  2. vpath % blish
  3. vpath %.c bar

则除了当前目录外,依次在foo、blish、bar这几个目录下找.c文件。

4.5.3 如何执行目录搜索

  1. 如果在 makefile中指定的路径中不存在目标文件,则执行目录搜索。
  2. 如果目录搜索成功,则保留该路径,并将此文件暂时存储为目标。
  3. 使用相同的方法检查此目标的所有先决条件。
  4. 在处理完先决条件后,目标可能需要也可能不需要重建:

    • 如果不需要重建目标,则在目录搜索期间找到的文件的路径将用于包含此目标的任何先决条件列表。简而言之,如果make不需要重建目标,则使用通过目录搜索找到的路径。

    • 如果确实需要重建目标(已过时),则丢弃在目录搜索期间找到的路径名,并使用 makefile 中指定的文件名重建目标。简而言之,如果make必须重建,那么目标是在本地重建,而不是在通过目录搜索找到的目录中。

4.6 虚假目标

仅代表一个要执行的recipe,并非文件名。

  1. 避免与同名文件发生冲突
  2. 提高性能

虚假目标作为.PHONY目标的prerequisite,就不会被解释为文件。

  1. .PHONY: clean
  2. clean:
  3. rm *.o temp

4.7 没有配方或先决条件的规则

比方说:

  1. clean: FORCE
  2. rm $(objects)
  3. FORCE:

所有依赖于FORCE的目标都会执行recipe,就像是声明了一个虚假目标一样,更建议使用 .PHONY。

4.8 空目标文件以记录事件

4.9 特殊内建目标名称

.PHONY
.SUFFIXES
.DEFAULT
.PRECIOUS
.INTERMEDIATE
.SECONDARY
.SECONDARY
.DELETE_ON_ERROR
.IGNORE
.LOW_RESOLUTION_TIME
.SILENT
.EXPORT_ALL_VARIABLES
.NOTPARALLEL
.ONESHELL
.POSIX

4.10 一个规则中多个目标

4.11 一个目标的多个规则

和所有规则中的prerequisites都合并到一个列表中是等价的。

若多个规则对同一个target都提供了recipe,make选用最后一个规则的recipe并打印一条错误消息。

若目标的显式规则都没有recipe,则寻求隐式规则的recipe。

4.12 静态模式规则

根据目标名称来决定prerequisites

4.12.1 静态模式规则的语法

  1. targets …: target-pattern: prereq-patterns
  2. recipe

例子:

  1. objects = foo.o bar.o
  2. all: $(objects)
  3. $(objects): %.o: %.c
  4. $(CC) -c $(CFLAGS) $< -o $@

target-pattern是%.o,prereq-patterns是%c

等价于:

  1. foo.o:foo.c
  2. xx
  3. bar.o:bar.c
  4. xx

4.13 双冒号规则

当一个目标出现在多个规则中时,所有的规则必须是相同的类型:都是普通的,或者都是双冒号。如果它们是双冒号,则它们中的每一个都是独立于其他的。如果目标早于该规则的任何先决条件,则执行每个双冒号规则的配方。如果该规则没有先决条件,则始终执行其配方(即使目标已经存在)。这可能导致不执行、不执行任何或所有双冒号规则。

4.14 自动生成先决条件

  1. gcc -M foo.c

会自动生成foo.c依赖的头文件。

5. recipe

recipe是shell命令行,默认使用/bin/sh

5.1 recipe语法

以制表符开头的shell语句。第一条recipe也可以写在target/prerequisite行,不过需要用分号隔开。

注意:

5.1.1 拆分recipe行

反斜杠\进行换行,连同反斜杠一起作为一条shell语句交给shell执行。(这一点与变量定义时不同)

5.1.2 在recipe中使用变量

例:

  1. LIST = one two three
  2. all:
  3. for i in $(LIST); do \
  4. echo $$i; \
  5. done

shell接收到的语句是:

  1. for i in one two three; do \
  2. echo $i; \
  3. done

产生的输出是:

  1. one
  2. two
  3. three

5.2 配方echo

make在执行recipe前都会把该recipe打印出来。

当recipe以@开头时,echo会被抑制,@会在将recipe传递到shell前丢弃。

make的参数‘-n’ or ‘--just-print’仅打印recipe,而不执行它们。

make的参数 ‘-s’ or ‘--silent’ 拒绝打印recipe(就像给所有recipe前边都加了一个@)。

5.3 recipe执行

针对recipe中的每一行,都会新生成一个shell去处理。这意味着同一个recipe中不同语句的shell环境是不同的。

如果想让recipe中前边的行影响后边的行,放在一行中就好:

  1. foo : bar/lose
  2. cd $(<D) && gobble $(<F) > ../$@

5.3.1 使用一个shell

将recipe的所有行都放在一个shell运行的好处:
- 避免生成额外进程提升性能
- 可以在recipe中包含换行符

只需要添加特殊的目标.ONESHELL,就像这样:

  1. .ONESHELL:
  2. foo : bar/lose
  3. cd $(@D)
  4. gobble $(@F) > ../$@

5.3.2 选择shell

shell取自环境变量SHELL,如果未设置该环境变量,则默认使用/bin/sh。

5.4 并行执行

一般,make同一时间仅执行一个recipe,但如果添加了‘-j’ or ‘--jobs’选项,则可以让make同时执行多个recipe。

.NOTPARALLEL pseudo-target可以禁止make并行执行多个recipe。

5.4.1 并行执行期间的输出

同时运行多个recipe,每个recipe的输出在生成后立即显示,来自不同recipe的输出可能会交替显示。

‘--output-sync’ (‘-O’) 选项用于避免这种情况,该选项让make保存执行recipe时的输出,在执行完成后再显示到屏幕。

同步输出有4个级别(--output-sync=xxx或者-Oxxx):

5.4.2 并行执行期间的输入

5.5 recipe错误

在每个 shell 调用返回后,make查看它的退出状态。如果 shell 成功完成(退出状态为零),则配方中的下一行将在新的 shell 中执行;最后一行完成后,规则完成。

如果有错误(退出状态为非零),make放弃当前规则,也许放弃所有规则。

若要忽略recipe行的错误,在文本开头写一个--在传入shell之前会被丢弃。例如:

  1. clean:
  2. -rm -f *.o

当使用‘-i’ or ‘--ignore-errors’ 选项时,make会忽略执行recipe时产生的所有错误。

5.6 中断或杀死make

make收到kill信号后,可能会删除recipe应该更新的目标文件,以便于下次make的时候可以重新制作目标文件(已经开始制作目标文件,但没有制作完)。

5.7 递归使用make

递归使用make指的是在Makefile中调用make命令。

  1. subsystem:
  2. cd subdir && $(MAKE)

或者等价于:

  1. subsystem:
  2. $(MAKE) -C subdir

5.7.1MAKE变量如何工作

递归make命令时应始终使用MAKE变量,而不是显式使用命令make。

使用MAKE变量有一个副作用,相当于在recipe前边添加+

5.7.2 将变量传递给子make

顶层make可以通过环境变量或者显式请求将变量传递给sub-make。

export和unexport用于导出或取消导出变量到sub-make。

单独使用export会导出所有变量。

5.7.3 将选项传达给子make

5.7.4 --print-directory选项

‘-w’ or ‘--print-directory’选项可以在sub-make进入时和退出时打印消息,方便查看。

  1. make: Entering directory `/u/gnu/make'.
  2. make: Leaving directory `/u/gnu/make'.

5.8 定义固定 recipes

不同目标可能用到相同的recipe时,可以考虑定义固定序列。固定序列本质上是一个变量,不能与其他变量命名冲突。如:

  1. define run-yacc =
  2. yacc $(firstword $^)
  3. mv y.tab.c $@
  4. endef
  5. foo.c : foo.y
  6. $(run-yacc)

在配方执行中,固定序列的每一行都被视为就像该行在规则中单独出现一样,前面有一个制表符。

5.9 使用空recipe

  1. target: ;

给目标定义empty recipe,可以防止目标寻找隐式规则。

6. 变量

变量是一个名称(name),用于代表一个字符串,这个名称代表的字符串也被称作变量的值

变量的值可以用来代表targets, prerequisites, recipes, 以及Makefile中的其他部分。

当make读取Makefile时,变量和函数都会被展开(除了recipes),

变量名可以是任何字符串,除了‘:’, ‘#’, ‘=’, 以及空格。

变量名区分大小写,传统上使用大写字母。

6.1 变量引用的基础

引用变量方式,$(变量名)或${变量名}

变量引用可以使用在任何上下文中,targets, prerequisites, recipes, most directives, and new variable values.

例子:

  1. objects = program.o foo.o utils.o
  2. program : $(objects)
  3. cc -o program $(objects)
  4. $(objects) : defs.h

字符串是严格展开的,所以对于下边的语句来说:

  1. foo = c
  2. prog.o : prog.$(foo)
  3. $(foo)$(foo) -$(foo) prog.$(foo)

它可以被展开为:

  1. prog.o : prog.c
  2. cc -c prog.c

, {, (时,会将第一个字符看做变量名,比方说$foo,会被当做${f}oo对待。

6.2 变量的两种形式

  1. 递归扩展变量

    使用“=”赋值,或者使用 define伪指令赋值的变量。

    例子:

    1. foo = $(bar)
    2. bar = $(ugh)
    3. ugh = Huh?
    4. all:;echo $(foo)

    最后会输出“Huh?”

    在扩展{bar},然后继续扩展为$(uhg),然后继续扩展为Huh? 这就是递归扩展,知道不再包含变量引用为止。

    递归循环变量有缺点:

    • 展开变量引用时可能遇到循环依赖:

      如果make在展开变量引用时检测到循环依赖,就会报错,比方说下边这句:

      1. CFLAGS = $(CFLAGS) -O
    • 速度慢一些

    • wildcardandshell函数给出不可预测的结果,wildcardandshell函数给出不可预测的结果,无法轻松控制它们何时被调用,甚至调用多少次。

  2. 简单扩展变量。

    使用“:=”或“::=”赋值的变量。

    简单扩展变量,在定义时就已经确定它的值,不会在运行时再次扩展。

    例子:

    1. x := foo
    2. y := $(x) bar
    3. x := later

    相当于

    1. y := foo bar
    2. x := later

    x的值虽然后续改变了,但y的值在定义y时已经确定,不会随着x值的修改而修改了。

    简单扩展变量还可以在为其他变量赋值时引入空格,比方说:

    1. nullstring :=
    2. space := $(nullstring) # end of the line

    space的值其实是一个空格

    ?=在变量尚未定义时,才去定义变量

6.3 引用变量的高级特性

6.3.1 替换引用

$(var:a=b)或者${var:a=b}

对于var中每个单词(以空白隔开的),将单词末尾的a替换为b,例:

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

foo的值是4个单词:a.ob.ol.ac.o,针对每个单词,将末尾的.o替换为.c,那么:
- a.o末尾的.o被替换为.c,就成了a.c
- b.o末尾的.o被替换为.c,就成了b.c
- l.a末尾没有.o,所以不被替换,保持原值l.a
- a.o末尾的.o被替换为.c,就成了c.c

最后的bar值就成了: a.c b.c l.a c.c

6.3.2 计算变量名

  1. x = $(y)
  2. y = z
  3. z = Hello
  4. a := $($(x))

a 值是 Hello

6.4 变量如何取值

6.5 设置变量

变量名后跟随“=” “:=” “::=” ,后边的内容就是变量的值

如:

  1. objects = main.o foo.o bar.o utils.o

变量名周围的空格,以及=符号之后跟随的空格将被忽略。

“!=”(shell 赋值运算符)右侧放置shell表达式,

  1. hash != printf '\043'
  2. file_list != find . -name '*.c'

执行 shell 脚本并将变量设置为其输出

!= 定义的变量是递归展开变量,如果使用简单展开变量,请使用下边写法(shell 函数):

  1. hash := $(shell printf '\043')
  2. var := $(shell find .-name "*.c")

6.6 向变量添加更多文本

‘+=’向变量追加文本

  1. objects += another.o

使用+=赋值的变量,若先前没有定义,则是一个递归扩展变量,否则沿用先前定义的变量类型。

6.7override伪指令

make命令行参数传入的变量优先级更高,此时会忽略Makefile中定义的变量。

  1. override variable = value
  2. override variable := value
  3. override variable += more

6.8 定义多行变量

define指令用于定义多行变量。

define后跟随变量名,之后可以跟随一个可选的赋值运算符(=, :=, +=),之后的行是变量的值,直到遇到endef为止。

紧邻endef之前的换行不算变量值的一部分,如:

  1. define newline
  2. endef

变量newline中实际只包含1个换行。

若省略赋值运算符,默认是“=”。

与override可以配合使用:

  1. override define two-lines =
  2. foo
  3. $(bar)
  4. endef

6.9 取消定义变量

通常将其设置为空字符串即可。

但是在一些flavor/origin函数中,需要使用undefine指令显式取消:

  1. foo := foo
  2. bar = bar
  3. undefine foo
  4. undefine bar
  5. $(info $(origin foo))
  6. $(info $(flavor bar))

这样就会输出“undefined”了。

取消命令行变量时,记得添加override

  1. override undefine CFLAGS

6.10 环境变量

make启动时将看到的环境变量都转为具有相同名称和值的变量,在Makefile中显示分配或命令行传递同名变量会覆盖环境变量的值(-e选项作用相反)。

当make执行recipe是,make中来自环境变量以及命令行的变量会被传递到递归调用,其他变量需使用export语句显式导出。

6.11 目标特定的变量值

此类变量,只在构建特定目标过程中有效。语法为:

  1. target : variable-assignment

特定于目标的变量,对该目标的所有先决条件有效,以及对先决条件的所有先决条件也有效,例:

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

CFLAGS不仅在prog的recipe中有效,也在prog.o foo.o bar.o的recipe中有效

6.12 特定于模式的变量值

此类变量,将对所有匹配制定模式的目标有效。语法为:

  1. pattern : variable-assignment

例:

  1. %.o : CFLAGS = -O

CFLAGS变量将作用于所有以.o结尾的目标。

若一个目标匹配多个模式,多个模式中有同名的特定于模式的变量,则选取名字最长的模式中定义的值,例:

  1. %.o: %.c
  2. $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
  3. lib/%.o: CFLAGS := -fPIC -g
  4. %.o: CFLAGS := -g
  5. all: foo.o lib/bar.o

lib/bar.o既匹配%.o,也匹配lib/%.o,这两个模式中都定义了名为CFLAGS的变量,在构建lib/bar.o时,将选取模式名更长的那个模式中定义的CFLAGS值,也就是-fPIC -g

6.13 抑制继承

若不希望先决条件继承变量,可在变量前加private,例:

  1. EXTRA_CFLAGS =
  2. prog: private EXTRA_CFLAGS = -L/usr/local/lib
  3. prog: a.o b.o

a.o b.o不会从prog中继承EXTRA_CFLAGS变量。

6.14 其他特殊变量

7. 条件控制

7.1 条件示例

  1. libs_for_gcc = -lgnu
  2. normal_libs =
  3. foo: $(objects)
  4. ifeq ($(CC),gcc)
  5. $(CC) -o foo $(objects) $(libs_for_gcc)
  6. else
  7. $(CC) -o foo $(objects) $(normal_libs)
  8. endif

7.2 条件句法

不带else的条件语句:

  1. conditional-directive
  2. text-if-true
  3. endif

带有else的条件语句:

  1. conditional-directive
  2. text-if-true
  3. else
  4. text-if-false
  5. endif

带有else if的条件语句:

  1. conditional-directive-one
  2. text-if-one-is-true
  3. else conditional-directive-two
  4. text-if-two-is-true
  5. else
  6. text-if-one-and-two-are-false
  7. endif

conditional-directive:

  1. ifeq (arg1, arg2)
  2. ifeq 'arg1' 'arg2'
  3. ifeq "arg1" "arg2"
  4. ifeq "arg1" 'arg2'
  5. ifeq 'arg1' "arg2"

如果想测试变量是否是一个非空值,空白字符不会被认为是非空值,除非使用strip函数去除空格,就像这样:

  1. ifeq ($(strip $(foo)),)
  2. text-if-empty
  3. endif
  1. ifneq (arg1, arg2)
  2. ifneq 'arg1' 'arg2'
  3. ifneq "arg1" "arg2"
  4. ifneq "arg1" 'arg2'
  5. ifneq 'arg1' "arg2"
  1. ifdef variable-name

若变量的值是非空值,则为true

  1. ifndef variable-name

7.3 测试标志的条件

使用MAKEFLAGS变量和findstring配合起来

  1. archive.a:
  2. ifneq (,$(findstring t,$(MAKEFLAGS)))
  3. +touch archive.a
  4. +ranlib -t archive.a
  5. else
  6. ranlib archive.a
  7. endif

8. 函数

8.1 函数调用语法

类似变量用法:

  1. $(function arguments)
  2. ${function arguments}

参数与函数名之间用一个或多个空格或制表符分隔,多个参数之间使用逗号分割

8.2 字符串替换和分析函数

8.4 条件函数

8.7 call函数

语法:

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

各个param被赋值给临时变量$(1)$(2)...

没有参数的call函数调用没有意义。

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

foo的值是 b,a。

8.10 origin函数

用于获取变量信息。语法是:

  1. $(origin variable)

函数结果是一个字符串,可能是:

8.13 shell函数

该函数与make之外的世界进行沟通。

执行shell命令,将输出中的换行都替换为空格,若有尾随换行,则将其删除。

  1. contents := $(shell cat foo)

9. 如何运行make

make的退出状态:

9.1 指定 Makefile 的参数

‘-f’ or ‘--file’

默认找GNUmakefile, makefile, and Makefile

9.2 指定目标的参数

默认找第一个

也可以通过命令行参数指定一个或多个目标。

如果可以找到如何制作某个文件的隐式规则,甚至该目标都不用包含在Makefile中。

10. 使用隐式规则

10.1 使用隐式规则

避免自己制定目标的recipe,可以不编写目标的recipe,或者根本就不编写规则,make会挑选隐式规则去构建该目标。比方说:

  1. foo : foo.o bar.o
  2. cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

上例中用到了foo.o,但是没有为此编写规则,make会为其寻找一个隐式规则,不论此时foo.o是否存在。

隐式规则会为foo.o添加若干先决条件以及recipe,如果你想为foo.o目标添加更多的先决条件,比方说添加一些头文件什么的,也可以显式的为foo.o添加一个规则,里边添加上先决条件,但不写recipe。

对于同一个目标,可能有多个隐式规则都符合它。比方说对于.o目标来说,它可能从.c文件编译来,也可能由.p文件编译来,make在处理.o目标时,会先查找当前存在什么类型的源文件,当前存在.c,则使用C编译器,当前存在.p,则使用Pascal编译器。

10.2 内置规则目录

C程序

n.o is made automatically from n.c with a recipe of the form

  1. $(CC) $(CPPFLAGS) $(CFLAGS) -c

.

Linking a single object file
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is

  1. $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)

10.3 隐式规则使用的变量

10.4 隐式规则链

10.5.3 自动变量

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注