Makefile Summary

一个工程在用make来执行时,需要一个命名为Makefile的特殊文件。这个文件告诉make需要作什么,该如何做。其主要用来进行工程编译和程序链接以至于最后的执行。目的是用来实现程序的自动化编译。所以说使用make工具来构建和管理一个属于自己的工程,是一项基本要求。
一个简单的Makefile一般是由“规则”组成。规则描述在什么情况下、如何重建规则的目标文件。以下示例:
TARGET... :PREREQUISITES...
COMMAND
...
...
target:规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需的中间过程文件名。[[BR]] 另外,也可以是一个make执行的动作名称。如伪目标“clean”。
prerequisites:规则的依赖。生成规则目标所需要的文件名列表。[[BR]] command: 规则的命令行。规则所要执行的动作。每个命令行必须以[tab]字符开始。

一、Makefile的规则

1. make如何更新规则

默认情形,make执行的是Makefile的第一个规则,此规则的第一个目标称为“终极目标”。
make更新规则时主要是根据文件的时间戳来执行动作:
(1)目标文件不存在,使用描述规则创建它;
(2)目标文件存在,如果它所依赖的文件任何一个比目标文件更新,根据规则重新编译它;
(3)目标文件存在,且比任何一个依赖文件更新,则什么都不做。

2. makefile的内容

在一个完整的makefile中,包含了5个东西:
(1)显示规则:描述何种情况下如何更新一个或者多个目标文件。需要给出明确的目标文件,依赖文件列表以及更新目标文件所需的命令(有些规则也许只描述文件之间的依赖关系)。
(2)隐含规则:它是make根据一类目标文件(文件后缀)而自动推导出来的规则。
(3)变量定义:使用一个字符或字符串代表一段文本串。
(4)makefile指示符:指明在make程序读取makefile文件过程中所要执行的一个动作。
(5)注释 : #字符后的内容作为注释内容处理,如果跟(\),那么下一行也被作为注释行。
(\#)是对#的转义。

3.makefile文件的命名

默认情况,make会在工作目录下查找文件名顺序为:GNUmakefile、makefile、Makefile.
make -f NAME 或make --file=NAME 可指定文件NAME作为执行make时读取的makefile文件。

4.包含其他makefile文件

关键字“include”指示符告诉make暂停当前的Makefile而去读取“include”指定的一个或者多个文件。完成之后再继续当前Makefile的读取。其书写要占独立一行。但不要以[tab]字符开始。
-include 告诉make,忽略由于包含文件不存在或者无法创建时候的错误提示。
MAKEFILES 变量
如果当前环境定义了一个“MAKEFILES”环境变量,make执行时首先将此变量的值作为需要读入的makefile文件。多个文件用空格分隔。
5.make的执行过程

(1) 依次读取变量“MAKEFILES”定义的 makefile 文件列表
(2) 读取工作目录下的 makefile 文件(根据命名的查找顺序“GNUmakefile、makefile、Makefile“,首次找到哪个就读取哪个。
(3) 依次读取工作目录 makefile 文件中使用指示符“include”包含的文件
(4) 查找重建所有已读取的 makefile 文件的规则
(5) 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支
(6) 根据“终极目标”以及其他目标的依赖关系建立依赖关系链表
(7) 执行除“终极目标”以外的所有的目标的规则(规则中如果依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)
(8) 执行“终极目标”所在的规则。

6.makefile的目录搜寻

在一个较大工程中,一般源代码和二进制文件安排在不同的目录来进行区分管理。这种情况下,我们可以用make提供的目录搜索依赖文件的功能来进行自动化编译和链接。
(1)一般搜索:变量VPATH
通过变量“VPATH”可以指定依赖文件的搜索路径,当规则的依赖文件在当前目录不存在时,make会在此变量指定的目录下去寻找这些依赖文件。
并且,"VPATH"变量所指定的是MAKEFILE中所有文件的搜索路径,包括了规则的依赖文件和目标文件。
定义变量“VPATH”时,使用空格或者冒号(:)将多个需要搜索的目录分开。make搜索目录的顺序是按照变量“VPATH”定义中的目录顺序进行的。
当前目录永远是第一搜索目录.
vpath全小写,是make的一个关键字,它所实现的功能和上一小节提到的“VPATH”变量类似, 但更加灵活。它有三种使用方法:
(2)选择性搜索
1. vpath PATTERN DIRECTORIES
为所有符合模式”PATTERN“的文件指定搜索目录“DIRECTORIES”,多个目录使用空格或者冒号分开。
2.vpath PATTERN
清除之前为符合模式“PATTERN”的文件设置的搜索路径。
3.vpath
清除所有已被设置的文件搜索路径。
PATTERN需要包含模式字符“%”,意思是匹配一个或者多个字符,“%.h”表示所有以“.h”结尾的文件。如果没有%,那就是一个明确的文件名,那就给定了此文件的所在目录。
(3)目录搜索机制
make在解析Makefile文件执行规则时对文件路径保存或废弃根据一定的算法:
1.如果规则的目标文件不存在于在makefile文件所在的目录, 那么就执行目录搜寻;
2.如果目录搜寻成功,且指定的目录下存在此规则的目标(也就是不需要被创建)。那么搜索到的完整路径名被保存。
3.对于规则中的所有依赖文件使用相同的方法处理。
4.依赖文件处理后,make程序就可以决定规则的目标是否需要重建,两种情况分别如下处理:
a)规则的目标不需要重建:通过目录搜索得到的所有完整的依赖文件路径名有效,规则的目标文件的完整路径名同样有效。
b)规则的目标需要重建: 通过目录搜索得到的目标文件的完整路径名无效。规则中的目标文件将会在工作目录下重建,而不是在目录搜寻时得到的目录。
注意:依赖文件的完整路径名是不会失效的。否则无法重建目标。
如果重建目标的时候,不改变目标文件生成的路径,使用GPATH。
(4) 命令行和搜索目录
为了保证依赖文件在其他目录下被发现时规则的命令能够正确执行。
我们采用自动化变量
$^:代表所有通过目录搜索得到的依赖文件的完整路径名(目录 + 一般文件名);
$@:代表规则的目标
$<:代表规则中通过目录搜索得到的依赖文件列表的第一个依赖文件

