一. Makefile/makefile规则及举例
二. make的工作方式
三、makefile中的常用自动化变量与通配符
四、makefile自动生成依赖性关系
五、 调用命令
1. 显示命令
2. 命令出错
3. 命令执行
六、 变量
1. 变量定义
2. 变量引用
3. 变量替换
七、 条件判断
八、 常用函数
1. 字符串处理函数
2. 文件名操作函数
3. if
4. foreach
5. shell
九、 指定make的目标
更全更多文档请见《跟我一起写Makefile》陈皓78页的pdf。
一. Makefile/makefile规则及举例:
makefile由规则组成,每条规则告诉make两件事: 文件的依赖关系 & 如何生成目标文件。
e.g
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
# 定义变量, 反斜杠(\)是换行符的意思。
edit : $(objects)# targets : prerequisites 冒号前表示目标文件, 冒号后为依赖文件(prerequisites)
cc -o edit $(objects)
# 编译命令, 每个target下都需要配一行command(使用了隐晦规则除外)
main.o : defs.h
# 本来是main.o: main.c defs.h, 根据make 的“隐晦规则”可以省略main.c,只列出其所需的头文件
# 根据make 的“隐晦规则”隐含了cc -c main.c
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
#.PHONY 意思表示 clean 是一个“伪目标”, 我们并不生成“clean”这个文件。 #可以用“make clean”运行“clean”这个目标。
clean :
rm edit $(objects)
二. make的工作方式:
1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系和文件生成时间,决定哪些目标要重新生成。
7、执行生成命令。
三、makefile中的常用自动化变量与通配符:
makefile中支持的3个通配符: *,?,[...]
$@: 目标集
$<: 第一个依赖文件
$^: 所有依赖文件
$?: 所有比目标新的依赖集合
objects = *.o
objects := $(wildcard *.o)
四、自动生成依赖性关系
在Makefile中如果要对每个文件编写依赖关系会非常麻烦, 为了避免这个过程, 我们可以用c/c++编译器的"-M"功能, 即自动寻找源文件中包含的头文件, 并生成依赖关系。
如 cc -M Main.c 的输出是 main.o: main.c defs.h
PS:GNU的编译器(gcc/g++)需要用-MM参数, 如gcc -MM main.c, 不然"-M"会把一些标准库的头文件也引进来。
这部分内容我们就不详细讲了,因为在CoMake2中已经有了更好的集成。
五、 调用命令
1. 显示命令
@echo running...
这在调试整个make流程时很有用, 可以用make -n 或make --just-print之显示命令,但不执行命令。 也就是可以看到我们写的makefile真正执行起来的顺序流程。
2. 命令出错
忽略错误继续执行: make -k 或 make b--keep-going
全局忽略错误: make -i 或 make --ignore-errors
某一句忽略错误: 在前面加一个减号, 如-rm -f *.o
3. 命令执行
exec:
cd subdir; export variable=value;\ %将变量value以"variable"传递到subdir, 如果传递所有变量, 只需一个export就行
$(MAKE);\ % 嵌套执行make;
mkdir subsubdir
PS:make -w 可以查看当前工作目录
六、 变量
1. 变量定义:
大小写敏感
不许含有:,#,=,空格
变量声明时需要赋值,
用=幅值不一定是用已经幅好值的变量,也可以引用后面定义的变量;
用:=幅值, 只能引用已经幅值过的变量进行幅值;
赋值运算符A?=B表示, 若A被定义过,则不做,否则A赋值为B;
A+=B表示,变量幅值时字符串的追加;
2. 变量引用:
$(var)
3. 变量替换:
foo:= a.o b.o c.o
希望赋值bar为a.c, b.c, c.c, 有两种方法:
方法一: bar := $(foo:.o=.c)#将foo中所有以.o结尾的".o"字串全替换为".c"
方法二: bar := $(foo:%.o=%.c)
七、 条件判断
语法:
libs_for_gcc = -lgnu
ifeq($(UNAME),Linux)
LINUX := 1
else ifeq($(UNAME),Darwin)
OSX := 1
endif
类似地, 还有三个关键字: ifneq,ifdef和ifndef
注意,条件表达式中不要放自动化变量, 如$@,因为自动化变量只在运行时才有,而make在读取Makefile时就会根据条件表达式的值选择语句。
八、 常用函数
函数调用方法:
$(<function> <args>)
1. 字符串处理函数:
$(subst <from>,<to>,<text>) 将<text>中的<from>替换成<to>
$(patsubst <fromP>,<toP>,<text>) 将<text>中符合模式<fromP>的部分替换成<toP>
$(strip <text>) 去<text>中开头和结尾的空字符
$(findstring <find>,<from>) 在<from>中找<find>; 找到返回<find>, 否则返回空字符串
$(filter <pattern>,<text>) 从text中过滤出符合pattern模式的项
$(filter-out <pattern>,<text>) 反过滤
$(sort <list>) 排序
2. 文件名操作函数:
$(dir <text>) 取目录
$(suffix <text>) 取后缀名
$(addsuffix <suffix>,<names>) 加后缀
$(addprefix <prefix>,<names>) 加前缀
$(join <list1>,<list2>) list1, list2对应元素进行concatenate
3. if
$(if <condition>, <then>, <else>)
4. foreach
$(foreach <var>,<list>,<text>)
5. shell
执行shell命令
e.g $(shell find . -name '*.$(suffix_name)')
九、 常用指定make的目标功能:
all: 编译所有目标
clean: 删除所有被make创建的文件
install: 安装已编译好的程序, 就是将目标执行文件拷贝到指定目标中去
print: 列出改变了的源文件
tar: 把原程序打包备份为一个tar文件
十 、下面是从我从其他地方找到的资料
每个目标都可以具有与其关联的一系列 shell 命令,这些命令通常用来创建目标。此脚本中的每一条命令都必须以制表符开始。虽然任何目标都能够显示在相关性行上,但除非使用 :: 操作符,否则这些相关性中只有一个能够通过创建脚本来跟随。
如果命令行的第一个或前两个字符是 @ (at 符号)、-(连字符)和 +(加号)这几个符号之一或全部,那么将特别处理该命令,如下:
@ | 使命令在被执行前不被回显。 |
- | 使任何命令行的任何非零退出状态都被忽略。 |
+ | 使命令行可以通过指定 -n、-q 或 -t 选项来执行。 |
所以,简单的说就是:
【make中命令行前面加上减号】
就是,忽略当前此行命令执行时候所遇到的错误。
而如果不忽略,make在执行命令的时候,如果遇到error,会退出执行的,加上减号的目的,是即便此行命令执行中出错,比如删除一个不存在的文件等,那么也不要管,继续执行make。
【make中命令行前面加上at符号@】
就是,在make执行时候,输出的信息中,不要显示此行命令。
而正常情况下,make执行过程中,都是会显示其所执行的任何的命令的。如果你不想要显示某行的命令,那么就在其前面加上@符号即可。
【make中命令行前面加上加号+】
对于命令行前面加上加号+的含义,目前还是不是很清楚。
参考材料: 《跟我一起写Makefile》