makefile使用总结--变量

发布时间 2023-04-18 10:23:58作者: 时间在哪
本文参考《跟我一起写 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