Makfile学习0——基础知识
学习Makfile需要掌握的基础知识:
一、基础规则:
目标(target)…:依赖(prerequiries)…
<tab>命令(command) #以TAB开头
make命令的使用:
执行 make 命令时,它会去当前目录下查找名为“Makefile”的文件,并根据它的指示去执行操作,生成第一个目标。
我们可以使用“-f”选项指定文件,不再使用名为“Makefile”的文件,比如:make -f Makefile.build。
我们可以使用“-C”选项指定目录,切换到其他目录里去,比如:make -C a/ -f Makefile.build
我们可以指定目标,不再默认生成第一个目标。make -C a/ -f Makefile.build other_target
二、Makefile 变量
1、定义变量
Makfile的定义变量要用到赋值符,“=”、“:=”、“?=”。还有追加“+=”
make 中对变量的赋值有两种方式:延时变量、立即变量。区别在于它们的定义方式和扩展方式不同,前者在这个变量使用时才扩展开,即当真正使用时这个变量的值才确定;后者在定义时它的值就已经确定了。使用`=’,`?=’定义或使用 define 指令定义的变量是延时变量;使用`:=’定义的变量是立即变量。需要注意的一点是,`?=’仅仅在变量还没有定义的情况下有效,即`?=’被用来定义第一次出现的延时变量。对于附加操作符`+=’,右边变量如果在前面使用(:=)定义为立即变量则它也是立即变量,否则均为延时变量。
A = xxx // 延时变量 变量的真实值取决于它所引用的变量的最后一次有效值。
B ?= xxx // 延时变量,如果这是第一次定义,则赋值成功;如果曾定义过,此赋值无效
C := xxx // 立即变量 变量的真实值取决于:=赋予的值。
D += yyy // 如果 D 在前面是延时变量,那么现在它还是延时变量;如果 D 在前面是立即变量,那么现在它还是立即变量
2、变量的导出(export):
在编译程序时,我们会不断地使用“make -C dir”切换到其他目录,执行其他目录里的 Makefile。如果想让某个变量的值在所有目录中都可见,要把它 export 出来。
比如“CC = $(CROSS_COMPILE)gcc”,这个 CC 变量表示编译器,在整个过程中都是一样的。定义它之后,要使用“export CC”把它导出来。
三、Makfile的模式规则
使用模式规则,至少要包含“%”,否则就是一般规则。目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方法如下
%.o : %.c
命令
四、Makefile的自动化变量
作用:通过一行命令来从不同的依赖文件中生成对应的目标。
自动化变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完。
自动化变量只应该出现在规则的命令中。
自动化变量 | 描述 |
$@ |
规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模 |
$% |
当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件, |
$< |
依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么 |
$? | 所有比目标新的依赖目标集合,以空格分开。 |
$^ |
所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件, |
$+ | 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件。 |
$* |
这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模 |
五、Makfile中使用shell命令:
比如:TOPDIR := $(shell pwd)。这是个立即变量,TOPDIR 等于 shell 命令 pwd 的结果。
六、在 Makefile 中怎么放置第 1 个目标
执行 make 命令时如果不指定目标,那么它默认是去生成第 1 个目标。所以“第 1 个目标”,位置很重要。有时候不太方便把第 1 个目标完整地放在文件前面,这时可以在文件的前面直接放置目标,在后面再完善它的依赖与命令。比如:
1 First_target: // 这句话放在前面 2 .... // 其他代码,比如 include 其他文件得到后面的 xxx 变量 3 First_target : $(xxx) $(yyy) // 在文件的后面再来完善 4 command
七、假想目标(伪目标)
一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
使用伪目标的主要是为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突。
clean: rm -f $(shell find -name "*.o") rm -f $(TARGET)
如果当前目录下恰好有名为“clean”的文件,规则因为没有依赖,所以目标被认为是最新的,那么执行“make clean”时它就不会执行那些删除命令。为了避免这个问题,我们可以将 clean 声明为伪目标,声明方式如下:
.PHONY : clean
八、Makfile的条件判断
两对关键字:ifeq、ifneq、ifdef 和 ifndef
ifeq用法:
ifeq (<参数 1>, <参数 2>) ifeq ‘<参数 1 >’,‘ <参数 2>’ ifeq “<参数 1>”, “<参数 2>” ifeq “<参数 1>”, ‘<参数 2>’ ifeq ‘<参数 1>’, “<参数 2>”
上述用法中都是用来比较“参数 1”和“参数 2”是否相同,相等则为真。ifneq用法类似,不过,不相等为真。
ifdef用法:
ifdef <变量名>
如果“变量名”的值非空,那么表示表达式为真,否则表达式为假。
九、Makefile函数
- 函数调用的格式
$(function arguments)
这里`function’是函数名,`arguments’是该函数的参数。参数和函数名之间是用空格或 Tab 隔开,如果有多个参数,它们之间用逗号隔开。
-
字符串替换和分析函数
-
$(subst from,to,text) 在文本`text’中使用`to’替换每一处`from’。
$(subst ee,EE,feet on the street) #结果为‘fEEt on the strEEt’。
-
$(patsubst pattern,replacement,text) 寻找`text’中符合格式`pattern’的字,用`replacement’替换它们。`pattern’和`replacement’中可以使用通配符。
$(patsubst %.c,%.o,x.c.c bar.c) #结果为:`x.c.o bar.o’。
-
$(strip string) 去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。
$(strip a b c ) #结果为`a b c’。
-
$(findstring find,in) 在字符串`in’中搜寻`find’,如果找到,则返回值是`find’,否则返回值为空。
$(findstring a,a b c) #返回'a'
$(findstring a,b c) #返回'' -
$(filter pattern...,text) 去除’text‘中不符合格式‘pattern...'的字,返回由空格隔开且匹配格`pattern...’的字
$(filter %.c %.s,foo.c bar.c baz.s ugh.h) #返回'foo.c bar.c baz.s'
-
$(filter-out pattern...,text) 去除’text‘中符合格式‘pattern...'的字,返回由空格隔开且不匹配格`pattern...’的字
$(filter %.c %.s,foo.c bar.c baz.s ugh.h) #返回'ugh.h'
-
$(sort list)将‘list’中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开的字的列表。
$(sort foo bar lose) #‘bar foo lose’
-
-
文件名函数
-
$(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 hacks) #src/foo src-1.0/bar hacks’
-
$(addsuffix suffix,names...)参数‘names...’是一系列的文件名,文件名之间用空格隔开;suffix 是一个后缀名。将 suffix(后缀)的值附加在每一个独立文件名的后面,完成后将文件名串联起来,它们之间用单个空格隔开。
$(addsuffix .c,foo bar) #‘foo.c bar.c’
-
$(addprefix prefix,names...)参数‘names’是一系列的文件名,文件名之间用空格隔开;prefix 是一个前缀名。将 preffix(前缀)的值附加在每一个独立文件名的前面,完成后将文件名串联起来,它们之间用单个空格隔开。
$(addprefix src/,foo bar) #src/foo src/bar’
-
$(wildcard pattern)参数‘pattern’是一个文件名格式,包含有通配符(通配符和 shell 中的用法一样)。函数 wildcard 的结果是一列和格式匹配的且真实存在的文件的名称,文件名之间用一个空格隔开。
#若当前目录下有文件 1.c、2.c、1.h、2.h
c_src := $(wildcard *.c) #‘1.c 2.c’
-
-
其他函数
-
$(foreach var,list,text)对 list 中的每一个元素,取出来赋给 var,然后把 var 改为 text 所描述的形式。
objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) # 最终 dep_files := .a.o.d .b.o.d -
$(if condition,then-part[,else-part]) 首先把第一个参数‘condition’的前导空格、结尾空格去掉,然后扩展。如果扩展为非空字符串,则条件‘condition’为‘真’;如果扩展为空字符串,则条件‘condition’为‘假’。 如果条件‘condition’为‘真’,那么计算第二个参数‘then-part’的值,并将该值作为整个函数 if的值。如果条件‘condition’为‘假’,并且第三个参数存在,则计算第三个参数‘else-part’的值,并将该值作为整个函数 if 的值;如果第三个参数不存在,函数 if 将什么也不计算,返回空值。 注意:仅能计算‘then-part’和‘else-part’二者之一,不能同时计算。
-
$(origin variable)变量‘variable’是一个查询变量的名称,不是对该变量的引用。所以,不能采用‘$’和圆括号的格式书写该变量,当然,如果需要使用非常量的文件名,可以在文件名中使用变量引用。 函数 origin 的结果是一个字符串,该字符串变量是这样定义的:
‘undefined' :如果变量‘variable’从没有定义;
‘default' :变量‘variable’是缺省定义;
‘environment' :变量‘variable’作为环境变量定义,选项‘-e’没有打开;
‘environment override' :变量‘variable’作为环境变量定义,选项‘-e’已打开;
‘file' :变量‘variable’在 Makefile 中定义;
‘command line' :变量‘variable’在命令行中定义;
‘override' :变量‘variable’在 Makefile 中用 override 指令定义;
‘automatic' :变量‘variable’是自动变量 -
$(shell command arguments) 函数 shell 是 make 与外部环境的通讯工具。函数 shell 的执行结果和在控制台上执行‘commandarguments’的结果相似。不过如果‘command arguments’的结果含有换行符(和回车符),则在函数 shell的返回结果中将把它们处理为单个空格,若返回结果最后是换行符(和回车符)则被去掉。
#当前目录下有文件 1.c、2.c、1.h、2.h,则:
c_src := $(shell ls *.c) #‘1.c 2.c’
-