Makefile tutorial
总结一下makefile tutorial里的教程,总不能一直拿bash做构建系统
Makefile rules
rules是makefile最基本的内容,当运行make时,其实就是在执行对应rule下面的命令
1  | target: prerequirement  | 
一个rule由以上结构组成,target是rule的目标文件名称,prerequirement是rule的依赖文件,只有这些文件都存在才会执行对应的rule,command是make命令或者shell命令,前面有一个tab缩进
rule之间可以互相依赖
1  | test: test.o  | 
比如上面这个,test的依赖是test.o,运行make时会先运行test.o,再执行test
变量
makefile变量只能是纯文本,引号之类的也不会转义,有三种给变量赋值的方式
1  | a:= 1  | 
:=是立刻赋值,=是当使用到了才赋值(延迟赋值),?=是未初始化则赋值
使用+=实现变量字符串的拼接
变量通过$()来使用
1  | a:= test1 test2 test3  | 
target
rule可以设置多个prerequirement
1  | all: one two three  | 
这个经典的例子就是make all,等价于make one two three
rule同时也可以设置多个target,等价于执行好多遍
1  | f1.o f2.o:  | 
预设变量
下面是几个常用的预设变量
$@target名$?所有时间戳比target新的prerequirement$^所有的prerequirement$<第一个prerequirement
正则匹配
只有*和%两种匹配符,*正则匹配只建议由wildcard函数包裹使用
*的功能是匹配任意字符,比如$(wildcard *.c)就是目录下所有后缀为.c的文件%的功能是匹配并替换,通常用在模式固定的rule或者字符串替换中
1  | %.o : %.c  | 
这里就是匹配目录下所有.c文件的名称作为pattern然后生成对应名字的.o文件
1  | objects = foo.o bar.o all.o  | 
这个则是从objects中提取符合%.o的模式,然后替换到%.c中,等于是为每个.o寻找对应的.c文件并编译
c/cpp
makefile会自动对目录下的与目标同名的c/cpp文件生成编译命令,不建议使用这个技巧
n.o会以n.c作为源被以下命令编译
$(CC) -c $(CPPFLAGS) $(CFLAGS) $^ -o $@如果是n.cc或n.cpp则是如下
$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $^ -o $@n会以n.o默认以以下命令链接
$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
命令
在命令前加@可以静默执行,make -s可以让所有命令静默执行
每行命令的shell环境是独立的,但是可以用;在一行连接多个命令,然后用\ 连接多个行
1  | all:  | 
shell变量
shell环境变量和makefile变量是两种不同的东西,要使用shell环境变量,则要在变量名前加$$且不用括号包裹(这里类似转义)
1  | make_var = I am a make variable  | 
递归调用make
如果需要在子文件夹中递归调用make则使用$(MAKE)命令而不是shell命令,因为前者会帮助传递makefile变量
export
由export命令设置成环境变量的makefile变量可以被所有的rule和子makefile访问
1  | shell_env_var=Shell env var, created inside of Make  | 
这里输出的两行都是一样的
override
带override修饰的变量会忽略来自命令行的传参override option_one = did_overrideoption_one永远是did_override
target特化的变量
1  | all: one = cool  | 
变量可以为不同的target设置不同的值,这里make all时one的值为cool,否则为空
这里的target也可以用正则
1  | %.c: one = cool  | 
条件表达式
1  | foo = ok  | 
函数
函数通过这两种模式的任意一种调用${fn,arguments},$(fn,arguments)
第一个参数的参数前不要带空格,否则空格会被识别成参数字符串的一部分
字符串替换
$(patsubst pattern,replacement,text)patsubst函数从text中筛选符合pattern的空格分隔的字符串并替换成replacement,这里通常结合正则使用
1  | foo := a.o b.o l.a c.o  | 
批量修改
$(foreach var,list,text)foreach函数把list中按空格分隔的每个字符串修改成新的,var是遍历时的变量,在text中使用,text是目标字符串
1  | foo := who are you  | 
过滤
$(filter pattern,objects)filter从objects中按空格为分隔符筛选出符合pattern的字符串filter-out则是筛掉符合的
vpath
vpath <pattern> <directories, space/colon separated>vpath是为指定pattern的文件添加搜索路径,pattern可以是%正则vpath %.h ../headers ../other-directory
比如这个就会在headers里搜索.h文件
.PHONY
.PHONY是伪目标宏,听起来很难懂,其实就是带.PHONY修饰的rule不管target状态如何都会执行
1  | 
  | 
Makefile tutorial