您的位置:首页 > 其它

Makefile学习笔记

2015-11-06 19:07 375 查看
摘要: 总结Makefile中常见的用法

前言

由于博主没有深入地学习Makefile的语法与规则,所以在分析Makefile的时候遇到了不少的困难。

前两天在尝试将C++程序打包成OpenWrt的ipk文件时,博主看了大量的Makefile文件[查博文]。看得博主头大。

经[GunsNRose]的指引,我再深入地学习了Makefile的语法。

推荐书籍:《跟我一起写Makefile

详细的写法请阅读推荐书籍。这里博主只例举出常见的用法与实践过程。

正文

Makefile语法列举

1.基本格式

<目标列表> : <所需列表>
<操作命令序列>

当<目标列表>不存在,或<所需列表>中有任意一项的修改时间比<目标列表>新,那么就会执行下面的<操作命令序列>里的指令。

是否成功,决定于生成的<目标列表>是否存在且比<所需列表>更新。如果不满足就会报错,停止make过程。

2.自动推导

这个功能很有用。我们没有必要把每一个要生成.o文件都写一个 xxx.o : xxx.c

target:=myapp
objects:=main.o demo.o foo.o bar.o
$(target):$(objects)
$(CC) -o $(target) $(objects)

make会根据objects变量的,自动推导出需要编译xxx.o对应的xxx.c或xxx.cpp文件,并进行$(CC) -c xxx.c

3.include文件

类似于C语言里的#include,在make解析Makefile时,会将include后面的内容导入进来。格式如下:

include foo.make *.mk $(bar)

它会导入当前路径下 foo.make, a.mk, b.mk, c.mk 以及bar变量所指的文件名。如果找不到,会到默认的路径下去找。

如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。

4.使用变量

4.1 变量赋值

变量赋值3种方式:(1)即时推算赋值(2)延后推算赋值

延后推算赋值:

bar = Booo
foo = $(bar)
bar = Hub
All:
@echo $(foo)

make输入的结果是 Hub,而不是Booo。

如果将第二行的等号换成":=":

bar = Booo
foo := $(bar)
bar = Hub
All:
@echo $(foo)

结果就是 Booo。

":=" 是将右边的变量推算后的结果赋值给右边。

"=" 是表示左边的变量由右边的变量推算而来,在以后对左边变量的读取时再推算。

最后一种是 ?=,表示如果该变量没有定义,那么就给它赋值。属于赋引用类型。

ccc = xxxx
ccc ?= zzzz
test:
@echo $(ccc)

输出结果是:xxxx,可以第2行的赋值是无效的。如果把第1行去掉,则输出结果便是zzzz了。

4.2 变量引用

引用一个变量,就是用 $()将变量名包起来,也可以是${}。如:

A = 12
B = $(A)
C = ${A}


4.3 高级用法

foo := a.o b.o c.o
bar := $(foo:.o=.c)

将foo中每所有的".o"替换成".c"并赋给bar。

var_name := aa
aa := AAAAA
bb := BBBBB
test:
@echo $($var_name)

输出结果是AAAA。如果将var_name改成bb,那么输出的结果是BBBB。它其实是进行了两步推算:
-->@echo $($var_name)
-->@echo $(aa) #将 $(var_name) 推算出 aa
-->@echo AAAAA

类似PHP里面也有这样的用法。

4.4 变量追加

在Shell里,往PATH里追加都是:

PATH=$PATH:/usr/local/xxx

在Makefile里也可以这么做:

AA:=11
AA:=$(AA) 22

不过有更好的方式:

AA := 11
AA += 22

+= 是属于赋值,不是赋引用。如下例子:

AA := 11
BB := 66
AA += 22 $(BB)
BB = 77
test7:
@echo $(AA)

输出结果是:11 22 66,而不是11 22 77


4.5 多行变量

多行变量是格式为

define <变量名>
<变量的值>
endef

与上面的"="变量赋值是一样的意思。

foo := AAAA

define MLVar
"Hi, It's multi-line var $(foo) $(1) $(2)"
endef

