在1.立即变量和延迟变量
在Makefile中最容易让人引困惑的是立即变量和延迟变量。
立即变量和延迟变量是按展开时间来划分的。
立即变量使用 := 操作符进行赋值,在解析阶段就直接展开了,顾名思义,立即展开变量。
延迟变量则是使用 = 操作符进行赋值,在make解析Makefile阶段不会立即展开,而是等到实际使用这个变量时才展开,获得其真正的值。
a = 1
b = 2
val_a := $(a)
val_b = $(b)
a = 10
b = 20
test:
echo $(val_a)
echo $(val_b)
在上面的例子中,val_a是立即变量,当make解析到:=赋值符号时,会把$(a)变量的值立即赋值给val_a,虽然后面a的值发生了变化,但val_a因为已经展开,所以值就不再发生变化。而val_b则不同,因为是延迟展开变量,所以,当make解析到 = 符号时,并没有立即把$(b)的值赋值给val_b,而是在运行echo命令时才对其展开,因为此时b的值已经是20,所以$(val_b)的值也是20
wit@pc:/home/makefile/# makeecho 11echo 2020
立即展开变量一般用在规则中的目标、目标依赖中。make在解析Makefile阶段,需要这些变量有确切的值来构建依赖关系树。
一个项目中的文件依赖关系在程序编译期间是固定不变的,因此需要立即变量在解析阶段就要有明确的值,立即展开。
延迟展开变量一般用在规则的命令行中,这些变量在make编译过程中被引用到才会展开,获得其实际的值。
2.自动变量
在makefile中,大家经常会见到类似$@、$^、$<这种类型的变量。这种变量一般称为自动变量,自动变量是局部变量,作用域范围在当前的规则内,它们分别代表不同的含义:
- $@:目标
- $^:所有目标依赖
- $<:目标依赖列表中的第一个依赖
- $?:所有目标依赖中被修改过的文件
有了这些自动变量,我们就可以改进下面的Makefile:
.PHONY: clean
CC = gcc
BIN = a.out
OBJS = hello.o
OBJS += module.o
$(BIN): $(OBJS)
@echo "start compiling..."
@echo $(OBJS)
$(CC) -o $(BIN) $(OBJS)
@echo "compile done"hello.o: hello.c
$(CC) -c -o hello.o hello.c
module.o: module.c
$(CC) -c -o module.o module.c
clean:
rm -f $(BIN) $(OBJS)
将这个Makefile中命令行中的目标和目标依赖分别使用$@和$^代替,Makefile就变成了下面的样子:
.PHONY: clean
CC = gcc
BIN = a.out
OBJS = hello.o module.o
$(BIN): $(OBJS)
@echo "start compiling..."
@echo $(OBJS)
$(CC) -o $@ $^
@echo "compile done"hello.o: hello.c
$(CC) -c -o $@
$^module.o: module.c
$(CC) -c -o
$@ $^
clean:
rm -f $(BIN) $(OBJS)
除了上面几个常用的自动变量外,还有一些自动变量不太常用,但是大家在以后阅读Makefile时可能会遇到,比如:
- $%:当规则的目标是一个静态库文件时,$%代表静态库的一个成员名
- $+:类似$^,但是保留了依赖文件中重复出现的文件
- $*:在模式匹配和静态模式规则中,代表目标模式中%的部分。比如hello.c,当匹配模式为%.c时,$*表示hello
- $(@D):表示目标文件的目录部分
- $(@F):表示目标文件的文件名部分
- $(*D):在模式匹配中,表示目标模式中%的目录部分
- $(*F):在模式匹配中,表示目标模式中%的文件名部分
- : :告诉make在编译时忽略所有的错误
- @: :告诉make在执行命令前不要显示命令
3.环境变量
除了用户自定义的一些变量,make在解析Makefile中还会引入一些系统环境变量,如编译参数CFLAGS、SHELL、MAKE等。这些变量在make开始运行时被载入到Makefile文件中,因为是全局性的系统环境变量,所以这些变量对所有的Makefile都有效。若Makefile中有用户自定义的同名变量,系统环境变量将会被用户自定义的变量覆盖。若用户在命令行中传递跟系统环境变量同名的变量,系统环境变量也会被传递的同名变量覆盖。
.PHONY:all
CFLAGS = -gall:
@echo "CFLAGS = $(CFLAGS)"
@echo "SHELL = $(SHELL)"
@echo "MAKE = $(MAKE)"
@echo "HOSTNAME = $(HOSTNAME)"
在上面的Makefile中,默认情况下,echo会打印各个系统变量的值:
wit@pc:/home/makefile/demo
# makeCFLAGS = -g
SHELL = /bin/sh
MAKE = make
HOSTNAME =
如果我们在执行make命令时,给Makefile传递一个同名的变量HOSTNAME=zixue.com,系统环境变量就会被这个同名变量覆盖,Makefile实际打印的值就变成了传递的同名变量的值:
wit@pc:/home/makefile/demo# make HOSTNAME=zixue.comCFLAGS = -gSHELL = /bin/shMAKE = makeHOSTNAME = zixue.com
4.变量替换
4.1字符串替换
.PHONY: all
SRC := main.c sub.c
OBJ := $(SRC:.c=.o)all:
@echo "SRC = $(SRC)"
@echo "OBJ = $(OBJ)"
执行make命令,运行结果为:
# makeSRC = main.c sub.cOBJ = main.o sub.o
4.2模式匹配替换
使用匹配符%匹配变量,使用 % 保留变量值中的指定字符串,然后其他部分使用指定字符串代替。
.PHONY: all
SRC := main.c sub.c
OBJ := $(SRC:%.c=%.o)all:
@echo "SRC = $(SRC)"
@echo "OBJ = $(OBJ)"
执行make命令,运行结果为:
# makeSRC = main.c sub.cOBJ = main.o sub.o