本文是一些自己学习makefile时的笔记。以下内容主要摘取自陈皓编写的跟我一起写Makefile,这是一个很好的学习资料。
makefile介绍
makefile的核心规则
1
2
target : prerequisites
command
target
是目标文件,prerequisites
是生成target
的依赖文件。当target
不存在,或prerequisites
中如果有一个以上的文件比target
文件要新的话,command
所定义的命令就会被执行,command
命令一定要由一个Tab
键开头。
在makefile末尾一般会看到,用来删除编译产生的中间文件等:
1
2
clean :
rm ...
这里clean
不是一个文件,它只不过是一个动作名字(伪目标),其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个label的名字,如make clean
。
make是如何工作的
make会在当前目录下找名字叫“Makefile”或“makefile”的文件,并根据该文件一层一层地去找文件的依赖关系,直到最终编译出第一个目标文件。详细可参见这里和这里。
书写规则
$@
表示目标的集合,就像一个数组, $@
依次取出目标,并执于命令,可用于多目标。参考这里。
静态模式可以更加容易地定义多目标的规则。其语法为
1
2
<targets ...> : <target-pattern> : <prereq-patterns ...>
<commands>
举例:
1
2
3
4
5
6
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
<command>
其中%.o
即为target-pattern
,%.c
为prereq-patterns
。%.o: %.c
的含义为:将objects
中所有以.o
的目标,后缀换为.c
组成新的集合。
使用变量
变量基础
变量定义
1
VAL = ...
变量使用
1 2 3
$(VAL) 或 ${VAL}
变量会在使用时精确展开。
定义变量值时的
=
与:=
=
允许使用当前尚未定义好的变量作为右值,但可能出现递归定义的问题;:=
强制要求只能使用前面已定义好了的变量作为右值。
变量高级用法
变量替换
1 2 3 4 5 6 7
# 定义变量 foo := a.o b.o # 变量替换 bar := $(foo:.o=.c) # bar = a.c b.c # 或 bar := $(foo:%.o=%.c) # bar = a.c b.c
变量追加
1 2
foo := a.o b.o foo += c.o # foo = a.o b.o c.o
自动化变量
$@
:当前规则中,目标(或目标集)。$^
:当前规则中,所有依赖组成的的集合。$<
:当前规则中,第一个依赖。
举个栗子,对
exc: library.cpp main.cpp
那么,
$@
就指代exc
,$^
指代library.cpp main.cpp
,$<
指代library.cpp
。如果
@$
指代的目标有多个(即是一个目标集)时,则将目标依次取出展开,执行command
,参考这种方式貌似对于多个相互独立的文件分别编译非常方便,尤其当文件非常多时。
举个栗子,对
1 2 3 4 5 6
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
展开后对应执行
1 2 3 4
foo.o : foo.c $(CC) -c $(CFLAGS) foo.c -o foo.o bar.o : bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o
使用条件判断
关键字
ifeq
,ifneq
,判断是否相等。示例:1 2 3 4 5
ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) else $(CC) -o foo $(objects) $(normal_libs) endif
关键字
ifdef
,ifndef
,判断变量是否有值。示例:1 2 3 4 5 6 7 8
foo = ifdef foo frobozz = yes else frobozz = no endif # 该例中frobozz为no
使用函数
函数的调用语法为:
1
2
3
$(<function> <arguments>)
# 或是
${<function> <arguments>}