foo := BBBB

test8:
@echo $(MLVar)
@echo $(call MLVar,bbb)
@echo $(call MLVar,aaa,bbb)

make test8的输出结果是:

Hi, It's multi-line var BBBB
Hi, It's multi-line var BBBB bbb
Hi, It's multi-line var BBBB aaa bbb

上面的例子,检验出了对MLVar的赋值是采用延后推算方式。

用 $(call MLVar) 可以像函数一样给MLVar变量里面的 $(1), $(2), $(3) ... $(N) 赋值。

我们可以将执行的命令放置在变量中,并执行。如下:

define rm_obj
@echo "Remove all objects"
rm -f *.o
rm -f *.obj
endef

test10:
$(rm_obj)


4.6 特殊变量

$@ 表示目标
$< 表示依赖的第一个目标
$? 比目标更新的依赖目标
$^ 所有的依赖目标的集合(无重复)
$+ 与$^相似,可重复

$(N) N为1,2,3...,表示$(call xxx)中的第N个参数

5.条件

ifdef (变量)
定义时时内容
else
没定义时内容
endif

ifndef (变量)
没定义时内容
else
定义时内容
endif

ifeq (变量1, 变量2)
相等时内容
else
不相等内容
endif

ifneq (变量1, 变量2)
不相等内容
else
相等内容
endif

根C里的宏没什么区别

6.函数

6.1 字符串处理

单词是以空格进行划份的。

下面的相关的函数:

函数格式
功能说明
$(subst <from>,<to>,<text>)
将<text>中的所有<from>字串替换为<to>,并返回
$(patsubst <pattern>,<replacement>,<text>)
用模式匹配方式替换字串,并返回
$(strip <text>)
去除<text>的空格
$(findstring <find>,<in>)
查找<in>中是否含有<find>字串,有则返回<find>
$(filter <pattern...>,<text>)
从<text>里选出符合<pattern>的单词
$(filter-out <pattern...>,<text>)与filet相反
$(sort <list>)
对列表进行排序,并去除相同的词
$(word <n>,<text>)
从<text>中取出第n个单词,以1开始
$(wordlist <s>,<e>,<text>)
从<text>中取出从<s>到<e>的单词
$(words <text>)
返回单词个数
$(firstword <text>)
返回第一个单词

6.2 文件

函数格式
功能说明
$(dir <names...>)
返回文件名序列<names>;的目录部分
$(notdir <names...>)
从文件名序列<names>;中取出非目录部分。
$(suffix <names...>)
取后缀函数
$(basename <names...>)
取前缀函数
$(addsuffix <suffix>,<names...>)
加后缀函数
$(addprefix <prefix>,<names...>)
把前缀<prefix>加到<names>中的每个单词后面
$(join <list1>,<list2>)
把<list2>;中的单词对应地加到<list1>;的单词后面。
$(wildcard <pattern>)
列举当前目录下匹配的文件

6.3 逻辑

(1)foreach

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

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

names := a b c d
files := $(foreach n,$(names),$(n).o)
test12:
@echo $(files)


(2)if

$(if <condition>,<then-part>,<else-part>)


(3)call

$(call <expression>,<parm1>,<parm2>,<parm3>...)

引用变量,并可以带参数。如:

AA='Hello, $(1) !'

test:
@echo $(call AA,John)

make输出结果:

$ make
Hello, John !

注意:AA要用延后推算$(1)才能生效。

(4)shell

$(shell <command>)

执行<command>命令,并返回标准输出结果。

test:
@echo $(shell ls)

上面就是执行ls命令。

$ make
11 22 33 44 cpp-main main.cpp main.o Makefile Makefile.bak


(5)eval
$(eval <text>)

将<text>中的文本作为Makefile的一部分。

(6)error & warning

$(error <text>)

提示错误并退出。

$(warning <text>)

提示警告,但不退出。

后记

学完了Makefile,知道怎么写了。再回头去看OpenWrt里的makefile也不那么头痛了。

http://my.oschina.net/hevakelcj/blog/411944
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  makefile