for example:foo.o : foo.c
cc -c $(CFLAGS) $^ -o $@
(5)库文件和搜索目录
Makefile中程序链接的静态库、共享库同样也可以通过搜索目录得到。
书写规则的依赖时指定一个类似“-INAME”的依赖文件名,INAME --> libNAME.so
搜索库文件的顺序:1.make执行规则时会在当前目录下搜索一个名为“libNAME.so”文件;
2.当前目录下不存在,继续搜索使用"VPATH"or "vpath"指定的搜索目录。
3.还是不存在,将搜索系统库文件存在的默认目录,顺序是"./lib" "/usr/lib"和“/usr/local/lib”
如果libNAME.so动态库通过以上搜索未找到,可以按照搜索顺序查找名为“libNAME.a”的文件。
除此之外,可以通过编译选项来指定搜索静态链接库还是动态链接库。
7. Makefile 伪目标
伪目标是这样的一个目标,它不代表一个真正的文件名,在执行make时可以指定这个目标来执行其所在规则定义的命令。
也可以将之称为一个标签。使用伪目标有两点原因:
(1)避免在Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突。
(2)提高执行make时的效率,特别是对于一些大型的工程来说,编译的效率是个值得关注的问题。
将一个目标声明为伪目标的方法是将它作为特殊伪目标.PHONY的依赖。
!.PHONY:clean
执行时输入make clean,命令会被执行。而且不会去试图查找隐含规则来创建它。保证了make的执行效率。
对多个目录进行make的实现方式可以是:在一个规则的命令行中使用shell循环来完成:
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done
当一个伪目标没有作为任何目标的依赖时,可以通过make的命令行来明确指定它为make的终极目标,来执行它所在规则所定义的命令。
如make clean。
在一个Makefile中,一个伪目标可以有自己的依赖(可以是文件或是伪目标);在一个目录下需要创建多个可执行程序,可以
将所有程序的重建规则在一个Makefile中描述,Makefile的第一个目标是终极目标,约定的做法是使用一个称为all的伪目标作为
终极目标,它的依赖文件是那些需要创建的程序。
另外:RM 是make存在的一个内嵌隐含变量,被定义为:“RM = rm -f",因此可以用$(RM)来代替“rm",避免一些不必要的麻烦。

