第二章、变量和模式

1. 变量

同其他编程语言一样,Makefile 支持变量的语法。使用变量可以代替一段字符串,在需要的时候可以使用引用变量的方式来将变量的值(字符串)替换到相应的位置。

使用变量可以让 Makefile 更易于阅读和修改。

在了解变量之前,我们先来了解 make 命令执行的两个阶段。第一阶段读取 Makefile(或makefile)文件,解析其内置变量及值,构造目标和依赖等。第二阶段是根据目标执行规则。

变量的四种定义方法

# 创建立即变量
立即变量名 := 值(立即计算)
# 创建延时变量
延时变量名 = 值(延时计算)
# 如果没有定义变量时使用创建延时变量 
延时变量名 ?= 值(延时计算)
# 在原有变量上追加值
延时变量名或立即变量名 += 

说明:

  1. 变量创建需要在 Makefile 内部书写,不能写在规则中。
  2. 立即变量会在创建变量是直接计算值并交给变量。
  3. 延时变量在创建时不会计算值,只有在使用时才会动态的计算其值(即延时计算)。
  4. ?= 是在没有此变量时,使用该值创建延时变量,如果变量已经存在则不执行赋值操作。
  5. += 是在已有变量的基础上追加值,如果变量是立即变量则立即计算该值,如果变量是延时变量则延时计算该值。

示例:

简化上节课的 Makefile 文件如下:

.PHONY: all clean

all: hello  # 编译最终目标

hello : hello.o
    gcc -o hello hello.o

hello.o : hello.c
    gcc -o hello.o -c hello.c

clean:  # 清除目标文件
    rm hello.o

现在我们依旧可以使用 hello.c 来生成 hello 这个可执行文件。

现在有个需求,就是将生成的可执行程序由 hello 改成 myapp,将目标文件名由 hello.o 改为 temp.o。每次名称的修改我们都要修改多处,并容易出错。如果以后还会有改动,那我们最好使用变量。

我们将 hello 用立即变量 TARGET 代替。hello.o 用立即变量 OBJECT 代替。

示例:

改写后的 Makefile 文件如下:

.PHONY: all clean

# 创建立即变量 TARGET、OBJECT 并立即取值
TARGET := hello
OBJECT := hello.o


all: $(TARGET)  # 编译最终目标

$(TARGET) : $(OBJECT)
    gcc -o $(TARGET) $(OBJECT)

$(OBJECT) : hello.c
    gcc -o $(OBJECT) -c hello.c

clean:  # 清除目标文件
    rm $(OBJECT)

其中 TARGET := helloOBJECT := hello.o 是创建两个立即变量。后续使用 $(TARGET)$(OBJECT) 是变量引用。

变量引用

变量引用 是取其变量的值,将其放入到引用的地方。

变量引用的语法

$(变量名)
# 或
${变量名}

说明:

上述使用变量的 Makefile 和没有使用变量的 Makefile 效果相同。但如果将生成的可执行文件改名为 myapp 则只需要修改变量 TARGET 的值即可,如:TARGET := myapp

立即变量和延迟变量的区别:

  1. 立即变量会在 赋值 :=+= 时直接计算变量值并改变变量,即在 make 执行的第一阶段计算值。
  2. 延时变量会在 赋值 =+=?= 时只赋值表达式,在变量引用时才计算变量值,即在 make 执行的第二阶段计算值。

立即变量示例

# 创建MYVAR1 变量
MYVAR1 := aaa
# 取 MYVAR1的值作为 MYVAR2的值
MYVAR2 := $(MYVAR1)
# 修改MYVAR1变量的值
MYVAR1 := bbb

all: 
    echo "MYVAR1: $(MYVAR1)"
    echo "MYVAR2: $(MYVAR2)"

执行结果如下:

weimingze@mzstudio:~$  make
echo "MYVAR1: bbb"
MYVAR1: bbb
echo "MYVAR2: aaa"
MYVAR2: aaa

延时变量示例

# 创建MYVAR1 变量
MYVAR1 := aaa
# 取 MYVAR1的值作为 MYVAR2的值
MYVAR2 = $(MYVAR1)
# 修改MYVAR1变量的值
MYVAR1 := bbb

all: 
    echo "MYVAR1: $(MYVAR1)"
    echo "MYVAR2: $(MYVAR2)"

执行结果如下:

weimingze@mzstudio:~$  make
echo "MYVAR1: bbb"
MYVAR1: bbb
echo "MYVAR2: bbb"
MYVAR2: bbb

上述两个示例的差别在于 MYVAR2 := $(MYVAR1)MYVAR2 = $(MYVAR1),但从结果上却有所不同。MYVAR2 := $(MYVAR1) 是立即算出 MYVAR2 的值。而 MYVAR2 = $(MYVAR1) 则是在 $(MYVAR2) 变量引用时才计算其值。

实验

尝试创建立即变量和延时变量,再尝试使用 ?=+= 修改变量的值,推断 Makefile 的执行过程。