Makefile简介和使用方法

1.makefile简介
makefile是和make工具一起配合使用的,用于组织管理项目源代码的编译和链接。
make工具用于找出修改过的文件,根据依赖关系,找出受影响的相关文件,最后按照规则单独编译这些文件。
makefile文件则记录依赖关系和编译规则。
makefile的本质:无论多么复杂的语法,都是为了更好地解决项目文件之间的依赖关系。
2.makefile规则介绍
makefile的一个规则由目标、依赖、命令组成,其语法结构如下所示:
目标:依赖的文件或者是其他目标命令1命令2...  
一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[tab]字符开始,[tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 makefile 中容易产生,而且比较隐蔽的错误。
3.makefile伪目标
伪目标不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令,有时也可以将一个伪目标称为标签。
使用伪目标有两点原因:
(1).避免在我们的 makefile 中定义的只执行命令的目标(此目标的目的是为了执行一系列命令,而不需要创建这个目标)和工作目录下的实际文件出现名字冲突。
(2).提高执行 make 时的效率,特别是对于一个大型的工程来说,编译的效率也许你同样关心。
将一个目标声明为伪目标的方法是将它作为特殊目标.phony的依赖。在书写伪目标规则时,首先需要声明目标是一个伪目标,之后才是伪目标的规则定义。目标clean的书写格式应该如下:
.phony: cleanclean: rm *.o temp  
4.makefile的变量
在 makefile 中,变量是一个名字(像是 c 语言中的宏),代表一个文本字符串。在 makefile 的目标、依赖、命令中引用变量的地方,变量会被它的值所取代。
变量名是不包括:、#、=、前置空白和尾空白的任何字符串。变量名最好使用字母、数字和下划线进行定义,对于其他的特殊符号不建议使用到变量名的定义中。
变量名是大小写敏感的。变量foo、foo和foo指的是三个不同的变量。
变量的引用方式是:$(variable_name) 或者 ${variable_name} 来引用一个变量的定义。
makefile的变量包含系统环境变量,自定义变量和自动化变量。
4.1.系统环境变量
使用系统环境变量需要注意以下几点:
(1).在 makefile 中对一个变量的定义或者以 make 命令行形式对一个变量的定义,都将覆盖同名的系统环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在 make 执行过程有效)。而 make 使用-e参数时,makefile 和命令行定义的变量不会覆盖同名的环境变量,make 将使用系统环境变量中这些变量的定义值。
(2).make的递归调用中,所有的系统环境变量会被传递给下一级make。默认情况下,只有系统环境变量和通过命令行方式定义的变量才会被传递给子make进程。在makefile中定义的普通变量需要传递给子make时需要使用export指示符来对它声明。
4.2.自定义变量
在makefile中自定义变量比较简单,下面简单的区分下各个符号的含义:
(1).延迟赋值:=;
使用=号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量并不会立即展开对其他变量的引用,而是在真正使用到该变量时才进行展开。例如下述例子中,只有在echo (a),虽然在定义变量b之前a的值为123,但是在执行echo $(b)时,a的最终值为456,因此会输出456。
(2).立即赋值::=;
立即赋值是相对于延迟赋值的,使用:=号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量会立即展开对其他变量的引用。可以结合下面的例子进行理解。
(3).为空赋值(条件赋值):?=;
使用?=号定义变量时,只有定义的变量在之前没有赋值的情况下才会对这个变量进行赋值。可以结合下面的例子进行理解。
(4).追加赋值:+=;
使用+=符号,可以对变量的值进行追加。可以结合下面的例子进行理解。
例子:
# 延迟赋值a=123b=$(a)a=456# 立即赋值c=123d:=$(c)c=456# 空赋值e?=123e?=456# 追加赋值f?=123f+=456.phony:allall: echo $(b) #结果:456 echo $(d) #结果:123 echo $(e) #结果:123 echo $(f) #结果:123 456  
4.3.自动化变量
makefile中常用的自动化变量如下描述:
$<:第一个依赖文件;
$^:全部的依赖文件;
$@:目标;
5.makefile条件分支
条件分支语法如下:
ifeq (var1,var2) ...else ...endififneq (var1,var2) ...else ...endif  
例子:
arch ?= x86ifeq ($(arch), x86) cc=gccelse cc=arm-linux-gnueabihf-gccendif  
6.makefile的常用函数
makefile还是提供了比较多的函数供我们使用,这里介绍下常用的几个函数。
6.1.patsubst
$(patsubst pattern,replacement,text)
函数名称:模式替换函数 - patsubst。
函数功能:搜索text中以空格分开的单词,将符合模式pattern替换为replacement。参数pattern中可以使用模式通配符%来代表一个单词中的若干字符。如果参数replacement中也包含一个%,那么replacement中的%将是pattern中的那个%所代表的字符串。在pattern和replacement中,只有第一个%被作为模式字符来处理,之后出现的不再作模式字符(作为一个字符)。在参数中如果需要将第一个出现的%作为字符本身而不作为模式字符时,可使用反斜杠进行转义处理。
返回值:替换后的新字符串。函数说明:参数text单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。
如果感觉上述的文字说明有点不太好理解,那么看看下面的示例吧,之后再结合示例看上面的文字说明应该就比较好理解了,示例如下:
.phony:allall: echo $(patsubst %.c,%.o,x.c.c bar.c) #结果:x.c.o bar.o  
上述示例的运行结果:把字串x.c.c bar.c中以.c 结尾的单词替换成以.o 结尾的字符。函数的返回结果是x.c.o bar.o。
6.2.notdir
$(notdir names…)
函数名称:取文件名函数 - notdir。
函数功能:从文件名序列names…中取出非目录部分。目录部分是指最后一个斜线(/)(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。
返回值:文件名序列names…中每一个文件的非目录部分。
函数说明:如果names…中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当names…中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定!这是此函数的一个缺陷。
示例:
.phony:allall: echo $(notdir src/foo.c hacks) #结果:foo.c hacks  
6.3.wildcard
$(wildcard pattern)
函数名称:获取匹配模式文件名函数 - wildcard。
函数功能:列出当前目录下所有符合模式pattern格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式pattern的文件名。
函数说明:pattern使用shell可识别的通配符,包括?(单字符)、*(多字符)等。
示例:
.phony:allall: echo $(wildcard *.c) #结果:当前目录下所有.c 源文件列表  
6.4.foreach
$(foreach var,list,text)
函数foreach不同于其它函数。它是一个循环函数。类似于 linux shell 中的for 语句。
函数名称:循环函数 - foreach。
函数功能:这个函数的工作过程是这样的:如果需要(存在变量或者函数的引用),首先展开变量var和list的引用;而表达式text中的变量引用不展开。执行时把list中使用空格分割的单词依次取出赋值给变量var,然后执行text表达式。重复直到list的最后一个单词(为空时结束)。text中的变量或者函数引用在执行时才被展开,因此如果在text中存在对var的引用,那么var的值在每一次展开式将会到的不同的值。
返回值:空格分割的多次表达式text的计算的结果。
备注:函数中参数var是一个局部的临时变量,它只在foreach函数的上下文中有效,它的定义不会影响其它部分定义的同名var变量的值。
示例:
.phony:alldirs := a b c dfiles := $(foreach dir,$(dirs),$(wildcard $(dir)/*))all: echo $(files) #结果:files是a b c d 4 个目录下所有文件组成的文件列表.  
示例分析:例子中,text的表达式为 $(wildcard $(dir)/*)。表达式第一次执行时将展开为 $(wildcard a/*);第二次执行时将展开为 $(wildcard b/*);第三次展开为 $(wildcard c/*); ...;以此类推。
7.其他
默认规则:.o文件默认使用对应的.c文件来进行编译。


ARM三年时间平均每年出货超过220亿ARM芯片 并在26年中出货首次超过1000亿
美国新颖医疗器械—新型“集尿袋”
SWARM虫型机器人能进入发动机内部进行检修
UDS之19服务中04子服务:读取快照数据
Gartner:2018年十大战略科技发展趋势详解
Makefile简介和使用方法
FPGA设计的迭代闭环思维和增量编译的用法
全球芯片短缺何时恢复
你觉得你的物联网安全吗
S40多点触控 诺基亚3110仅售740元
三星Galaxy S11e渲染图曝光,或将采用屏下指纹识别
深度探讨半导体的历史、应用及未来发展趋势
无缝液晶拼接屏的应用场景有哪些
恩智浦推动未来可穿戴技术创新
英特尔正式推出其第九代酷睿台式机处理器
中国自动驾驶仿真蓝皮书发布 内容涵盖自动驾驶仿真测试所有领域
物联网“十二五”5000亿金矿等挖掘
比亚迪的轮边电机是什么 比亚迪的轮边电机有何优势
Innovasic以太网/IP网络接口已通过ODVA一致性测试
禾多科技:让中国汽车行业成为全球智能化标杆