8.双冒号规则
双冒号规则就是使用“::” 代替普通规则的“:“得到的规则。当同一个文件作为多个规则的目标时,双冒号规则的处理和普通规则的处理过
程完全不同。双冒号规则允许在多个规则中为同一个目标指定不通的重建目标的命令。
在Makefile中,一个目标可以出现在多个规则中。但是这些规则必须是同一类型的规则,要么都是普通规则,要么都是双冒号
规则。而不允许一个目标同时出现在两种不同类型的规则中。他们的不同点在于:
(1)双冒号规则中,当依赖文件比目标文件更新时,规则将会被执行。对于一个没有依赖而只有命令行的双冒号规则,当引用
此目标时,规则的命令将会被无条件执行。而普通规则,当规则的目标文件存在时,此规则的命令永远不会被执行。
(2)当同一个文件作为多个双冒号规则的目标时,这些不同的规则会被独立处理。而不是像普通规则那样合并所有的依赖到一个
目标文件。意味着这些规则的处理像多个不通的普通规则一样。多个双冒号规则中的每一个依赖文件被改变后,make只执行此规则定义的
命令,而其他的以这个文件作为目标的双冒号规则将不会被执行。
9.自动产生依赖
gcc -M 选项 可以自动查找源文件中包含的头文件,并生成文件的依赖关系。
for example: maic.c include defs.h
run command: gcc -M main.c
output : main.o:main.c defs.h
而且也包含对标准库的头文件的依赖关系描述。
当不需要在依赖关系中考虑标准库头文件时,对gcc需要使用“-MM"参数。二、规则的命令
1. 规则的命令
(1).规则命令由一些shell命令行组成,它们被一条一条的执行。除了第一条紧跟在依赖列表之后使用分号隔开的命令以外,其他的每
一行命令行必须以【TAB】字符开始。多个命令行之间可以有空行和注释行。
空行:就是不包含任何字符的一行。
空命令行:以tab键开始,而其后没有命令的行,此行不是空行,是空命令行。
2.命令的回显
如果规则的命令行以字符@开始,make执行这个命令时,就不会回显这个将要被执行的命令。
make -n or "--just-print" 那么make执行时,只显示所要执行的命令,但不会真正的去执行这些命令。
make -s or "--silent" 禁止所有执行命令的显示。好像所有命令均使用@开始一样。
3.命令的执行
当目标需要重建时,规则的命令将会被执行,如果是多行命令,那么每一行命令将在一个独立的子shell进程中被执行。多行命令之间的执行
是相互独立的,相互之间不存在依赖。
在makefile中,书写在同一行的多个命令属于一个完整的shell命令行,书写在独立行的一条命令是一个独立的shell命令行。
如果希望把一个完整的shell命令行书写在多行,需要使用(\)来对于多行的命令进行连接。
make对所有命令的解析使用环境变量 “SHELL“所指定的那个程序,在GNU make中,默认的程序是"/bin/sh".
4.命令的并行执行
make支持同时执行多条命令。make -j or --job选项来告诉make在同一时刻可以允许多条命令同时执行。默认j=1
5.命令执行的错误
规则中的命令运行结束后,make会检测命令执行的返回状态,如果返回成功,启动另外一个子shell来执行下一条命令。规则中所有命令
执行完成之后,这个规则就执行完成了。如果某个命令出现错误返回非0,make就会放弃对当前规则后续命令的执行。
一般情况下,规则中命令的执行失败并不代表规则执行的错误。
在命令之前加一个减号”-“,来告诉make忽略此命令的执行失败。也可在make执行时,使用选项-ior --ignore-errors,make将忽略
所有规则中命令执行的错误。也可以使用-k or --keep-going来通知make,出现错误时,不立即退出。而是继续后续命令的执行。
通常情况下,执行失败的命令一旦改变了所在规则的目标文件,就不是一个被正确重建的文件,推荐做法是make执行失败时,修改错误
之后执行make clean,来明确删除第一次错误重建的所有目标。
6.几个make变量
MAKE,使用make递归调用时,在命令行中应该使用变量MAKE,来代替直接使用make。
两个特殊的变量“SHELL“”MAKEFLAGS“,他们在整个make的执行过程中始终自动的传递给所有的子make,除非使用unexport对他们进行
声明。对其他的变量,需要使用export声明,才会传递给子make。
不带任何参数的export指示符: export 含义是此Makefile中定义的所有变量传递给子make过程。
MAKELEVEL 代表了调用的深度。
有几个特殊的命令行选项不会传递给MAKEFLAGS变量,他们是-C -f -o -W
7.自动化变量
define 可以定义一组命令,同时使用一个变量来代表这一组命令;
变量可以用来代表一个文件名列表,编译选项列表,程序运行选项参数列表等等
变量名大小写敏感的
具有特殊字符的几个变量称他们是 自动化变量。
$@ 表示规则的目标文件名,在多目标模式规则中,它代表的是哪个触发规则被执行的目标文件名
$% 当规则的目标文件是一个静态库文件时,代表静态库的一个成员名,如果目标不是静态库,其值为空。
$< 代表规则的第一个依赖文件名。如果目标文件使用隐含规则来重建,代表由隐含规则加入的第一个依赖文件
$? 所有比目标文件更新的依赖文件列表,空格分割;如果目标是静态库文件名,代表的是库成员
$^ 规则的所有依赖文件列表,使用空格分割。如果是静态库文件,代表的是所有库成员名
$+ 类似$^,但是它保留了依赖文件中重复出现的文件,主要用在程序链接时,库的交叉引用场合
$* 在模式规则和静态模式规则中,代表茎,茎是目标模式中 % 所代表的部分,也包含目录。
for example:文件“dir/a.foo.b",当目标的模式为 “a.%.b"时,$*的值为dir/a.foo
$$ 引用$ 这个符号
8,变量的引用
make的变量(MAKEFILE中定义的,或者是make的环境变量)的引用使用$(VAR),无论VAR是单字符还是多字符变量名
出现在规则中的shell变量,引用使用$tmp格式
出现在命令行中的make变量我们同样使用$(CMDVAR)格式来引用

