本文参考《跟我一起写 Makefile》编写,并做了一些适合个人习惯的修改,稍加总结而成。
一篇文章肯定不够详细记录makefile所有的知识,所以这篇接着描述makefile中的变量。
在Makefile中的定义的变量,变量可以使用在“目标”,“依赖目标”,“命令”或是Makefile的其它部分中。
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。
一、变量定义
edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
我们可以看到[.o]文件的字符串被重复了两次,为了makefile的易维护,在makefile中我们可以使用变量。
比如,我们声明一个变量objects,在makefile一开始就这样定义:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
于是,就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了:
edit : $(objects) cc -o edit $(objects)
如果有新的 .o 文件加入,我们只需简单地修改一下 objects 变量就可以了。
变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。先看一个例子:
objects = program.o foo.o utils.o program : $(objects) cc -o program $(objects) $(objects) : defs.h
变量会在使用它的地方精确地展开,就像C/C++中的宏一样。
但是变量赋值不止这一种方式,还有另外两种种方式:
OBJS := a.o
OBJS ?= b.o
下面看看=和:=定义变量的区别:
OBJS3 = $(OBJS1) c.o OBJS2 := $(OBJS1) c.o OBJS1 = a.o b.o all: @echo "OBJS1: $(OBJS1)" @echo "OBJS2: $(OBJS2)" @echo "OBJS3: $(OBJS3)"
结果是:
OBJS1: a.o b.o
OBJS2: c.o
OBJS3: a.o b.o c.o
这是因为:= 只能使用前面定义好的变量, = 可以使用后面定义的变量,会把把变量的真实值推到后面来定义。
还有一个比较有用的操作符是“?=”,先看示例:
OBJ ?= bar
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做。
二、变量追加
OBJS1 := a.o b.o OBJS1 += c.o all: @echo "OBJS1: $(OBJS1)" 结果: OBJS1: a.o b.o c.o
三、变量替换
OBJS1 := a.o b.o SRCS1 := $(OBJS1:%.o=%.c) all: @echo "OBJS1: $(OBJS1)" @echo "SRCS1: $(SRCS1)" 结果: OBJS1: a.o b.o SRCS1: a.c b.c
四、变量覆盖
override能够使makefile中定义的变量覆盖make命令中参数指定的变量。
语法:
- override =
- override :=
- override +=
示例:
OBJS1 := a.o b.o override OBJS2 := a.o b.o all: @echo "OBJS1: $(OBJS1)" @echo "OBJS2: $(OBJS2)" 结果: # make all OBJS1=cmd OBJS2=cmd OBJS1: cmd OBJS2: a.o b.o
五、目标变量
作用是使变量的作用域仅限于这个目标(target), 而不像之前例子中定义的变量, 对整个Makefile都有效.
- <target ...> : <variable-assignment>
- <target ...> :: <variable-assignment>
- <target ...> :: override <variable-assignment>
示例:
OBJS := a.o b.o c.o target1: OBJ := d.o target1: @echo "OBJS: " $(OBJS) @echo "OBJ: " $(OBJ) target2: @echo "OBJS: " $(OBJS) @echo "OBJ: " $(OBJ) all: target1 target2 结果: # make all OBJS: a.o b.o c.o OBJ: d.o OBJS: a.o b.o c.o OBJ:
六、多行变量
使用define关键字设置变量的值可以有换行,这有利于定义一系列的命令。
define指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以endef关键字结束。其工作方式和“=”操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令。
define two-lines echo foo echo $(bar) endef
七、环境变量
make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量)。因此,如果我们在环境变量中设置了“CFLAGS”环境变量,那么我们就可以在所有的Makefile中使用这个变量了。这对于我们使用统一的编译参数有比较大的好处。如果Makefile中定义了CFLAGS,那么则会使用Makefile中的这个变量,如果没有定义则使用系统环境变量的值,一个共性和个性的统一,很像“全局变量”和“局部变量”的特性。
当make嵌套调用时,上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义在文件中的变量,如果要向下层Makefile传递,则需要使用exprot关键字来声明。
八、模式变量
在GNU的make中,还支持模式变量(Pattern-specific Variable),通过上面的目标变量中,我们知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种“模式”,可以把变量定义在符合这种模式的所有目标上。
- <pattern ...> : <variable-assignment>;
- <pattern ...> : override <variable-assignment>;
我们知道,make的“模式”一般是至少含有一个“%”的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量:
%.o : CFLAGS = -O