makefile
介绍
一个makefile可以看成是由许多命令组成的,每个命令都遵循如下形式
target ... : prerequisites ... |
其中target
是目标,prerequisites
是依赖,command
是执行的命令,例如
hello.o: hello.c |
第一个例子就是单个命令的写法,第二个例子是组合多个命令的写法。虽然有多个命令,但是实际上我们只会运行compile命令,compile命令右边的main.bin会自动在寻找可以生成main.bin的命令,然后再根据main.bin的依赖寻找其生成依赖的命令。
具体工作方式为:
- 输入make后,在当前目录寻找名称为Makefile或makefile的文件
- 如果找到,会将第一个命令作为根命令的左边作为最后的目标文件。
- 解析左边的目标,然后再执行当前命令
规则的书写
规则包含三个部分: 输出文件,规则的依赖文件,生成目标的方法
例如:foo.o: foo.c defs.h
cc -c -g foo.c
这个例子中:
左边是目标文件,也就是说执行这个规则最终会得到foo.o。右边的是这个文件的依赖,也就是下面生成目标文件中所需要的文件。
而下面就是得到这个规则所执行的一条条命令,注意前面是tab键而不是空格。
foo.c和defs.h都是从搜寻当前目录,不会对整个目录进行递归搜索
通配符
make支持三种通配符*
, ?
和~
这和shell中是相同的含义。
*是匹配任意长度的字符,?是匹配0个或一个字符,~表示$HOME目录。
例如clean:
rm -f *.o
# 注意: *同样不会递归搜索
文件搜寻
我们一般不会把代码放在同一目录中,但是前面搜索只会在当前目录下搜索,那么便会出现无法找到依赖而报错。解决这个问题第一种方法是指定目录
foo.o: src/foo.c include/defs.h |
但是这种方法不够灵活,我们还可以使用vpath进行搜索。
格式:
vpath <pattern> <directories>
: 为符合pattern格式的文件设置directories。pattern需要包括%(表示匹配0或若干个字符),如%.h表示所有以h结尾的文件vpath <pattern>
: 清除符合pattern格式的搜索目录vpath
: 清除所有搜索目录
例如:vpath %.c src
vpath %.h include
vpath %.so lib
伪目标
一般来说我们执行完目标之后都会生成目标文件,但是有一些规则我们只是单纯的想执行一些命令,并不想输出文件,这时将该命令称为伪命令。
一个例子便是cleanclean:
rm -f *.o
它便是典型的伪命令,它并不想产生clean文件。但是如果某个规则依赖clean,那么他应该寻找clean文件还是这条规则呢?这时就需要.PHONY
显式指定了。
.PHONY clean |
使用了.PHONY后,clean便成为一条伪命令,只要执行make clean,那么便会一定执行这条指令。而如果依赖clean那也只会需要clean文件而不会执行这条指令。
嵌套执行make
在一些大型工程中,我们可能会写多个makefile,然后通过一个总控makefile进行控制,总控的makefile可以这样写
subsystem: |
总控makefile的变量通过export可以传递到下层变量中,但是不会覆盖下层变量,除非制定了-e
参数。如果要传递所有变量,那么只需要写一个export,那么除了unexport指定的变量外其他变量都会被传递。
variable = value |
变量
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 : 、 # 、 = 或是空字符(空格、回车等)。变量是大小写敏感的。
变量在申明时需要赋初值,然后在引用时使用${VAR}
的方式
例如:objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
=,:=和?=
变量可以由其他变量定义,如
foo = $(bar) |
这种方法变量可以写在后面,他会先获得所有变量再进行解析,但是他的问题是可能导致递归定义:
CFLAGS = $(CFLAGS) -O |
另外可以使用:=
操作符定义变量,这种方法就和平常定义变量一样了,不能使用未定义的变量
而?=
的含义是如果该变量未被定义过,那么将使用右边的值,如果已经被定义过那么将什么也不做。如FOO ?= bar
高级用法
- 变量值的替换:
$(var:a=b)
含义为将所有以a结尾的字符替换成b字符,如bar := $(foo:.o=.c)
- 变量的嵌套:
x = y
y = z
a := $($(x)) - 追加变量值: 如
objects += another.o
在objects变量后追加another.o - 目标变量: 针对某个规则基于特定的目标值。如果我们需要针对某个特定的目标设定一些变量的话可以
<target ...> : <variable-assignment>;
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
即在prog这条命令中CFLAGS就是-g,而全局的CFLAGS可能不是这个条件语句
示例:libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
语法:
<conditional-directive> |
- conditional-directive: 条件关键字,有ifeq, ifneq, ifdef, ifndef
- text-if-true: 条件,一般格式是
(<arg1>, <arg2>)
如果arg1和arg2相同则为真
函数
基本语法: $(<function> <arguments>)
。
- function: 函数名
- arguments: 函数的参数
字符串处理函数
$(subst \
,\ ,\ )
- 功能: 将text中的from字符串替换成to字符串
- 例如:
$(subst ee,EE,feet on the street)
结果为: fEEt on the strEEt$(patsubst \
,\ \,\ )
- 功能:将text中复合pattern模式的字符串替换成replacement。注意pattern可以包含%,表示任意多个字符,replacement中也可以由%表示pattern中%所表示的字符
- 示例:
$(patsubst %.c,%.o,x.c.c bar.c)
结果为x.c.o bar.o$(strip \
)
- 功能: 去掉string开头和结尾的空字符
$(findstring \
,\ )
- 功能: 在in中查找find
- 返回: 如果找到了返回find,没找到返回空
$(filter \
,\ )
- 功能: 保留在text中符合pattern的字符串,pattern可以有多个
- 示例:
sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
cc $(filter %.c %.s,$(sources)) -o foo
结果为foo.c bar.c ugh.h$(filter-out \
,\ )
- 功能: 去除符合pattern的字符串
$(sort \
)
- 功能: 给list中的字符升序排序,并且会去除掉相同的单词
$(word \
,\ )
- 功能: 取text中第n个单词(最小值是1)
- 示例:
$(word 2, foo bar baz)
返回bar$(wordlist \
,\ ,\ )
- 功能: 从text中取ss到e的单词
$(words \
)
- 功能: 统计单词的个数
$(firstword \
)
- 功能: 取首个单词
文件名操作函数
$(dir \
):
- 功能: 取出文件名的目录部分,也就是最后一个/前面的部分,如果没有/则返回./
$(notdir \
):
- 功能: 取出文件名部分,也就是最后一个/后面的部分
$(suffix \
):
- 功能: 取出各个文件名的后缀
$(basename \
)
- 功能: 取出各个文件名的前缀
$(addsuffix \
,\ ):
- 功能: 为文件名添加后缀
$(addprefix \
,\ )
- 功能: 将prefix添加到names的前缀的最前面
- 示例:
$(addprefix src/,foo bar)
结果为 src/foo src/bar$(join \
,\ )
- 功能: 将list2的单词添加到list1对应位置的后面,如果list2比list1多,那么结果会返回多出的list2
- 示例:
$(join aaa bbb , 111 222 333)
结果:aaa111 bbb222 333foreach,if
$(foreach \,\
,\
)
- 功能: 将list中的单词逐一取出放到var中,然后再运行text表达式
- 示例:
names := a b c d
files := $(foreach n,$(names),$(n).o)$(if \
,\ ,\ )
- 功能: 条件语句
call
call可以用来定义自己的函数
$(call \
,\ ,\ ,…,\ )
- 功能: 执行expression表达式,后面的是这个表达式的参数
- 示例:
reverse = $(2) $(1)
foo = $(call reverse,a,b)