目录
基于陈皓大佬的教程整理的思维框架:跟我一起写 Makefile(一)_《跟我一起写makefile》_haoel的博客-CSDN博客
目录说明
Makefile_1: 编写基础代码
Makefile_2: 声明一个变量(objects)包含所有目标文件
Makefile_3: 由于make可以自动推导,所以脚本可以再次简化 #伪目标文件
Makefile_4: 优化clean,清空目标文件的规则
Makefile_5: 显式规则、隐晦规则、变量定义、文件指示和注释
Makefile_6: make的工作步骤
Makefile_7: makefile规则
Makefile_8: makefile命令
Makefile_9: 变量的使用
Makefile_10: 函数的使用 字符串函数 文件名操作函数 foreach循环函数 if函数
Makefile_11: make的运行
Makefile_12: 隐含规则
Makefile_13: 库函数的使用
Makefile_1
#编写基础代码
edit : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean:
rm edit main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
Makefile_2
#声明一个变量 objects 包含所有目标文件
objects : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean:
rm edit $(objects)
Makefile_3
#由于 make 可以自动推导,所以脚本可以再次简化
objects : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
#伪目标文件
.PHONY : clean
clean:
rm edit $(objects)
Makefile_4
#优化 clean ,清空目标文件的规则
objects : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
#伪目标文件
.PHONY : clean
clean:
-rm edit $(objects) #小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事
Makefile_5
# 显式规则、隐晦规则、变量定义、文件指示和注释。
objects : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
#伪目标文件
.PHONY : clean
clean:
-rm edit $(objects) # 小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事
Makefile_6
# make的工作步骤
1、读入所有的Makefile;
2、读入被include的其它Makefile;
3、初始化文件中的变量;
4、推导隐晦规则,并分析所有规则;
5、为所有的目标文件创建依赖关系链;
6、根据依赖关系,决定哪些目标要重新生成;
7、执行生成命令。
1-5步为第一个阶段,6-7为第二个阶段。
第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
Makefile_7
一、Makefile 规则
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
Makefile表达式中,规则的顺序是非常重要的,一般第一个目标会成为最终目标!
二、Makefile 语法
targets : prerequisites
command
...
或是这样:
targets : prerequisites ; command
command
...
targets :是文件名,以空格分开,可以使用通配符。
prerequisites :是目标所依赖的文件(或依赖目标)。
command :是命令行,如果其不与 “target:prerequisites” 在一行,那么,必须以[ Tab键 ]开头,
如果和 prerequisites 在一行,那么可以用分号做为分隔。(见上)
如果命令太长,你可以使用反斜框(‘/’)作为换行符。
三、在规则中使用通配符 wildcard
* //通配符
? //通配符
[] //通配符
/ //转义符 将通配符转化为其原本表示的真实字符
objects = *.o //表示"*.o"
objects := $(wildcard *.o) //表示所有.o文件的合集
四、路径--文件搜索 VPATH 和 vpath
VPATH = src:../headers //定义了两个目录 src 和 ../headers ,多个目录之间用“:”分开
vpath有三种使用方法:
1、vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
2、vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
3、vpath
清除所有已被设置好了的文件搜索目录。
vapth 使用方法中的 <pattern> 需要包含“%”字符。“%”的意思是匹配零或若干字符,例如 “%.h”表示所有以“.h”结尾的文件。
vpath %.h ../headers //该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
五、伪目标 .PHONY
伪目标的重要特性:总是被执行!!!
伪目标不是目标文件,只是一个标签,调用 make “name” 执行name伪目标的依赖命令
为了避免伪目标与目标文件重名,可以用 “.PHONY” 来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。
六、多目标 自动化变量 $@
$@ :这个变量表示着目前规则中所有的目标的集合
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
上述规则等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
其中,-$(subst output,,$@) 中的 “$” 表示执行一个 Makefile 的函数,函数名为 subst ,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@” 表示目标的集合,就像一个数组,“$@” 依次取出目标,并执于命令。
七、静态模式
目标:目标模式:目标依赖
命令
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
targets 定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern 是指明了 targets 的模式,也就是的目标集模式。
prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。
案例一:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
案例二:
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
八、自动生成依赖性 -M
如果我们执行下面的命令:
cc -M main.c
其输出是:
main.o : main.c defs.h
比较复杂,后面再详细解释。
Makefile_8
# makefile 会一按顺序一条一条的执行命令,每条命令的开头必须以 [Tab] 键开头,
# 除非,命令是紧跟在依赖规则后面的分号 “;” 后的。
一、显示命令
用“@”字符在命令行前,那么,这个命令将不被 make 显示出来。
二、执行命令
如果要让上一条命令的结果应用在下一条命令时,需要使用分号 “;” 分隔这两条命令。
首先,make 会在 SHELL 所指定的路径中找寻命令解释器;
如果找不到,其会在当前盘符中的当前目录中寻找;
如果再找不到,其会在PATH环境变量中所定义的所有路径中寻找。
三、命令出错
当命令运行完后,make 会检测每个命令的返回码,
如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。
如果一个规则中的某个命令出错了(命令退出码非零),那么 make 就会终止执行当前规则,这将有可能终止所有规则的执行。
为了做到忽略命令的出错,我们可以在 Makefile 的命令行前加一个减号 “-” (在 Tab键 之后),标记为不管命令出不出错都认为是成功的。如:
clean:
-rm -f *.o
“-i”或是“--ignore-errors”:参数,Makefile 中所有命令都会忽略错误。
而如果一个规则是以 “.IGNORE” 作为目标的,那么这个规则中的所有命令将会忽略错误。
“-k”或是“--keep-going”:如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。
四、嵌套执行make
先进入 “subdir” 目录,然后执行make命令
subsystem:
cd subdir && $(MAKE) # subdir--子目录名称
如果你要传递变量到下级 Makefile 中,那么你可以使用这样的声明:
export <variable ...>
如果你不想让某些变量传递到下级 Makefile 中,那么你可以这样声明:
unexport <variable ...>
有两个变量,一个是 SHELL ,一个是 MAKEFLAGS ,这两个变量不管你是否 export ,其总是要传递到下层 Makefile 中,特别是 MAKEFILES 变量,其中包含了 make 的参数信息,如果我们执行 “总控Makefile” 时有 make 参数或是在上层 Makefile 中定义了这个变量,那么 MAKEFILES 变量将会是这些参数,并会传递到下层 Makefile 中,这是一个系统级的环境变量。
五、定义命令包
define 开始, endef 结束
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。
foo.c : foo.y
$(run-yacc)
“$^” 就是 “foo.y” , “$@” 就是 “foo.c”
Makefile_9
# 变量的使用
变量大小写敏感!
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 “:” “#” “=” 或是 空格、回车。
一、变量的基础
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 “$” 符号,但最好用小括号 “()” 或是大括号 “{}” 把变量给包括起来。如果你要使用真实的 “$” 字符,那么你需要用 “$$” 来表示。
二、变量中的变量 条件表达式
:= //前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
?= //FOO ?= bar 如果FOO没有被定义过,FOO = “bar”,如果FOO先前被定义过,那么什么也不做
三、变量的高级用法
1、变量值的替换
foo := a.o b.o c.o
bar := $(foo:.o=.c)
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
2、把变量的值当成变量
x = y
y = z
z = u
a := $($($(x)))
$(a) 的值是 “u”
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))
$(a) 的值是 “Hello”
first_second = Hello
a = first
b = second
all = $($a_$b)
$(all) = Hello
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c)
//如果 al= a 则 $(sources)= a.o b.o c.o
//如果 al= 1 则 $(sources)= 1.o 2.o 3.o
ifdef do_sort
func := sort
else
func := strip
endif
bar := a d b g q c
foo := $($(func) $(bar))
//如果定义 do_sort 则 foo := $(sort a d b g q c)
//如果没定义 do_sort 则 foo := $(strip a d b g q c)
dir = foo
$(dir)_sources := $(wildcard $(dir)/*.c)
define $(dir)_print
lpr $($(dir)_sources)
endef
四、追加变量值 +=
variable := value
variable += more
等价于:
variable := value
variable := $(variable) more
五、override 指示符
override <variable> = <value>
override <variable> := <value>
追加
override <variable> += <more text>
override define foo
bar
endef
六、多行变量
define //关键字 设置变量值
echo foo //
echo $(bar) //
endif //结束关键字
七、环境变量
CFLAGS //全局环境变量
-e //覆盖makefile中的系统环境变量
exprot //传递变量到下级
八、目标变量
局部变量语法
<target ...> : <variable-assignment>
<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”
<target ...> : overide <variable-assignment>
针对于make命令行带入的变量,或是系统环境变量。
九、模式变量 Pattern-specific Variable
make的“模式”一般是至少含有一个“%”的
%.o : CFLAGS = -O
同样,模式变量的语法和“目标变量”一样:
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>
override同样是针对于系统环境传入的变量,或是make命令行指定的变量。
十、条件判断
是否被定义
ifdef * //判断 * 是否被定义
...
else
...
endif
是否不被定义
ifndef * //判断 * 是否不被定义
...
else
...
endif
是否等于
ifeq (a,b) //判断 a 是否等于 b
...
else
...
endif
是否不等于
ifneq (a,b) //判断 a 是否不等于 b
...
else
...
endif
Makefile_10
# 函数
一、函数的调用语法
$(<function> <arguments>)
或是
${<function> <arguments>}
<function> 就是函数名,make 支持的函数不多。
<arguments> 是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。
函数的调用和变量的调用一样,函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。
二、字符串处理函数
$(subst <from>,<to>,<text>) //字符串替换函数
功能:
把字串<text>中的<from>字符串替换成<to>。
示例:
$(subst ee,EE,feet on the street)
把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”
$(patsubst <pattern>,<replacement>,<text>) //模式字符串替换函数
功能:
查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“/”来转义,以“/%”来表示真实含义的“%”字符)。
示例:
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
$(strip <string>) //去空格函数
功能:
去掉<string>字串中开头和结尾的空字符。
示例:
$(strip a b c )
把字串“a b c ”去到开头和结尾的空格,结果是“a b c”
$(findstring <find>,<in>) //查找字符串函数
功能:
在字串<in>中查找<find>字串。
示例:
$(findstring a,a b c)
$(findstring a,b c)
第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
$(filter <pattern...>,<text>) //过滤函数
功能:
以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter %.c %.s,$(sources))
返回值:“foo.c bar.c baz.s”
$(filter-out <pattern...>,<text>) //反过滤函数
功能:
以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。
示例:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
$(filter-out $(mains),$(objects))
返回值:“foo.o bar.o”
$(sort <list>) //排序函数
功能:
给字符串<list>中的单词排序(升序)。
示例:
$(sort foo bar lose)
返回值:“bar foo lose”
$(word <n>,<text>) //取单词函数
功能:
取字符串<text>中第<n>个单词。(从一开始)
示例:
$(word 2, foo bar baz)
返回值是:“bar”
$(wordlist <s>,<e>,<text>) //取单词串函数
功能:
字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
示例:
$(wordlist 2, 3, foo bar baz)
返回值:“bar baz”
$(words <text>) //单词个数统计函数
功能:
统计<text>中字符串中的单词个数。
示例:
$(words, foo bar baz)
返回值:“3”
备注:
如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
$(firstword <text>) //获取首单词函数
功能:
取字符串<text>中的第一个单词。
示例:
$(firstword foo bar)
返回值:“foo”
备注:
这个函数可以用word函数来实现:$(word 1,<text>)。
三、文件名操作函数
$(dir <names...>) //取目录函数
功能:
从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
示例:
$(dir src/foo.c hacks)
返回值:“src/ ./”
$(notdir <names...>) //取文件函数
功能:
从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
示例:
$(notdir src/foo.c hacks)
返回值:“foo.c hacks”
$(suffix <names...>) //取后缀函数
功能:
从文件名序列<names>中取出各个文件名的后缀。
示例:
$(suffix src/foo.c src-1.0/bar.c hacks)
返回值:“.c .c”
$(basename <names...>) //取前缀函数
功能:
从文件名序列<names>中取出各个文件名的前缀部分。
示例:
$(basename src/foo.c src-1.0/bar.c hacks)
返回值:“src/foo src-1.0/bar hacks”
$(addsuffix <suffix>,<names...>) //加后缀函数
功能:
把后缀<suffix>加到<names>中的每个单词后面。
示例:
$(addsuffix .c,foo bar)
返回值:“foo.c bar.c”
$(addprefix <prefix>,<names...>) //加前缀函数
功能:
把前缀<prefix>加到<names>中的每个单词后面。
示例:
$(addprefix src/,foo bar)
返回值:“src/foo src/bar”
$(join <list1>,<list2>) //连接函数
功能:
把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
示例:
$(join aaa bbb , 111 222 333)
返回值:“aaa111 bbb222 333”
四、foreach 循环函数
$(foreach <var>,<list>,<text>)
参数:
<var> 最好是一个变量名
<list> 可以是一个表达式
<text> 中一般会使用<var>这个参数来依次枚举<list>中的单词
示例:
names := a b c d
files := $(foreach n,$(names),$(n).o)
$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
备注:
foreach中的<var>参数是一个临时的局部变量,foreach函数执行完后,参数<var>的变量将不在作用,其作用域只在foreach函数当中。
五、if 函数
$(if <condition>,<then-part>)
或是
$(if <condition>,<then-part>,<else-part>)
参数:
<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,
表达式为真:<then-part>会被计算,
表达式为假:<else-part>会被计算。
六、call 函数
all函数是唯一一个可以用来创建新的参数化的函数。
$(call <expression>,<parm1>,<parm2>,<parm3>...)
参数:
当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call函数的返回值。
示例:
reverse = $(1) $(2)
foo = $(call reverse,a,b)
此时,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
此时,foo的值就是“b a”。
七、origin 函数
origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是:
$(origin <variable>)
参数:
<variable> 是变量的名字,不应该是引用,所以不要在<variable>中使用“$”字符。
返回值:
“undefined”
如果 <variable> 从来没有定义过,origin函数返回这个值“undefined”。
“default”
如果 <variable> 是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。
“environment”
如果 <variable> 是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
“file”
如果 <variable> 这个变量被定义在Makefile中。
“command line”
如果 <variable> 这个变量是被命令行定义的。
“override”
如果 <variable> 是被override指示符重新定义的。
“automatic”
如果 <variable> 是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。
八、shell 函数
shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。
shell函数把执行操作系统命令后的输出作为函数返回。
示例:
contents := $(shell cat foo)
files := $(shell echo *.c)
这个函数会新生成一个Shell程序来执行命令,所以要注意其运行性能,如果Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于系统性能是有害的。特别是Makefile的隐晦的规则可能会让shell函数执行的次数比想像的多得多。
九、控制 make 的函数
make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,让make继续执行,还是停止。
$(error <text ...>)
产生一个致命的错误,<text ...>是错误信息。
示例一:
ifdef ERROR_001
$(error error is $(ERROR_001))
endif
示例二:
ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)
示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。
$(warning <text ...>)
这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。
Makefile_11
# make 的运行
一、make 的退出码
make命令执行后有三个退出码:
0 —— 表示成功执行。
1 —— 如果make运行时出现任何错误,其返回1。
2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。
二、指定 Makefile
-f / --file //给make命令指定一个特殊名字的Makefile / make –f hchen.mk "hchen.mk"是新makefile的名字
三、指定目标
“all”
这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
“clean”
这个伪目标功能是删除所有被make创建的文件。
“install”
这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
“print”
这个伪目标的功能是例出改变过的源文件。
“tar”
这个伪目标功能是把源程序打包备份。也就是一个tar文件。
“dist”
这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
“TAGS”
这个伪目标功能是更新所有的目标,以备完整地重编译使用。
“check”和“test”
这两个伪目标一般用来测试makefile的流程。
四、检查规则
“-n”
“--just-print”
“--dry-run”
“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。
“-t”
“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
“-q”
“--question”
这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
“-W <file>”
“--what-if=<file>”
“--assume-new=<file>”
“--new-file=<file>”
这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。
另外一个很有意思的用法是结合“-p”和“-v”来输出makefile被执行时的信息(这个将在后面讲述)。
五、make 参数
“-b”
“-m”
这两个参数的作用是忽略和其它版本make的兼容性。
“-B”
“--always-make”
认为所有的目标都需要更新(重编译)。
“-C <dir>”
“--directory=<dir>”
指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。
“—debug[=<options>]”
输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是<options>的取值:
a —— 也就是all,输出所有的调试信息。(会非常的多)
b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
i —— 也就是implicit,输出所以的隐含规则。
j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。
“-d”
相当于“--debug=a”。
“-e”
“--environment-overrides”
指明环境变量的值覆盖makefile中定义的变量的值。
“-f=<file>”
“--file=<file>”
“--makefile=<file>”
指定需要执行的makefile。
“-h”
“--help”
显示帮助信息。
“-i”
“--ignore-errors”
在执行时忽略所有的错误。
“-I <dir>”
“--include-dir=<dir>”
指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。
“-j [<jobsnum>]”
“--jobs[=<jobsnum>]”
指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的)
“-k”
“--keep-going”
出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]”
指定make运行命令的负载。
“-n”
“--just-print”
“--dry-run”
“--recon”
仅输出执行过程中的命令序列,但并不执行。
“-o <file>”
“--old-file=<file>”
“--assume-old=<file>”
不重新生成的指定的<file>,即使这个目标的依赖文件新于它。
“-p”
“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。
“-q”
“--question”
不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。
“-r”
“--no-builtin-rules”
禁止make使用任何隐含规则。
“-R”
“--no-builtin-variabes”
禁止make使用任何作用于变量上的隐含规则。
“-s”
“--silent”
“--quiet”
在命令运行时不输出命令的输出。
“-S”
“--no-keep-going”
“--stop”
取消“-k”选项的作用。因为有些时候,make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。
“-t”
“--touch”
相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行。
“-v”
“--version”
输出make程序的版本、版权等关于make的信息。
“-w”
“--print-directory”
输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。
“--no-print-directory”
禁止“-w”选项。
“-W <file>”
“--what-if=<file>”
“--new-file=<file>”
“--assume-file=<file>”
假定目标<file>需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样,使得<file>的修改时间为当前时间。
“--warn-undefined-variables”
只要make发现有未定义的变量,那么就输出警告信息。
Makefile_12
# 隐含规则
一、使用隐含规则
1、编译C程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”
2、编译C++程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)
3、编译Pascal程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.p”,并且其生成命令是“$(PC) –c $(PFLAGS)”。
4、编译Fortran/Ratfor程序的隐含规则。
“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
5、预处理Fortran/Ratfor程序的隐含规则。
“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”
6、编译Modula-2程序的隐含规则。
“<n>.sym”的目标的依赖目标会自动推导为“<n>.def”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>” 的目标的依赖目标会自动推导为“<n>.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。
7、汇编和汇编预处理的隐含规则。
“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用C预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。
8、链接Object文件的隐含规则。
“<n>”目标依赖于“<n>.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:
x : y.o z.o
并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。
9、Yacc C程序时的隐含规则。
“<n>.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)
10、Lex C程序时的隐含规则。
“<n>.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)
11、Lex Ratfor程序时的隐含规则。
“<n>.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。
12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。
“<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>.y”和“<n>.l”也是同样的规则。
二、命令的变量
AR
函数库打包程序。默认命令是“ar”。
AS
汇编语言编译程序。默认命令是“as”。
CC
C语言编译程序。默认命令是“cc”。
CXX
C++语言编译程序。默认命令是“g++”。
CO
从 RCS文件中扩展文件程序。默认命令是“co”。
CPP
C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
FC
Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
GET
从SCCS文件中扩展文件的程序。默认命令是“get”。
LEX
Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
PC
Pascal语言编译程序。默认命令是“pc”。
YACC
Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
YACCR
Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
MAKEINFO
转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
TEX
从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
TEXI2DVI
从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
WEAVE
转换Web到TeX的程序。默认命令是“weave”。
CWEAVE
转换C Web 到 TeX的程序。默认命令是“cweave”。
TANGLE
转换Web到Pascal语言的程序。默认命令是“tangle”。
CTANGLE
转换C Web 到 C。默认命令是“ctangle”。
RM
删除文件命令。默认命令是“rm –f”。
三、关于命令参数的变量
ARFLAGS
函数库打包程序AR命令的参数。默认值是“rv”。
ASFLAGS
汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
CFLAGS
C语言编译器参数。
CXXFLAGS
C++语言编译器参数。
COFLAGS
RCS命令参数。
CPPFLAGS
C预处理器参数。( C 和 Fortran 编译器也会用到)。
FFLAGS
Fortran语言编译器参数。
GFLAGS
SCCS “get”程序参数。
LDFLAGS
链接器参数。(如:“ld”)
LFLAGS
Lex文法分析器参数。
PFLAGS
Pascal语言编译器参数。
RFLAGS
Ratfor 程序的Fortran 编译器参数。
YFLAGS
Yacc文法分析器参数。
四、隐含规则链
五、定义模式规则
1、模式规则介绍
2、模式规则示例
3、自动化变量
4、模式的匹配
5、重载内建隐含规则
六、老式风格的“后缀规则”
七、隐含规则搜索算法
Makefile_13
# 库函数的使用
一、函数库文件的成员
二、函数库成员的隐含规则
当make搜索一个目标的隐含规则时,一个特殊的特性是,如果这个目标是"a(m)"形式的,其会把目标变成"(m)"。
如果我们的成员是"%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"的形式调用Makefile时,隐含规则会去找"bar.o"的规则,如果没有定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o,如果找得到的话,make执行的命令大致如下:
cc -c bar.c -o bar.o
ar r foo.a bar.o
rm -f bar.o
还有一个变量要注意的是"$%",这是专属函数库文件的自动化变量。
三、函数库文件的后缀规则
你可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如:
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
其等效于:
(%.o) : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
四、注意事项
其他
命令说明
#make -f 文件名 //指定编译文件
MAKEFILES //环境变量
- //编译过程中,无论加了-的语句出现什么错误,都不要报错,继续执行
* //系统通配符 不知道目标的名字,系统该目录下中所有后缀为.c的文件都是
% //系统通配符 应用在当前的Makefile文件中
? //系统通配符
[] //系统通配符
/ //转义符 将通配符转化为其原本表示的真实字符
@ //用在命令行前,这个命令将不被make显示出来
自动化变量说明
$@ //目标的名字
$^ //构造所需文件列表所有所有文件的名字
$< //构造所需文件列表的第一个文件的名字
$? //构造所需文件列表中更新过的文件
test1.o:test1.c
gcc -o $@ $<
test1.o:test1.c head.c
gcc -o $@ $^
操作符
:= //前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
?= //FOO ?= bar 如果FOO没有被定义过,FOO = “bar”,如果FOO先前被定义过,那么什么也不做
参数说明
-i / --ignore-errors //忽略错误
-k / --keep-going //命令出错时,终止该规则的执行,但继续执行其它规则。
-e //覆盖makefile中的系统环境变量
export <variable> //传递变量到下级
-w / --print-directory //在make的过程中输出一些信息,显示目前的工作目录
-C //指定 make 下层 makefile , -w自动生效
-S / --slient //-w自动失效
-f / --file //给make命令指定一个特殊名字的Makefile / make –f hchen.mk "hchen.mk"是新makefile的名字
系统变量
MAKELEVEL //记录当前Makefile的调用层数。
CFLAGS //可以控制编译时的编译器参数
条件判断
是否被定义
ifdef * //判断 * 是否被定义
...
else
...
endif
是否不被定义
ifndef * //判断 * 是否不被定义
...
else
...
endif
是否等于
ifeq (a,b) //判断 a 是否等于 b
...
else
...
endif
是否不等于
ifneq (a,b) //判断 a 是否不等于 b
...
else
...
endif
函数
$(subst 要被替换的字符串,用来替换的字符串,被处理的字符串):
用“用来替换的字符串”替换“被处理的字符串”中的“要被替换的字符串”
$(subst .c,.o,test1.c test2.c) //把 test1.c test2.c 中的 .c 替换为 .o
$(wildcard 寻找的文件)
在系统中寻找文件
$(wildcard *.c)
就等于找到系统中所有后缀为.c的文件,返回成以空格隔开的一整行字符
环境变量
MAKECMDGOALS //这个变量中会存放你所指定的终极目标的列表