学习资料:《跟我一起写Makefile 》–陈皓
makefile 概述
我们都知道对于一个c/c++程序大概要经过编译、链接,然后生成可执行的文件,而makefile文件规定了编译和链接的一系列规则,试图解决文件间的依赖关系,makefile需要用make命令来执行。
一个最简单的Makefile
#in Makefile
include ./Makefile.def
test : $(object)
g++ -o test $(object)
$(object) : a.cpp
g++ -c a.cpp
clean:
-rm test $(object)
#in Makefile.def
object = a.o
在这个Makefile文件中,”test”是我们的第一个目标文件,make会把这个文件作为最终的目标文件,然后去一层一层地寻找其依赖文件(a.o,a.cpp),直到编译出第一个目标文件。 这样,因为clean这一目标没有和“test”直接或间接关联,所以不会被自动执行,若要显式执行,需要make clean 来清除所有的目标文件。 从这个栗子,我们可以看到Makefile所包含的五个部分:
- 显示规则—clean
- 隐晦规则—自动推导依赖关系
- 变量定义—object
- 文件指示—include
- 注释
规则
规则的书写包括两部分:依赖关系和生成目标文件的方法。
通配符
主要有*和?这两个,用法和shell一致。
文件搜寻
应用场景:make寻找文件的依赖关系时,其它文件存放在不同的目录中,主要在较大的工程项目中。
我们可以使用VPATH这一特殊变量来指定make的搜寻目录,如:VPATH = src : ../headers
。
另外关键字 vpath 则提供了更灵活的make搜寻方式,即指定文件在指定目录搜索。
伪目标
我们回到上文那个简单的Makefile文件,最后的clean就是makefile中的伪目标, 伪目标需要我们在执行make命令时,显式地执行,即make clean
。
同时,为了避免有文件和我们的伪目标同名,通常需要显式地指明一个目标是伪目标—.PHONY, 因此栗子中更保险的写法是:
.PHONY : clean
clean:
-rm test $(object)
根据伪目标的特性,我们还可以吧伪目标放在Makefile的开头,并指定该伪目标的依赖文件们,这样我们只要敲一个make命令就可以一次生成多个可执行文件all : test1 test2 test3
,多么有趣,right?
多目标
所谓多目标,其实就是如果有多个目标依赖于相同的文件,我们可以将多个目标写在同一个规则中。 通常我们需要用到一个自动化变量—$@,其表示当前规则中所有的目标变量。
静态模式
静态模式可以让我们很方便地书写多目标和多依赖文件的规则,其本质是模式匹配。
objects = foo.o bar.o
.PHONY : all
all : objects
# 目标文件们 : 目标文件的匹配模式 : 依赖文件的匹配模式
$objects : %.o : %.c
g++ -c $< -o $@
这里我们用到了另一个自动化变量—$<, 表示所有的依赖目标集。
命令
命令和shell命令一样。
变量
定义变量时,通常采用:=, 这样前面的变量不能使用后面的变量。
变量的高级用法
变量替换
#替换方式一---$(var:a=b) 分割符为空格或结束符
foo := a.o b.o c.o
bar := $(foo:.o=.c)
#替换方式二---模式匹配
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
追加变量值
objects += anoter.o
环境变量
这里的环境变量指的是系统设置的环境变量,如#root: export CFLAGS=-o
, 那么我们在Makefile中$(CFLAGS)
则为’-o’。
目标变量
test : CFLAGS = -g
这样在目标test和其所引发的所有规则中,CFLAGS 的值 都是 -g, 覆盖环境变量的值。
模式变量
%.o : CFLAGS = -o
类似目标变量。
条件分支
if ($(CFLAGS),-o)
do sth
else
do oth
endif
函数
用法:$(<func> <arguments>)
常用字符串处理函数
subst–字符串替换
$(subst <from>,<to>,<test>)
patsubst–模式字符串替换
$(patsubst <patt>,<replace>,<test>)
strip–去首尾空格
$(strip <test>)
findstring–字符串查找
$(findstring <target>,<test>)
filter/filter-out–模式过滤/剔除
$(filter <filter patt>,<test>)
word*–单词操作相关
文件名操作函数
dir/notdir/suffix/basename/addsuffix/addprefix
,顾名思义,对路径文件名进行对应操作
foreach函数
$(foreach <var>,<list>,<operation>)
#遍历name中的值,加后缀
name := a b c d
files := $(foreach n,$(name),$(n).c)
shell函数
$(shell <shell command>)
顾名思义,shell函数将shell命令的输出作为函数的返回。