模板
# 下面的变量可以在shell 的环境变量里面指定。
# 也可以象下面这样在 Makefile 里面指定。
# CC=gcc # 编译器
# CFLAGS=-Wall -Werror -g # 编译器参数
# LD=gcc # 连接器参数
# LDFLAGS= $(LIBS) -lpthread # 连接器参数
# DEPENDFLAG=-MM # 生成依赖关系文件的参数
# INCLUDES=-Idir1 -Idir2 # 指明包含外部头文件的目录
# LIBS=-la -lb -lc # 指明引用外部的库文件
CFLAGS:=$(CFLAGS) $(INCLUDES)
LDFLAGS:=$(LDFLAGS) $(LIBS)
#指明项目中,包含源程序的所有的子目录。
SRCDIRS=.
#指明最终生成的可执行文件的名称
PROGRAMS=test.exe
#下面的部分一般不用改动
#从所有子目录中得到源代码的列表
SRCS=$(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c))
#得到源代码对应的目标文件的列表
OBJS=$(SRCS:.c=.o)
#得到源代码对应的依赖关系文件的列表
#依赖关系文件就是一个目标文件依赖于
#哪些头文件和源程序,依赖关系是自动
#生成的,并且用include语句包含在Makefile中
DEPENDS=$(SRCS:.c=.d)
#指明默认目标是生成最终可执行文件。
all: $(PROGRAM)
#生成依赖关系文件
%.d:%.c
$(CC) $(DEPENDFLAG) $(CFLAGS) $< |/
sed "s?//(.*//):?$(basename $<).o $(basename $<).d :?g" /
> $@ || $(RM) $@
$(PROGRAMS): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(filter %.o ,$+)
# 包含入依赖关系文件
include $(DEPENDS)
# 删除生成的中间文件
clean:
rm $(OBJS) $(DEPENDS) $(PROGRAMS)
自动化变量
$@
代表规则中的目标文件名。如果目标是一个文档(Linux中,一般称.a文件为文档),那么它代表这个文档的文件名。在多目标的模式规则中,它代表的是哪个触发规则被执行的目标文件名。
$%
规则的目标文件是一个静态库文件时,代表静态库的一个成员名。例如,规则的目标是“foo.a(bar.o)”,那么,“$%
”的值就为“bar.o
”,“$@
”的值为“foo.a
”。如果目标不是函数库文件,其值为空。
$<
规则的第一个依赖文件名。如果是隐含规则,则它代表通过目标指定的第一个依赖文件。
$?
所有比目标文件更新的依赖文件列表,空格分割。如果目标是静态库文件名,代表的是库成员(.o文件)的更新情况。
$^
规则的所有依赖文件列表,使用空格分隔。如果目标是静态库文件名,它所代表的只能是所有库成员(.o文件)名。一个文件可重复的出现在目标的依赖中,变量“$^
”只记录它的一次引用情况。就是说变量“$^
”会去掉重复的依赖文件。
$+
类似“$^
”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时,库的交叉引用场合。
$(@D)
代表目标文件的目录部分(去掉目录部分的最后一个斜杠)。如果“$@
”是“dir/foo.o
”,那么“$(@D
)”的值为“dir”。如果“$@
”不存在斜杠,其值就是“.”(当前目录)。注意它和函数“dir”的区别!
$(@F)
目标文件的完整文件名中除目录以外的部分(实际文件名)。如果“$@
“为“dir/foo.o
”,那么“$(@F)
”只就是“foo.o
”。“$(@F)”等价于函数“$(notdir $@)
”。
$(%D)
$(%F)
当以如“archive(member)
”形式静态库为目标时,分别表示库文件成员“member
”名中的目录部分和文件名部分。它仅对这种形式的规则目标有效。
$(<D)
$(<F)
分别表示规则中第一个依赖文件的目录部分和文件名部分。
$(^D)
$(^F)
分别表示所有依赖文件的目录部分和文件部分(不存在同一文件)。
$(+D)
$(+F)
分别表示所有依赖文件的目录部分和文件部分(可存在重复文件)。
$(?D)
$(?F)
分别表示被更新的依赖文件的目录部分和文件部分。
MakeFile中给变量赋值有以下两种方式:
1, 递归展开式,使用=直接定义,例子如下:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
执行“make”将会打印出“Huh?
”。整个变量的替换过程时这样的:首先“$(foo)
”被替换为“$(bar)
”,接下来 “$(bar)
”被替换为“$(ugh)
”,最后“$(ugh)
”被替换为“Hug?
”。整个替换的过程是在执行“echo $(foo
”是进行的。
这种方式的缺点是
- 缺点1:使用此风格的变量定义,可能会由于出现变量递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败.
- 缺点2:这种风格的变量定义中如果使用了函数,那么包含在变量值中的函数总会在变量被引用的地方执行(变量被展开时)。
2, 直接展开式
这种风格的变量使用“:=
”来定义变量。在使用“:=
”定义变量时,变量值中对另外变量的引用或者函数的引用在定义时被展开(对变量进行替换)。
x := foo
y := $(x) bar
x := later
就等价于:
y := foo bar
x := later
需要
CFLAGS := $(include_dirs) -O
include_dirs := -Ifoo -Ibar
由于在变量“include_dirs
”的定义出现在“CFLAGS
”定义之后。因此在“CFLAGS
”的定义中,“include_dirs
”的值为空。“CFLAGS
”的值为“-O
”而不是“-Ifoo -Ibar -O
”。注意的是:此风格变量在定义时就完成了对所引用的变量的展开,因此它不能实现对其后定义变量的引用。
变量的替换引用,格式为“$(VAR:A=B)
”(或者“${VAR:A=B}
”),意思是,替换变量“VAR
”中所有“A
”字符结尾的字为“B
”结尾的字。“结尾”的含义是空格之前(变量值多个字之间使用空格分开)。而对于变量其它部分的“A
”字符不进行替换。
般在我们书写Makefile时,各部分变量引用的格式我们建议如下:
1. make变量(Makefile中定义的或者是make的环境变量)的引用使用“$(VAR)
”格式。
2. 出现在规则命令行中shell变量(一般为执行命令过程中的临时变量,它不属于Makefile变量,而是一个shell变量)引用使用shell的“$tmp
”格式。
3. 对出现在命令行中的make变量我们同样使用“$(CMDVAR)
” 格式来引用。
通配符
%
是通配符,%.cc
表示工程里的.cc文件
赋值
=
当它的右边赋值是变量时,这个变量的定义在本条语句之前或之后都可以,即可以递归展开。
:=
它右边赋得值如果是变量,只能使用在这条语句之前定义好的,而不能使用本条语句之后定义的变量,即不可以递归展开。
?=
该符号左边的变量,如果在本条语句之前没有定义过,则执行本语句,如果已经定义,那么本语句什么都不做。
+=
是添加等号后面的值
关键字/命令
include
在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include
,被包含的文件会原模原样的放在当前文件的包含位置
-include
其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。
sinclude
同上