条件判断
使用条件判断可让make根据运行时的不同情况选择不同的执行分支
libs_for_gcc = -lgnu
normal_libs =
foo : $(objects)
ifeq ($(CC), gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
也可以写的更简洁点
ifeq($(CC), gcc)
libs = $(libs_for_gcc)
else
libs = $(normal_libs)
foo : $(objects)
$(CC) -o foo $(objects) $(libs)
使用到了三个关键字:ifeq else endif
- ifeq
如果要比较make的命令行参数可以使用
ifeq(< arg1>, < arg2>)
- ifneq 如果不相同
- ifdef 如果变量的值为非空,则表达式的值为真,否则表达式的值为假
foo =
var = $(foo)
ifdef var
bozz = yes
else
bozz = no
endif
foo =
ifdef foo
bozz = yes
else
bozz = no
endif
则我们知道,第一个$ (bozz)为yes,第二个$(bozz)为no
- ifndef 他的作用与ifdef相反
特别注意,make是在读取makefile时就计算条件表达式的值,所以最好不要把自动化变量$@放入条件表达式中,因为自动化变量是在运行时才有的
makefile函数调用
函数的调用语法
函数的调用与变量的使用方法类似,都是通过$来标识
$(< function> < arguments, arguments, arguments>)
参数之间以“,”分隔,函数名和参数之间以空格分隔
函数中的参数可以使用变量 eg:$ (subst a,b,$(x))
字符串处理函数
$(subst < from>,< to>,< string>)
将字符串中的字符替换函数
comma := ,
empty :=
space := $ (empty)$(empty)
string := a b c
bar := $(subst $ (space),$ (comma),$(string))
将a b c替换成为a,b,c
$(patsubst < pattern>,< replacement>,< text>)
模式字符串替换函数,查找text中的单词(单词以空格、Tab、回车、换行分隔)是否符合模式< pattern>,如果匹配的话,则以< replacement>替换
pattern可以包含通配符 %,表示任意长度的字串
bar := $(patsubst %.c,%.o,x.c.c foo.c)
得到的$(bar) = x.c.o foo.o
$(strip < string>)
去空格函数,去掉字符串开头或者结尾的空字符
bar := $(strip a b c )
得到的$(bar) = a b c
$(findstring < find>,< in>)
查找字符串函数,在字符串in中查找find字串,如果找到就返回find,否则返回空字符串
$(findstring a,a b c)
$(findstring a, b c)
第一个函数返回字符串a,第二个函数返回空字符串
$(filter < pattern…>,< text>)
过滤函数,以pattern模式过滤text中的字符串中的单词,只保留符合pattern的单词,返回符合模式的pattern的字串
sources := 1.c 2.c 3.o 4.s
foo : $(sources)
gcc $ (filter %.c %.s,$(sources)) -o foo
$(filter-out < pattern>,< text>)
反过滤函数,按照pattern的要求过滤text的内容,返回过滤后不存在pattern的单词
objects = main1.o foo.o main2.o bar.o
mians = mian1.o main2.o
$(filter-out $ (mians),$(objects))的返回值就是 foo.o bar.o
$(sort < list>)
排序函数,给字符串list中的单词首字母排序(升序),并且会去除重复的单词
$(sort foo bar lose)返回的就是bar foo lose
$(word < n>,< text>)
取单词函数,从text中取第n个单词,从1开始数;如果n比text中的单词数要大,那么返回空字符串
$(word 2, foo bar baz)返回的值为bar
$(wordlist < s>,< e>,< text>)
取单词串函数,从text中取从s到e的单词串,s和e是数字
如果s比e大,则返回的是空字符串
如果e比text的单词数大,则返回的是text
$(wordlist 2,3,foo bar baz)返回的就是 bar baz
$(words < text>)
统计text中的单词数量
$(words foo bar baz)返回的值就是3
$(firstword < text>)
取text中第一个单词
$(firstword foo bar baz)返回值就是foo
也可以使用
$(word 1,foo bar baz)返回值也是foo
练习:
假设搜索路径的特殊变量VPATH = src : …/header
修改为cc 或gcc 搜索头文件路径的参数是多少,并且如何修改
参数是目录前面加上-I,因为有两个目录,所以分别要加上-I,分隔符冒号也要修改为空格
为-Isrc , -I…/header
使用subst和patsubst修改:
$ (patsubst %,-I%,$ (subst :, ,$(VPATH)))
最后修改为:-Isrc -I…/header
override CFLAGS += $ (patsubst %,-I%, ( s u b s t : , , (subst :, , (subst:,,(VPATH)))
文件名操作函数
这里的函数主要是处理文件名,每个函数的参数字符串都会被当做一个或者一系列的文件名来对待,最重要是文件名
$(dir < names…>)
取出目录部分,所谓的目录部分就是最后一个 / 之前的内容
如果没有/,则返回 “./”
$(dir src/foo.c hacks)的返回值是 “src/”加上 “./”
因为这是两个路径:src/foo.c 、hacks
$(notdir < 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.c hacks)的返回值是 src/foo src-1.0/bar hacks
$(addsuffix < suffix>,< names…>)
加后缀函数,将suffix加到每个names中的每个单词后面
$(addsuffix .c,foo bar)返回值是 foo.c bar.c
$(addprefix < prefix>,< names…>)
加前缀函数,将prefix加到每个names中的每个单词前面
$(addprefix src/,foo bar)的返回值是src/foo src/bar
$(join < list1>,< list2>)
连接函数,将list2单词对应连接到每个list1的单词的后面
如果list1的单词数大于list2的单词数,则list1的单词就会保持原样
如果list1的单词数小于list2的单词数,则list2的单词就会保持原样连接
$(join aaa bbb,111 222 333)的返回值为 aaa111 bbb222 333
foreach函数
foreach主要用于循环
$(foreach < var>,< list>,< text>)
表示将list中的单词逐个取出放入var指定的容器中,每放一次就会执行一次text中的表达式,得到结果就会返回一个字符串,每次返回的字符串都会用空格分隔开,最后当整个循环结束时,< text>所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值
list := a b c d
$ (foreach n,$ (list),$(n).o)则返回的值为a.o b.o c.o d.o
var只是一个局部变量作用域只会存在于foreach中
call函数
call用来创建新的参数化的函数
你可以写一个复杂的表达式,定义很多参数,并用call来依次传递参数
$(call < expression>,< parm1>,< parm2>,< parm3>…)
例如我定义了一个表达式:reverse = $(1) $(2)
foo = $(call reverse,a,b)
a和b依次覆盖了1和2,同时得到的返回值foo的值就是a b
reverse = $(2) $(1)
foo = $(call reverse,a,b)
a和b依次覆盖了1和2,同时得到的返回值foo的值就是b a
origin函数
origin函数不同于其他函数,其他函数都是去操作参数的值,而这个函数通过返回值告诉你参数的来源
$(origin < variable>)
此时variable是变量的名字,而不是变量的引用,所以不要去加$
origin函数的返回值 | 解析 |
---|---|
“undefined” | 如果< variable>从来没有定义过,origin 函数返回这个值“undefined”。 |
“default” | 如果< variable>是一个默认的定义,比如“CC”这个变量 |
“environment” | 如果< variable>是一个环境变量,并且当 Makefile 被执行时,“-e”参数没有被打开。 |
“file” | 如果< variable>这个变量被定义在 Makefile 中。 |
“command line” | 如果< variable>这个变量是被命令行定义的。 |
“override” | 如果< variable>是被 override 指示符重新定义的。 |
“automatic” | 如果< variable>是一个命令运行中的自动化变量。 |
练习
如果我的makefile中包含了文件make.def,在make.def中存在一个变量bletch,而我们的环境中也有一个环境变量叫做bletch。此时判断一下,如果变量来源于环境,那么我们就把变量重定义了;如果变量来自于make.def或者是命令行等环境,那我们就不重新定义
ifdef bletch
ifeq “$(origin bletch)” “environment”
bletch = bar, aa
endif
endif
这里如果使用override意思是覆盖变量的使用,但如果bletch是命令行的参数传参,就有可能被override覆盖
Shell函数
shell函数的参数就是操作系统shell的命令
也就是说shell函数把执行操作系统命令后的输出作为函数的返回
content := $(shell cat foo)
files := $(shell echo *.c)
但是过量使用shell函数是对系统性能是有害的
控制make的函数
当按下make时,也有一些函数能够控制make的使用,比如在make时打印出信息,并根据信息的反馈来决定是否要进行下一步make
$(error < text…>)
产生了一个致命的错误,错误信息打印在text中,这个函数并不是一使用就会打印错误信息
可以将错误信息定义在一个变量中,并在后续的脚本中使用这个变量
ifdef ERROR_01
$(error error is $(ERROR_01))
endif
当ERROR_01被定义时,才能执行error调用
还有一种情况是err被执行时才发生error调用:
ERR = $(error found an error)
.PHONY : err
err ; $(ERR)
就是把错误信息啥的都写在一个变量里,随时调用
最后当产生错误时make就会退出
$(warning < text…>)
这个函数很像error,但是出错时不会退出,只是警告而已