Makefile¶
原始内容来自 rstacruz/cheatsheets 有改动。
变量赋值¶
foo = "bar" # foo 当前值为 bar,允许后面进行会修改
bar = $(foo) foo # Makefile 展开,确定 foo 值之后才能决定最后 bar 的值
dum := $(foo) foo # dum值为 bar foo
foo := "boo" # 此时 foo 值被修改为 boo
foo ?= /usr/local # 如果 foo 没有赋值过,才会进行赋值
bar += world # 追加
foo != echo fooo # 执行shell 命令后foo 值为 fooo
# 因为 foo 最终值确定为 fooo,所以最终 bar 值为fooo foo world。
=
用于创建一个可变的变量,后面可以重新赋值这个变量,最终值是整个 Makefile 最后指定的值。:=
是赋予当前位置的值。?=
是如果该变量没有被赋值,才被赋值为等号后面的值。
上面例子中x,y,z最终值分别为xyz
,xyz bar
, foo bar
。
魔术变量¶
out.o: src.c src.h src.c
$@ # "out.o" (目标对象)
$< # "src.c" (第一个前置依赖)
$^ # "src.c src.h" (所有的前置依赖)
$+ # "src.c src.h src.c" (类似$^,不同于$^地方是它不会去重)
$? # 所有比目标对象新的依赖的集合
$(@D) # "." (目标对象中的目录部分,没有目录部分那么对应是.)
命令前缀¶
前缀 | 描述 |
---|---|
- |
忽略错误 |
@ |
不打印命令输出 |
+ |
忽略错误,但会打印错误信息 |
查找文件¶
js_files := $(wildcard test/*.js) # 匹配所有test目录下的js文件
all_files := $(shell find images -name "*") # 查找images目录下所有文件
函数¶
# 替换相关
file = $(SOURCE:.cpp=.o) # 替换 .cpp 为 .o
outputs = $(files:src/%.coffee=lib/%.js) # 替换 .coffee 为 .js
outputs = $(patsubst %.c, %.o, $(wildcard *.c)) # 替换 *.c 为 *.o
assets = $(patsubst images/%, assets/%, $(wildcard images/*)) # 替换 images/ 为 assets/
# 其他函数
$(strip $(string_var)) # 移除字符串两端的空格
$(filter %.less, $(files)) # 匹配所有以 .less 结尾的文件
$(filter-out %.less, $(files)) # 匹配所有不是以 .less 结尾的文件
$(subst ee,EE,feet on the street) # 替换 ee 为 EE
patsubst <pattern>,<replacement>,<text>
是将 text 中的所有匹配 pattern 的部分替换成 replacement。
多目标规则¶
Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。
其中, -$(subst output,,$@)
中的 $ 表示执行一个Makefile的函数,函数名为subst,后面的为参数。上述规则等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
静态模式¶
静态模式可以更加容易地定义多目标的规则。
-
targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
-
target-pattern是指明了targets的模式,也就是的目标集模式。
-
prereq-patterns是目标的依赖模式,它对target-pattern形成的模式再进行一次依赖目标的定义。
上面的例子中,指明了我们的目标从$object中获取, %.o 表明要所有以 .o 结尾的目标,也就是 foo.o bar.o ,也就是变量 $object 集合的模式,而依赖模式 %.c 则取模式 %.o 的 % ,也就是 foo bar ,并为其加下 .c 的后缀,于是,我们的依赖目标就是 foo.c bar.c 。而命令中的 $< 和 $@ 则是自动化变量, $< 表示第一个依赖文件, $@ 表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价于下面的规则:
再看一个例子:
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
$(filter %.o,$(files))
表示调用Makefile的filter函数,过滤“$files”集,只要其中模式为“%.o”的内容。
隐含规则与模式规则¶
隐含规则¶
这个Makefile中并没有写下如何生成 foo.o 和 bar.o 这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。在上面的那个例子中,make调用的隐含规则是,把 .o 的目标的依赖文件置成 .c ,并使用C的编译命令 cc –c $(CFLAGS) foo.c
来生成 foo.o 的目标。也就是说,它等同于下面的两条规则:
另外对于没有 Makefile 的时候,对于一个 foo.c 文件,我们可以 make foo 时,会自动运行cc foo.c -o foo
。
老式风格的“后缀规则”¶
后缀规则是一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容,GNU make同样兼容于这些东西。后缀规则有两种方式:“双后缀”和“单后缀”。
双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如 .c.o 相当于 %o : %c 。单后缀规则只定义一个后缀,也就是源文件的后缀。如 .c 相当于 % : %.c 。后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名。
模式规则¶
我们可以使用模式规则来定义一个隐含规则,模式规则中,至少在规则的目标定义中要包含 % ,否则,就是一般的规则。目标中的 % 定义表示对文件名的匹配。
条件判断¶
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
自动生成依赖性¶
在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,我们可以借助编译器来生成依赖关系。
cc -M main.c # -M选项输出依赖关系
gcc -MM main.c # -MM 选项输出依赖关系,输出的依赖关系中会去掉标准库中的头文件
cc -MM src/chap8/udpserv01.c -I./src/include # 使用 -I 参数指定自定义头文件位置
包含其他 Makefile¶
make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:
-
如果make执行时,有 -I 或 --include-dir 参数,那么make就会在这个参数所指定的目录下去寻找。
-
接下来按顺序寻找目录
/include (一般是 /usr/local/bin )、 /usr/gnu/include 、 /usr/local/include 、 /usr/include 。
环境变量 .INCLUDE_DIRS 包含当前 make 会寻找的目录列表。你应当避免使用命令行参数 -I 来寻找以上这些默认目录,否则会使得 make “忘掉”所有已经设定的包含目录,包括默认目录。
命令行选项¶
make
-e, --environment-overrides # 允许环境变量覆盖 Makefile 的变量
-B, --always-make # 强制重新编译所有目标
-s, --silent # 不打印任何信息
-j, --jobs=N # 并行编译,N 表示同时编译的线程数
-f, --file=FILE # 指定 Makefile 文件
参考示例¶
CC=cc
CFLAGS+=-Wall -Werror -Wformat=2 -g
LDFLAGS=-I./src/include -L./src/lib
COMPILER_VERSION=$(shell $(CC) --version)
ifneq '' '$(findstring clang, $(COMPILER_VERSION))'
CFLAGS += -Qunused-arguments
endif
lib := src/lib
bin := udpserv01 udpcli01
all: $(bin)
lib_objects :=$(patsubst %.c, %.o, $(wildcard $(lib)/*.c))
$(lib_objects): %.o : %.c
$(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@
.PHONY: udpserv01
udpserv01: src/chap8/udpserv01.c $(lib_objects)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
.PHONY: udpcli01
udpcli01: $(lib_objects) src/chap8/udpcli01.c dg_cli.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@