9.如何定义空格
nullstring :=
space := $(nullstring) # end of the line
变量space表示一个空格
10.定义变量的方式
变量的定义是通过“=”(递归方式)”:=“(静态方式)”+=“(追加方式)来实现的。
:= 定义的变量 为直接展开式变量,直接展开式的变量如果其值中存在其他变量或者函数的引用,在定义时这些引用将会被替换展开。
= 递归展开式变量,此变量的引用,是严格的文本替换过程,如果变量中存在对其他变量的引用,在变量定义时,变量值中的引用不会被
替换展开,而是变量在引用它的地方替换展开的同时,它引用的变量才会被一同替换展开。
+= 追加变量值 在一个变量定义之后的其他一个地方,可以对其值进行追加,这是非常有用的。
?= 条件赋值的赋值操作符 ?=. FOO?=bar 含义是如果之前FOO没有被赋值的情况下,才会对FOO赋值。FOO = bar
11. override 指示符
如果通过命令行定义了一个变量,那么它将替代在Makefile中出现的同名变量定义。如果不希望替换makefile中的变量定义,需要使用
指示符override来对变量进行声明
12 define多行定义
类似于c语言中的define,可以对一个变量进行多行定义。以指示符define开始,endif结束,所要定义的变量名字和指示符define在
同一行,空格分开,指示符所在行的下一行开始一直到endif所在行上一个行直接的若干行,都是变量值。
三、makefile的条件执行1.条件判断基本语法
for example:
ifeq($(CC),gcc)
$(CC) -o foo $() $()
else
$(CC) -o foo $() $()
endif
2.ifeq :判断参数是否相等
ifeq (ARG1, ARG2)'[[BR]] ifeq 'ARG1' 'ARG2''
ifeq "ARG1" "ARG2"'[[BR]] ifeq "ARG1" 'ARG2''
ifeq 'ARG1' "ARG2"'[[BR]]3.ifneq :判断参数是否不相等[[BR]] ifneq (ARG1, ARG2)'
ifneq 'ARG1' 'ARG2!''[[BR]] ifneq "ARG1" "ARG2"'
ifneq "ARG1" 'ARG2!''[[BR]] ifneq 'ARG1' "ARG2"'
4.ifdef :判断一个变量是否已经定义
ifdef VARIABLE-NAME5.ifndef: 判断一个变量是否没有定义,与ifdef实现的功能相反四、makefile的内嵌函数GNU make 的函数提供了处理文件名、变量、文本和命令的方法,使用函数使得Makefile的书写更加灵活健壮。1.函数的调用语法
$开始的表示一个引用,语法格式如下
$(FUNCTION ARGUMENTS)
or ${FUNCTION ARGUMENTS}
FUNCTION 是需要调用的函数名,应该是make的内嵌函数名。用户自己定义的函数需要通过make的call函数来间接调用。
ARGUMENTS 是函数的参数,参数和函数名直接使用若干空格或者tab字符分割,多个参数之间使用逗号分割
$开头,使用成对的圆括号或花括号把函数名和参数括起来,推荐变量引用和函数引用使用统一的圆括号
2.字符串替换函数—$(subst FROM,TO,TEXT)
$(subst ee,EE,feet on the street)结果为字符串“fEEt on the strEEt"
3.模式替换函数-- $(patsubst PATTERN,REPLACEMENT,TEXT)
$(patsubst %.c,%.o,x.c.c bar.c) 结果为 x.c.o bar.o
4.去空格函数 $(strip STRINT)
STR =a b c
LOSTR = $(strip $(STR))
结果为 a b c5.查找字符串函数 $(findstring FIND,IN)
$(findstring a,a b c)
$(findstring a,b c)
第一个函数结果是字“a”;第二个值为空字符。

