makefile简介
在基于Linux嵌入式系统开发的过程中,构建一个工程中的源文件不计其数,按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列的规则来制定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更复杂的功能操作。因为Makefile就像一个shell脚本一样,其中也可以执行操作系统的命令。Makefile带来的好处就是“自动化编译”。一旦写好了makefile文件,只需要一个make命令,整个工程便会完全自动进行编译,极大地提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具。
所要完成的Makefile文件描述了整个工程的编译、链接等规则,其中包括:工程中的那些源文件需要编译以及如和编译,需要创建那些库文件以及如何创建这些库文件,如何产生想要的可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写Makefile的好处是能够使用一行命令来完成“自动化编译”。一旦提供一个(通常对于一个工程来说会是多个)正确的Makefile,编译整个工程所要做的唯一的一件事就是在shell提示符下输入make命令。整个工程完全自动编译,极大地提高了效率。
Makefile文件编写规则
一个Makefile文件包含一系列规则,样式如下:
目标(target)…:依赖(prerequisites
<tab>命令(command)
目标通常是要生成的文件的名称,可以是可执行文件或.o文件,也可以是一个执行的动作名称,如“clean”。依赖(依赖条件、依赖关系)是用来产生目标的材料(比如源文件),一个目标经常有几个依赖。命令是生成目标时执行的动作,一个规则可以含有几个命令,每个命令占一行。每个命令行前必须是一个Tab字符,即命令行第一个字符是由Tab键产生的空格(不能用多个空格组成)。
规则一般用于解释怎样和何时重建目标。make首先调用命令处理依赖,进而才能创建或更新目标。一个规则也可以是用于解释怎样和何时执行一个动作,即打印提示信息。一个Makefile文件也可以包含规则以外的其他文本。
Makefile文件里的赋值
Makefile文件里,变量定义的语法形式如下:
immediate = deferred
immediate ?= deferred
immediate := immediate
immediate += deferred or immediate
define immediate
deferred
endef
在GNU make中对变量赋值有两种方式,延时变量和立即变量。区别在于它们的定义方式和扩展是的方式不同,前者在这个变量使用时才可以展开,即当真正使用时这个变量的值才确定;后者在定义是它的值就已经确定了。使用“:=”定义的变量是立即变量。“?=”仅仅在变量还没有定义的情况下有效,即“?=”被用来定义第一次出现的变量。
对于“+=”,右边的变量如果在前面已经使用“?=”定义为立即变量,则它也是立即变量,否则为延时变量。
Makefile函数
Makefile文件里,函数调用的格式如下:
$(function arguments)
“function”是函数名,“arguments”是该函数的参数,参数和函数之间用空格或Tab隔开,如果有多个参数,它们之间用逗号隔开。
常用函数
字符串处理函数
- List ite
subst
$(subst , ,)
字符串替换函数,把字符串text中的from字符串替换为to,返回被替换过的字符串。 - .patsubst
$(patsubst , ,)
模式字符串替换函数,查找text中的单词是否符合pattern,如果匹配,则以replacement替换。pattern可以使用%通配符,表示任意长度的字串
objects = foo.o bar.o bb.c
$(patsubst %.o, %.c, $(objects)) 结果是foo.c bar.c bb.c - strip
$(strip )
去掉string字串中开头和结尾的空格字符。注意只去掉开头和结尾的。 - findstring
$(findstring , )
查找字符串函数,在字符串in中查找find字符串,如果找到,返回find,否则返回空字符串
$(findstring a, a b c) //返回a
$(findstring a, b c) //返回“”空字符串 - filter
$(filter ,)
过滤函数,以pattern模式过滤text字符串中的单词,保留符合模式pattern的单词。返回符合模式pattern的字串
source := foo.c bar.c baz.c ugh.h
foo : $(source)
cc $(filter %.c %.s, $(source)) -o foo
$(filter %.c %.s, $(source))的返回值是foo.c bar.c baz.c - filter-out
$(filter-out ,)
反过滤函数,以pattern模式过滤text字符串中的单词,去除符合pattern的单词 - sort
$(sort )
排序函数,给list中的单词排序(升序),返回排序后的字符串,sort函数会去掉list中相同的单词 - word
$(word ,)
取单词函数,取字符串中的n个单词,从1开始数。如果n比text中的单词数大,返回空字符 - wordlist
$(wordlist, ,)
取单词串函数,从字符串text中从s到e的单词串,如果s比text中的单词要大,则返回空字符串
$(wordlist 2, 3, foo bar baz),返回值为“bar baz” - words
$(words) 统计字符串中的单词个数,返回字符串中的单词数。
k.firstword
$(firstword) 取字符串text中的第一个单词
文件名操作函数
- dir
$(dir ) 从名字序列中取出目录部分,各个名字用空格分隔 - notdir
$(notdir ) 从名字列表中取出非目录部分,就是取出文件名 - suffix
$(suffix ) 取后缀函数,从文件名序列中取出各个文件名的后缀,比如.c .h等。 - basename
$(basename ) 取前缀名字,从文件名序列中取出各个文件名的前缀部
$(basename src/foo.c src/bar.c hacks) 返回值为src/foo src/bar hack - addsuffix
$(addsuffix < suffix >, < names >) 加后缀函数, 将后缀suffix加到names中的每个单词的后面,返回加过后缀的文件名序列 $(addsuffix .c, foo bar)返回值为foo.c bar.c - addprefix
$(addprefix , ) 加前缀函数,把前缀prefix加到names中的每个单词后面。示例:
$(addprefix src/,foo bar),返回值为src/foo src/barg.join
$(join , ) 链接函数,将list2中的单词对应的加到list1的单词后面。如果list1中的单词个数要比list2中的多,那么list1中多出来的单词保持原样,如果list2的单词个数比list1多,则list2中多出来的单词复制到list1中。
$(join aaa bbb, 111 22 333) 返回值为aaa111 bbb222 333
foreach函数
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
)
上
面
的
例
子
中
,
n
a
m
e
s
中
的
单
词
会
被
挨
个
取
出
,
并
存
在
n
中
,
(n).o) 上面的例子中,names中的单词会被挨个取出,并存在n中,
(n).o)上面的例子中,names中的单词会被挨个取出,并存在n中,(n).o每次计算出一个值,这些值以空格分隔,所以最后files的值为 a.o b.o c.o d.o
if函数
if函数很像make的条件语句ifeq,if的语法为:
$(if < condition >,< then-part >)或者是 $(if < condition >,< then-part >,< else-part >)
condition参数是if的表达式,如果其返回的为非空字符串,那么这个表达式相当于真。
call函数
call函数是唯一 一个可以用来创建新的参数化的函数,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。
$ (call < expression >, < parm1 >,< parm2 > …) 当执行这个函数时,expression中的变量,如$(1), $(2), $(3)等,会被parm1, parm 2, parm3依次取代。而expression的返回值就是call函数的返回值。例如:
reverse = $(1) $(2)
foo = $(call reverse, a, b) 那么foo的值就是a b。
origin函数
origin函数并不操作变量的值,他只是告诉你这个变量是哪里来的, 语法为:
$ (origin < variable >) 注意variable是变量的名字,不是引用,所以不要用$号。
返回值:
undefined : 没有被定义过
default : 默认的定义,如CC
file: 在makefile中被定义
command line : 被命令行定义
override : 被override指示符重新定义的
automatic : 自动化变量。
shell函数
shell函数就是执行shell的命令,它的参数就是操作系统shell的命令,shell函数把执行操作系统命令后的输出作为函数返回。这里要注意,makefile中不是任何地方都能直接运行shell命令的,我们从前面的介绍会发现,只有target后面跟的command才能直接运行shell函数,其他地方都需要利用shell函数来运行shell command。
例子:
contents := $(shell cat foo)
files := $(shell echo *.c)//注意这里不会真正打印到屏幕,而是将echo命令的返回值赋值给files变量
注意,shell函数会生成一个shell程序来执行命令,所以要注意其性能。
控制make的函数
- error
$(error <text …>)error函数会产生一个致命错误,当执行该函数时,会打印出text信息,然后终止执行 - warning
$(warning <text …>) 和error函数类似,但是它只是输出一段warning,不会让make终止。
make的运行
make的退出码
make命令有三个退出码:
0 - 表示成功执行
1 - 如果make运行时出现任何错误,返回1
2 - 如果使用了make的-q选项,并且make使得一些目标不需要更新,那么返回2