Makefile简介及常用的规则函数
一、Makefile?
Windows中编译生成可执行文件有现成的集成开发环境(IDE),这个IDE是由半导体厂商或者软件厂商开发好的,一般软件开发者只需要将自己的代码编写出来,便可以通过相关的IDE完成代码的编译链接。
在LInux和Unix系统中,编译代码是没有专门的IDE环境,因此需要软件人员专门写一个文件用于指定整个工程编译规则的文件,这个文件便称为“Makefile”,也可以写作“makefile”“MAKEFILE”。然后通过命令行输入make便可以直接编译整个工程。
Makefile的大致模型如下:
target :source
conmand #(此命令以TAB开始,不能使用空格)
clean:
rm *.o
rm target
其中:“:”代表前方的文件依赖于后方的文件,target是我们需要的最终目标文件,source为生成target这个目标文件所依赖的相关文件,command则为由source生成target时所执行的命令(规则)。clean为一个目标,用来清除编译生成的文件。“*.o”*是指当前目录下所有以.0结束的文件,rm即删除的作用。
Makefile是一个不断寻找依赖文件的过程。make后不管命令的工作原理,会一直比对目标文件和他的依赖文件的修改日期,若依赖文件比目标文件的日期要新,则会执行后续命令。若目标文件也不存在,也会执行命令。
如:(以linux下gcc编译为例)
main : main.o input.o output.o #main为目标文件 input.o output.o为依赖文件
gcc -o main main.o input.o output.o #生成main所需的命令规则,以TAB键开始
main.o : main.c #main.o为目标文件 main.c为依赖文件
gcc -c main.c #生成main.o所需的命令规则,以TAB键开始
input.o : input.c input.h
gcc -c input.c
output.c : output.c output.h
gcc -c output.c
clean:
rm *.o #清除所有的.o文件
rm main #清楚main文件
上述的代码的执行过程为:
第一条规则执行的目标一般为默认的最终目标,即main
判断main目标是否存在。若不存在,则执行后面的命令(规则);若存在,则判断依赖文件是否比目标文件要新(新即代表我们修改过),如果新,则需要重新执行规则命令。
后续的input.o output.o类似于main,区别则是对应命令不同。
最后一个目标为clean,他没有依赖文件,作用为完成工程的清理。在终端中输入 make clean或make -f clean 目标clean则会被执行。
二、 Makefile 包含内容?
Makefile主要由显示规则、隐晦规则、变量定义、文档注释和文件指示构成。
显示规则:是指我们在指定目标和依赖文件的同时,还指明了该文件的执行命令和规则。如下:
input.o : input.c input.h
gcc -c input.c
指明了目标文件input.o和其所依赖的文件input.c input.h ,以及它所执行的规则:gcc -c input.c
隐晦规则:Makefile具有自动推导的能力。有些依赖文件我们不需要特别的写明,make会自动识别,并推导出命令。如下:
input.o : input.h
并未指明目标文件input.o的所有依赖文件以及执行命令。但make的过程中会自动找到input.o的隐晦依赖文件input.c,并且命令 gcc -c input.c也会被推导出来执行。
注:此种方法编写简单,内容简洁,但不利于后期的维护开发,不建议使用。
文件注释:正如C/C++中采用 // 或/* */对文档说明或代码进行注释,Matlab采用%进行注释一样,Makefile也有其特别的注释方式"#"。
main : main.o input.o output.o #main为目标文件 input.o output.o为依赖文件
变量定义和引用:变量在定义时需要赋初值,在使用时需要“$”符号(中文符号¥对应的英文输入法)进行使用。该变量在出现处使用变量值进行替换即可,类似于宏定义。如下:
object = main.o input.o output.o
main : $(object) #main为目标文件 input.o output.o为依赖文件
gcc -o main $(object) #生成main所需的命令规则,以TAB键开始
等价于
main : main.o input.o output.o #main为目标文件 input.o output.o为依赖文件
gcc -o main main.o input.o output.o #生成main所需的命令规则,以TAB键开始
这样的作法优点是:在工程复杂巨大的过程中便于修改。如u-boot和linux 内核中的顶层Makefile。
文件指示:是指主Makefile中调用子Makefile的过程。
三、Makefile符号 及 模式规则
变量复制符号:
①“=”:对变量进行赋值,但该变量输出可以受到后面变量的改动,可以理解为值为最新的。如下:
name = one
curname = $(name)
name = oner
print:
@echo curname : $(curname)
在终端中使用make print即可得到变量值为oner,而不是one,因为oner是变量的最新值。
附:在分析u-boot或Linux kernel的顶层Makefile时,会有很多的变量值,分析起来比较复杂且意义不大,因此我们可以通过在Makefile该变量后加入如下语句直接将变量值打印出来。如下:
myprint:
@echo curname :$(curname)
#形如 @echo 变量名 :($变量名)
然后在终端中输入make myprint即可得出变量值。
②“:=”对变量赋初值。该变量赋值后,值不会受到后续改变的影响,可以理解为采用变量的最出值。如下:
name = one
curname := $(name)
name = oner
print:
@echo curname : $(curname)
在终端中使用make print即可得到变量值为one,而不是oner,因为one是变量的最初值。
③“?=”对变量进行赋值。如果变量在之前未赋初值,那么此变量可以被赋新值。若之前定义过,则执行完后仍为之前定义的值。
curname =
curname ?= one
print:
@echo curname : $(curname)
curname = oner
curname ?= one
print:
@echo curname : $(curname)
则上述两个代码段的输出分别为one和oner
④“+=”给变量追加值。如下:
object = main.o input.o output.o
obiect += test.o
执行完后等价于:
object = main.o input.o output.o test.o
模式规则通配符:
①“%”代码任意的所有的非空字符串。如:
%.o表示所有以.o结尾的文件;
a.%.c表示所有以a.开头和以.c结束的所有文件。
②:“
@
”
代
表
生
成
目
标
的
集
合
,
如
果
有
多
个
生
成
目
标
则
可
以
使
用
自
动
化
变
量
“
@”代表生成目标的集合,如果有多个生成目标则可以使用自动化变量“
@”代表生成目标的集合,如果有多个生成目标则可以使用自动化变量“@”
③:“$<”代表依赖的文件集合。如多个.o文件依赖于多个.c文件时,可以写为:
%.o : %.c
gcc -c $<
④:“”也代表任意长的字符串。.c即代表所有以.c结尾的文件。
四、 Makefile 规则?
Makefile规则可以理解为Makefile的实现模型,一般有两种模式。
①
target : source
command
上述规则中依赖文件和命令行不在同一行,但是命令行的开始必须以TAB键开始。
②
target : source ; commad
上述规则中依赖文件和命令行在同一行,依赖文件和命令行之间用“;”作为间隔。如果命令行或依赖文件过长,可以使用换行符“\”。
- Makefile 命令?
①“@”字符:在命令行前,这个命令不会被显示出来;不在,则会将命令也显示出来。如下:
@echo 正在编译XXXX模块
echo 正在编译XXXX模块
输出结果分别为:
正在编译XXXX模块
echo 正在编译XXXX模块
正在编译XXXX模块
②变量导出
若顶层Makefile中的变量在其他子Makefile中也要使用,则可以使用变量的导出。
variable = one
export variable
若不想导出,则使用
unexport variable
五、 Makefile 函数
函数的调用和变量的调用都一样,采用“$”;函数名和参数之间用空格间隔,多个参数参数和参数之间用逗号间隔。
$(函数名 参数1,参数2,参数3,····)
1、subst函数
$(subst from,to,text)
功能:将参数3中全部的参数1替换为参数2,即将text中全部from字符串替换为to字符串。如:
$(sudst mm , MM,enmmmm)
输出为:
enMMMM
2、patsubst
$(patsubst pattern,replace,text)
功能:将参数3中符合参数1的模式全部替换为参数2,即将text中的全部pattern替换为replace。
pattern可以为空格、回车、%.c、%.h、%.o、a.%.c等形式。
如:
$(patsubst %.c,%.o,main.c input.c output.c)
输出为:
main.o input.o output.o
3、strip
$(strip string)
功能:去除参数1中的空格,即去除字符串string中的空格。
如:
$(strip en mmmm aaaa)
输出为:
enmmmmaaaa
5、findstring
$(findstring goal ,string)
功能:寻找参数2中符合参数1的字符串,即在string中寻找符合goal的字符串或字符。
$(findstring one, oner two)
输出为:
one
$(findstring three, oner two)
输出为:
" "
5、filter
$(filter pattern , text)
功能:过滤出参数2中不符合参数1的单词,即过滤出text中不符合pattern的单词,然后保留全部符合pattern的单词。
$(filter %.c %s,main.c main.o input.c input.h output.c output.h start.s)
输出为:
main.c input.c output.c start.s
与filter对应的函数有filter-out
$(filter-out %.c %s,main.c main.o input.c input.h output.c output.h start.s)
输出为:
main.o input.h output.h
6、firstword
$(firstword text)
功能:取参数1中的第一个单词,即取text字符串中的第一个单词。如:
$(firstword I am Tiger)
输出为:
I
7、sort
$(sort string)
功能:将参数1字符串中的单词按升序排序,即将string中的单词按照(a-z)的顺序排序,注意是单词。如下:
variable = I am Tiger
$(sort $(variable))
输出为:
am I Tiger
8、word
$(word n,string)
功能:取参数2字符串中的第几个单词,即取string中的第n个单词。如下:
$(word 3,I am Tiger)
输出为:
Tiger
9、foreach
$(foreach to,from,text)
功能:将参数2中的单词全部存储到参数1中,并将参数1执行参数3的表达式,即将from中的单词存储到to中,然后再执行text。如下:
varible = I am Tiger
$(foreach vari,$(varible),if.$(vari))
输出为:
if.I if.am if.Tiger
10、if
$(if condition,then-part,else-part)
功能:如果参数1为真(不为空),则执行参数2,否则执行参数3,即若condition为真,则执行then-part,否则执行else-part。
11、origin
(
o
r
i
g
i
n
v
a
r
i
a
b
l
e
)
功
能
:
查
找
参
数
1
的
来
源
,
即
查
找
v
a
r
i
b
l
e
的
来
源
。
v
a
r
i
b
l
e
为
变
量
名
,
做
好
不
要
使
用
"
(origin variable) 功能:查找参数1的来源,即查找varible的来源。varible为变量名,做好不要使用"
(originvariable)功能:查找参数1的来源,即查找varible的来源。varible为变量名,做好不要使用""。其返回值可能为undifined、command-line、file、automatic,分别代表未定义、命令行输入、Make file文件中、自动化变量。
六、make工作流程
1、读取所有的Makefile文件,包括顶层Makefile和子Makefile。
2、读入被inlcude的其它Makefile。
3、初始化文件中的变量。
4、若包含隐晦规则,则先将隐晦规则推导出来。
5、为所有的目标梳理依赖关系,并创建依赖链。
6、根据时间的最新关系,判断哪些目标要重新生成。若目标根本不存在,则全部重新生成。
7、执行生成命令或生成规则。