【北京迅为】i.MX6ULL终结者Makefile语法

1. 初识Makefile

Makefile文件是由一些列的规则组合而成的,格式如下:
target(目标文件) …: prerequisites(依赖的文件) …
command(命令)


比如3.3.2中写的Makefile的规则:
main.o:main.c
gcc -c main.c
这条规则的main.o是目标文件(将要生成的文件),main.c是依赖的文件(生成main.o需要的文件),“gcc -c main.c”是生成main.o需要运行的命令。Makefile中每行的脚本如果有缩进的情况,必须使用“Tab”键缩进,切记不能使用空格缩进(这是Makefile的语法要求),大家一定要切记!
下面我们来分析一下图3.3.2章节中写的Makefile文件,脚本如下:
1 main:main.o calc.o
2 gcc -o main main.o calc.o
3 main.o:main.c
4 gcc -c main.c
5 calc.o:calc.c
6 gcc -c calc.c
7
8 clean:
9 rm -rf *.o
10 rm -rf main

该脚本一共有4条规则,1、2行是第一条规则,3、4行是第二条规则,5、6是第三条规则8、9、10是第四条规则。我们在运行make命令的时候,会解析当前目录下的这个Makefile文件里面的规则,首先解析第一条规则,第一条规则中的目标文件是main,只要完成了该目标文件的更新,整个Makefile的功能就完成了。在第一次编译的时候,由于目标文件main不存在,则会解析第一条规则,第一条规则依赖文件main.o、calc.o,make命令会检查当前目录下是否有这两个.o文件,经过检查发现没有,然后make会在Makefile中查找分别以main.o、calc.o为目标的规则(第二条,第三条规则)。执行第二条规则依赖的文件是main.c,make命令检查发现当前目录下有这个文件,然后执行第二条规则的命令“gcc -c main.c”生成main.o文件。然后执行第三条规则,第三条规则的目标文件是calc.o,依赖的文件是calc.c,make命令检查发现当前目录下存在该文件,然后执行第三条规则的命令“gcc -c calc.c”生成calc.o文件,至此第一条规则依赖的main.o、calc.o;两个文件已经生成了,然后运行第一条规则的命令“gcc -o main main.o calc.o”生成main文件。因为make命令运行的时候会从Makefile的第一条规则开始解析,然后根据第一条规则的依赖文件去遍历文件中的“对应规则”,然后在根据“对应规则”的依赖文件去遍历“对应的规则”,采用这样递归的方式会遍历出完成第一条规则所需要的所有规则。下面我们来看看第四条规则的目标文件是clean,我们通过查看发现该规则与第一条规则没有关联,所以我们在运行make命令的时候,不会遍历到该规则。我们可以在终端输入“make clean”命令来运行第四条规则,第四条规则没有依赖的文件,所以执行运行命令“rm -rf *.o”和“rm -rf main”,这两条命令的功能是删除以.o为结尾的所有文件,删除文件main,运行如图 1.1所示:在这里插入图片描述

图 1.1

通过上图可以看到main.o、mcalc.o和main三个文件已经删除了。通过该规则我们可以清除编译产生的文件,实现工程的清理。

我们再来总结一下make命令的执行过程:
1.make命令会在当前目录下查找以Makefile命名的文件
2.找到Makefile文件,就会按照Makefile里面的规则去编译,并生成最终文件
3.当发现目标文件不存在或者所依赖的文件比目标文件新(修改时间),就会执行规则对应的命令来更新。我们可以看到make是一个工具,他会通过Makefile文件里面的内容来执行具体的编译过程。

2. Makefile的变量

在3.3.2章节中的Makefile第一条规则:

main:main.o calc.o                                                                        
	gcc -o main main.o calc.o       

在该规则中main.o、calc.o这两个文件我们输入了两次,由于我们的Makefile文件内容比较少,如果Makefile复杂的情况下,这种重复的输入就会非常占用时间,而且修改起来也会很麻烦,为了解决这个问题,Makefile可以使用变量。Makefile的变量是一个字符串。比如上面的规则我们声明一个变量,叫objects,objs或者是OBJ,反正不管是什么,只要能够表示main.o、calc.o就行了,我们修改上面的规则

1	objects = main.o calc.o                                                                  
2	main:$( objects)                                                                         
3		gcc -o main $( objects)        

我们来分析下修改后的规则,首先第一行是我们定义了一个变量objects,并给赋值“main.o calc.o”,第二行、第三行用到了变量objects。Makefile中的变量引用方式是“$(变量名)”,变量objects的赋值使用“=”,Makefile中变量的赋值还可以使用“:=”、“?=”、“+=”,这四种赋值的区别如下:
1.“=”赋值符
我们先在用户根目录的work目录下创建一个Makefile脚本,输入下面的内容:

1	ceshi1 = test                                                                         
2	ceshi2 = $(ceshi1)                                                                    
3	ceshi1 = temp                                                                         
4                                                                                      
5	out:                                                                                  
6		@echo ceshi2:$(ceshi2)  

第一行我们定义了变量并赋值“test”,第二行定义了变量ceshi2并赋值变量ceshi1,第三行修改变量ceshi1的值为“temp”,第五行、第六行是输出变量ceshi2的值。我们在终端输入“make out”命令,如图 2.1所示:在这里插入图片描述

图 2.1

在上图可以看到变量ceshi2的值是temp,也就是变量ceshi1最后一次的赋值。

2.“:=”赋值符
我们修改“=”赋值符中的代码,第二行的“=”改成“:=”,代码如下:

1	ceshi1 = test                                                                         
2	ceshi2 := $(ceshi1)                                                                    
3	ceshi1 = temp                                                                         
4                                                                                      
5	out:                                                                                  
6		@echo ceshi2:$(ceshi2)          

我们在终端输入“make out”命令,如图 2.2所示:在这里插入图片描述

图 2.2

我们可以看到上图的运行结果输出变量ceshi2的值是test,虽然在第三行我们修改了变量ceshi1的值,通过本实验我们可以看到“:=”赋值符的功能了。

3.“?=”赋值符

ceshi ?= test      

“?=”赋值符的作用是如果前面没有给变量ceshi赋值,那么变量就赋值“test”,如果前面已经赋值了,就使用前面的赋值。

4.“+=”赋值符

objs = main.o                                                                           
objs += calc.o   

上面的脚本最后变量objs的值是“main.o calc.o”,“+=”赋值符的功能是实现变量的追加。

3. 条件判断

使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。其语法有下面两种:
1.
<条件比较>
[条件为真时执行的脚本]
endif
2.
<条件比较>
[条件为真时执行的脚本]
else
[条件为假时执行的脚本]
endif
条件比较用到的比较关键字有:ifeq、ifneq、ifdef、ifndef。
ifeq表示如果比较相等,语法如下:
ifeq(<参数1>, <参数2>)
ifneq表示如果不相等,语法如下:
ifneq(<参数1>, <参数2>)
ifdef表示如果定义了变量,语法如下:
ifdef <变量名>
ifndef表示如果没有定义变量,语法如下:
ifndef <变量名>

4. 使用函数

在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函数的返回值可以当做变量来使用。
函数的调用很像变量的使用,也是以“$”来标识的,语法如下:
$(<函数名> <参数集合>)
或者:
${<函数名> <参数集合>}

函数名和参数集合之间以空格分隔,参数集合的参数通过逗号分隔。函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量。函数中的参数可以使用变量。为了风格的统一,函数和变量的括号最好一样,如使用“$(subst a,b,$(x))”这样的形式,而不是“$(subst a,b,${x})”的形式。因为统一会更清楚,也会减少一些不必要的麻烦。
接下来我们介绍几个常用的函数,其它的函数可以参考文档《跟我一起写 Makefile》。

1.subst函数

$(subst <from>,<to>,<text>)         

此函数的功能是把字符串<text>中的<from>字符串替换成<to>,函数返回被替换过后的字符串。如下示例:
$(subst ee,EE,feet on the street)
以上脚本实现把字符串“feet on the street”中的“ee”字符串替换成“EE”字符串,替换后的字符串为“fEEt on the strEEt”。
2. patsubst函数

$(patsubst <pattern>,<replacement>,<text>)   

此函数的功能是查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里<pattern>可以包括通配符“%”,表示任意长度的字符串。如果<replacement>中也包含“%”,那么<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字符串。(可以用“\”来转义,以“%” 来表示真实含义的“%”字符)。函数返回被替换过后的字符串。如下示例:

$(patsubst %.c,%.o,x.c bar.c)        

以上脚本实现把字符串“x.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.o bar.o”

3.strip函数

$(strip <string>)    

此函数的功能是去掉字符串中开头和结尾的空字符,函数返回被去掉空格的字符串值。如下示例:
$(strip a b c )
以上脚本实现把字符串“a b c ”去掉开头和结尾的空格,结果是“a b c”。
4. findstring函数

$(findstring <find>,<in>)    

此函数的功能是在字符串中查找字符串,如果找到,那么返回,否则返回空字符串,如下示例:
$(findstring a,a b c)
$(findstring a,b c)
以上脚本,第一个返回“a”字符串,第二个返回空字符串。

5.dir函数

$(dir <names...>)       

此函数的功能是从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。返回文件名序列的目录部分,如下示例:
$(dir src/foo.c hacks)
以上脚本运行结果返回“src/”。
6. notdir函数

$(notdir <names...>)    

此函数的功能是从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分,返回文件名序列的非目录部分,如下示例:
$(notdir src/foo.c)
以上脚本返回字符串“foo.c”
7. foreach函数

$(foreach <var>,<list>,<text>)     

此函数的功能是把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式。每执行一次<text>会返回一个字符串,循环过程中,<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。所以,<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 函数当中)。

5. 在规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make命令支持三种通配符:“*”,“?”和“[...]”,这是和 Unix 的 B-Shell 是相同的。“~”字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户根目录下的test文件。而“~admin/test”则表示用户admin根目录下的 test文件。
通配符代替了一系列的文件,如“*.c”表示所有后缀为 .c 的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“\”,如“*” 来表示真实的“*”字符,而不是任意长度的字符串。
下面我们来看几个具体的示例:

clean:                                                                                 
	rm -rf *.o   

上面这个示例说明通配符可以在规则的命令中使用。

print: *.c     

上面这个示例说明通配符可以在规则的依赖中使用

objects = *.o   

上面这个示例表示了,通配符同样可以用在变量中。并不是说[*.o]会展开,objects的值就是“*.o”。Makefile 中的变量其实就是 C/C++中的宏。如果你要让通配符在变量中展开,也就是让 objects 的值是所有[.o]的文件名的集合,那么,你可以这样:

objects := $(wildcard *.o)            

这种用法由关键字“wildcard”指出,关于Makefile的关键字可以参考文档《跟我一起写Makefile》。

关于Makefile的相关内容我们就介绍到这里,本节只是对Makefile做了基本的讲解,Makefile还有大量的知识点,有兴趣的朋友可以参考文档《跟我一起写Makefile》来深入的了解Makefile。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BeiJingXunWei/article/details/108234387