6.过滤函数 $(filter PATTERN...,TEXT)
sources := foo.c bar.c baz.s ugh.h
$(filter %.c %.s,$(sources))
函数返回值为 foo.c bar.c baz.s
7.反过滤函数 $(filter-out PATTERN...,TEXT)
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects))
函数返回值为 foo.o bar.o
8.排序函数 $(sort LIST)
$(sort foo bar lose foo)
返回值 bar foo lose
9.取单词函数 $(word N,TEXT)
$(word 2, foo bar baz)
返回值为 bar
10.取字串函数 $(wordlist S,E,TEXT)
$(wordlist 2, 3, foo bar baz)
返回值是: bar baz
11.统计单词数目函数 $(words TEXT)
$(words, foo bar)
返回值是2
12.取首单词函数 $(firstword NAMES...)
$(firstword foo bar yes)
返回值是foo
13.取目录函数 $(dir NAMES...)
$(dir src/foo.c hacks)
返回值为 src/ ./
14.取文件名函数 $(notdir NAMES...)
$(notdir src/foo.c hacks)
返回值为 foo.c hacks15.取后缀函数 $(suffix NAMES...)
$(suffix src/foo.c src-1.0/bar.c hacks)
返回值为“.c .c”
16.取前缀函数 $(basename NAMES...)
$(basename src/foo.c src-1.0/bar.c /home/jack/.font.cache-1 hacks)
返回值为:“src/foo src-1.0/bar /home/jack/.font hacks”
17.加后缀函数 $(addsuffix SUFFIX,NAMES...)
$(addsuffix .c,foo bar)
返回值为“foo.c bar.c”
18.加前缀函数 $(addprefix PREFIX,NAMES...)
$(addprefix src/,foo bar)
返回值为“src/foo src/bar”
19.单词连接函数 $(join LIST1,LIST2)
$(join a b , .c .o)
返回值为:“a.c b.o”
$(join a b c , .c .o)
返回值为:“a.c b.o c”
20.获取匹配模式文件名函数 $(wildcard PATTERN)
$(wildcard *.c)
返回值为当前目录下所有.c 源文件列表。
21.foreach 函数 是一个循环函数,类似于shell中的for语句
$(foreach VAR,LIST,TEXT)
函数功能:首先展开变量VAR和LIST的引用,执行时把LIST中使用空格分割的单词依次取出赋值给变量VAR,然后执行TEXT表达式。
重复直到LIST的最后一个单词
dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
TEXT 表达式为“$(wildcard $(dir)/*)”,第一次执行时将展开为“$(wildcard a/*)”;第二次将展开为“$(wildcard b/*)”
依次类推。
22.if函数
提供了一个在函数上下文中实现条件判断的功能
$(if CONDITION,THEN-PART[,ELSE-PART])
$(if $(SRC_DIR) $(SRC_DIR),/home/src)
函数结果是:如果SRC_DIR变量值不为空,则将变量SRC_DIR指定的目录作为一个子目录;否则将目录/home/src作为一个子目录
23.call函数
call函数是唯一一个可以创建定制化参数函数的引用函数。使用这个函数可以实现对用户自己定义函数的引用。
我们可以将一个变量定义为一个复杂的表达式,用call函数根据不同的参数对它进行展开来获得不同的结果。
$(call VARIABLE,PARAM,PARAM,...)
函数功能:执行时,将它的参数“PARAM“依次赋值给临时变量$(1) $(2)...这些临时变量定义在VARIABLE的值中,call函数对参数
的数目没有限制,也可以没有参数,没有参数值的call没有任何实际存在的意义。执行时变量VARIABLE被展开为在函数上下文有效的临时
变量,变量定义中的$(1)作为第一个参数,并将参数中的第一个参数赋值给它;变量中的$(2)一样被赋值为第二个参数值。
$(0)代表变量VARIABLE本身。之后对变量VARIABLE表达式计算值。
reverse = $(2) $(1)
foo = $(call reverse,a,b)
变量foo的值为 ba,这里变量reverse中的参数定义顺序可以根据需要来调整,并不是需要按照 $(1) $(2) $(3)这样的顺序来定义。
24.value函数
$(value VARIABLE)
函数功能:不对变量VARIABLE进行任何展开操作,直接返回变量VARIABLE的值,这里VARIABLE是一个变量名,一般不包含$,除非计算的
变量名。
返回值:变量VARIABLE所定义的文本值,如果变量定义为递归展开式,其中包含对其他变量或者函数的引用,那么函数不对这些引用进行
展开。函数的返回值是包含有引用值。
# sample Makefile
FOO = $PATH
all:
@echo $(FOO)
@echo $(value FOO)
执行make,结果为:第一行为ATH,因为变量FOO定义为$PATH,所以展开为ATH $P为空。
第二行才是我们需要显示的环境变量PATH 的值。value函数得到变量FOO的值$PATH25.eval函数
函数功能:函数“eval”是一个比较特殊的函数。使用它可以在Makefile中构造一个可变的规则结构关系(依赖关系链),其中可以
使用其它变量和函数。函数“eval”对它的参数进行展开,展开的结果作为Makefile的一部分,make可以对展开内容进行语法解析。
展开的结果可以包含一个新变量、目标、隐含规则或者是明确规则等。也就是说此函数的功能主要是:根据其参数的关系、结构,对
它们进行替换展开。
返回值:函数“eval”的返回值时空,也可以说没有返回值。
函数说明:“eval”函数执行时会对它的参数进行两次展开。第一次展开过程发是由函数本身完成的,第二次是函数展开后的结果被
作为Makefile内容时由make解析时展开的。明确这一过程对于使用“eval”函数非常重要。理解了函数“eval”二次展开的过程后。
实际使用时,如果在函数的展开结果中存在引用(格式为:$(x)),那么在函数的参数中应该使用“$$”来代替“$”。因为这一点,
所以通常它的参数中会使用函数“value”来取一个变量的文本值。
26.origin函数
函数origin和其他函数不同,函数origin的动作不是操作变量,它只是获取此变量相关的信息,告诉我们这个变量的出处。
函数语法:$(origin VARIABLE)
函数功能:函数origin查询参数 VARIABLE(一个变量名)的出处。
函数说明:VARIABLE是一个变量名而不是一个变量的引用。因此通常它不包含$(计算的变量名例外)
返回值: 返回VARIABLE 的定义方式。用字符串表示
函数的返回情况有以下几种:

1. undefined 变量“VARIABLE”没有被定义。

2. default 变量“VARIABLE”是一个默认定义(内嵌变量)。如“CC”、“MAKE”、“RM”等变量。
如果在Makefile中重新定义这些变量,函数返回值将相应发生变化。

3. environment 变量“VARIABLE”是一个系统环境变量,并且make没有使用命令行选项“-e”
(Makefile中不存在同名的变量定义,此变量没有被替代)。
4. environment override 变量“VARIABLE”是一个系统环境变量,并且make使用了命令行选项“-e”。
Makefile中存在一个同名的变量定义,使用“make -e”时环境变量值替代了文件中的变量定义。
5. file 变量“VARIABLE”在某一个makefile文件中定义。
6. command line 变量“VARIABLE”在命令行中定义。
7. override 变量“VARIABLE”在makefile文件中定义并使用“override”指示符声明。
8. automatic 变量“VARIABLE”是自动化变量。27.shell 函数
shell函数不同于除“wildcard”函数之外的其它函数。make可以使用它来和外部通信
函数功能:函数“shell”所实现的功能和shell中的引用(``)相同。实现对命令的扩展。这就意味着需要一个shell 命令作为此
函数的参数,函数的返回结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数返回结果中的所有
换行符(“\n”)或者一对“\n\r”替换为单空格;并去掉末尾的回车符号(“\n”)或者“\n\r”。进行函数展开式时,它所调用的
命令(它的参数)得到执行。除对它的引用出现在规则的命令行和递归变量的定义中以外,其它决大多数情况下,make是在读取
解析Makefile时完成对函数shell的展开。
返回值:函数“shell”的参数(一个shell命令)在shell环境中的执行结果。
函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由make进行的。当对函数的引用出现在规则
的命令行中,命令行在执行时函数才被展开。展开时函数参数(shell命令)的执行是在另外一个shell进程中完成的,因此需要对
出现在规则命令行的多级“shell”函数引用需要谨慎处理,否则会影响效率(每一级的“shell”函数的参数都会有各自的shell进程)。

contents := $(shell cat foo)
将变量“contents”赋值为文件“foo”的内容,文件中的换行符在变量中使用空格代替。
28,make的控制函数
$(error TEXT...)
函数功能:产生致命错误,并提示“TEXT…”信息给用户,并退出make的执行。需要说明的是:“error”函数是在函数展开式
(函数被调用时)才提示信息并结束make进程。因此如果函数出现在命令中或者一个递归的变量定义中时,在读取Makefile时
不会出现错误。而只有包含“error”函数引用的命令被执行,或者定义中引用此函数的递归变量被展开时,才会提示致命信息“TEXT…”
同时退出make。
返回值:空
函数说明:“error”函数一般不出现在直接展开式的变量定义中,否则在make读取Makefile时将会提示致命错误。
29. $(warning TEXT…)
功能:函数“warning”类似于函数“error”,区别在于它不会导致致命错误(make不退出),而只是提示“TEXT…”,make的
执行过程继续。
返回值:空
函数说明:用法和“error”类似,展开过程相同。
五、make的执行
1.目标就是make最终所要重建的Makefile某个规则的目标,它的重建,会触发它的依赖或者依赖的依赖文件被重建的过程。可以使用make TARGET_NAME 如make clean,
可以把这个目标指定为终极目标。使用这种方式,我们也可以同时指定多个终极目标
2.指定编译或者创建那些正常编译过程不能生成的文件,这些文件在Makefile中存在重建规则,但是他们没有出现在默认终极目标的目标依赖中。
3.在Makefile中,使用变量CFLAGS来指定编译参数,例如:CFLAGS=-g cc-c $(CFLAGS) foo.c
4.指定执行一个由伪目标定义的若干条命令或者一个空目标文件。部分标准的伪目标和空目标命名
all:作为Makefile的顶层目标,一般此目标作为默认的终极目标
clean:这个伪目标定义了一组命令,这些命令的功能是删除所有由make创建的文件。
print:打印楚所有被更改的源文件列表
dist:为源文件创建发布的压缩包,可以使各种压缩方式的发布包
check test:对Makefile最后生成的文件进行检查
以上这些并不是GNU make规定的,可以在Makefile中定义任何命名的伪目标,但以上这些作为一个约定。绝大多数程序员遵循这种约定。
5.make 的命令行选项make所支持的命令行选项如下:
-b
-m
忽略提供其他版本make的兼容性
-B
--always-make
强制重建所有规则的目标,不根据规则的依赖描述决定是否重建目标文件。
-C DIR
--directory=DIR
在读取Makefile之前,进入目录DIR,就是切换工作目录到DIR之后进行make。
存在多个-C选项时,make的最终工作目录是第一个目录的相对路径 make -C / -C etc等价于 make -C /etc
-d
make在执行过程中打印出所有的调试信息。包括make认为那些文件需要重建,那些文件需要比较它们最后修改时间,比较结果等等
--dubug【=OPTIONS】
make执行时输出调试信息。可以使用OPTIONS 控制调试信息级别。默认是OPTIONS=b,OPTIONS可能值为以下这些,首字母有效
a(all) 输出所有类型的调试信息,等效于-d选项
b(basic)输出基本调试信息。包含那些目标过期是否重建成功过期目标文件
v(verbose) basic级别以上的输出信息。包含解析的makefile文件名,不需要重建文件等。
i(implicit)输出所有使用到的隐含规则描述。此选项目默认打开basic级别的调试信息
j(jobs) 输出所有执行命令的子进程,包含命令执行的PID等
m(makefile) 输出make读取makefile,更新makefile,执行makefile的信息
-e(--environment-overrides)使用系统环境变量的定义覆盖Makefile中的同名变量定义
-f=FILE
--file=FILE 指定FILE为make执行的makefile 文件。
-h --help 打印帮助信息
-i --ignore-errors 执行过程中忽略规则命令执行的错误
-I DIR
--include-dir=DIR 指定被包含makefile文件的搜索目录。
-j --jobs 指定可同时执行的命令数目
-K --keeping 执行命令错误时不终止make的执行,make尽最大可能的执行所有的命令,直到出现致命错误才终止。
除此之外,还有很多选项帮助我们得到所需要的信息,我们可以通过man手册查看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章