第六载:makefile变量的高级主题
在makefile中,我们已经知道变量的基本赋值方法、使用等,但是在makefile中,变量还有一些高级的属性,掌握这些属性,可以使编写的makefile更加高效,现在就来了解下makefile中变量的高级属性
一、变量值的替换
- makefile支持使用指定字符(串)替换变量值中的后缀字符(串)
- 语法格式为:$(var:a=b) 或者 ${var:a=b},即使用b替换变量var的后缀a,假如var为cba,那个替换后的结果cbb
注意:替换表达式中不能有任何空格, make中也支持使用${}方式对变量进行取值
.PHONY : all
src := a.cpp b.cpp c.cpp
obj := $(src:.cpp=.o)
all:
@echo "obj => $(obj)"
如上的makefile内容,执行后结果如下(使用.o替换src变量中的.cpp后缀)
/home/delphi>make all
obj => a.o b.o c.o
/home/delphi>
二、变量的模式替换
上边说的变量值的替换,仅仅是使用新的字符串替换变量的指定后缀字符串,而模式替换的替换范围稍微广一些,可以保留变量的指定字符,替换其他字符。
- 使用%保留变量值中的指定字符,替换其他字符(%可以理解为make中的通配符)
- 语法格式:$(var:a%b=x%y)或${var:a%b=x%y},即将var变量的前缀后缀a\b分别用x\y替换,中间部分不变。
.PHONY : all
src := hello.cpp hello.cpp hello.cpp
obj := $(src:h%.cpp=H%.o)
all:
@echo "obj => $(obj)"
上述的makefile,我们H、.o分别替换src变量的h前缀与.cpp后缀,运行结果如下
/home/delphi>make all
obj => Hello.o Hello.o Hello.o
/home/delphi>
三、规则中的模式替换
targets : target-pattern : prereq-pattern
command1
command2
......
意义为:
-通过target-pattern 从targets中匹配子目标;再通过prereq-pattern从子目标生成依赖;进而构成完整的规则。这个稍微不大好理解,下边用一个例子来说明
对于 $(obj) : %.o : %.c,会对变量obj进行逐个处理,首先拿%.o规则去匹配obj变量,得到main.o,main.o作为一个子目标,改行表达式就成为main.o : %.c,再使用%.c生成main.o的依赖,最后衍生成main.o : main.c这样的目标依赖关系,最终得到aaa@qq.com与$^;同理,遍历完obj中的main.o后,会接着遍历fun.o,逻辑与main.o一样,使用规则中的模式替换的方式可以避免对每个.c文件都写一条编译命令,下边来看一个makefile文件
.PHONY : clean rebuild all
target := hello.out
CC := gcc
obj : main.o func.o
#使用模式规则替换,避免每一条.c都要写一个编译命令
$(obj) : %o : %c
$(CC) -o aaa@qq.com -c $^
$(target) : main.o func.o
$(CC) -o aaa@qq.com $^
all : $(target)
rebuild : clean all
clean :
rm -f *.o $(target)
执行结果
/home/delphi/myshare/makefile>make all
gcc -c -o main.o main.c
gcc -c -o func.o func.c
gcc -o hello.out main.o func.o
/home/delphi/myshare/makefile>
从结果看出,规则中的模式替换,可以大大简化我们的工作量,尤其在项目中有成千上百个文件要编译时。
四、变量值的嵌套引用
- 一个变量名之中可以包含对其他变量的引用
- 嵌套引用的本质是使用一个变量表示另一个变量
.PHONY : all
x := y
y := z
val := $($(x))
all:
@echo "val => $(val)"
make all,这个嵌套引用比较简单
/home/delphi>make all
val => z
/home/delphi>
五、命令行变量
- 运行make时,在命令行定义变量
- 命令行变量默认覆盖makefile中定义的变量
- makefile中使用override关键字修饰变量时,命令行变量值不能覆盖makefile中定义的变量
.PHONY : all test
ifeq ($(DEBUG),true)
result := this is debug
else
result := this is release
endif
val_1 := this_is_val_1
override val_2 := this_is_val_2
all:
@echo "result => $(result)"
@echo "val_1 => $(val_1)"
@echo "val_2 => $(val_2)"
执行命令,参数从命令行传入make all DEBUG:=true val_1:=cmd_this_is_val_1 val_2:=cmd_this_is_val_2
/home/delphi>make all DEBUG:=true val_1:=cmd_this_is_val_1 val_2:=cmd_this_is_val_2
result => this is debug
val_1 => cmd_this_is_val_1
val_2 => this_is_val_2
/home/delphi>
参数从命令行传入的好处是,我们可以直接在make参数中决定编译debug版本还是release版本
六、环境变量(全局变量)
- makefile中能够直接使用环境变量的值
a、makefile中定义与环境变量同名的变量,环境变量会被覆盖
b、运行make时指定"- e"选项,优先使用环境变量
注意:所有makefile都可以直接使用环境变量,但是过多使用环境变量,会降低makefile的移植性
- 变量在不同makefile之间的传递方式
a、直接在外部定义环境变量进行传递(不推荐,可移植性差)
b、使用export定义变量进行传递(定义临时环境变量)
c、使用make 命令行变量进行传递(推荐)
为了说明变量的跨文件传输,我们编写两个makefile测试:makefile与makefile1
#makefile 内容
.PHONY : all
QTDIR := MY_QTDIR #QTDIR为环境变量,此处更改为MY_QTDIR,会覆盖原有环境变量,影响makefile1的QTDIR
val1 := this_is_val1
export val2 := this_is_val2 #定义临时环境变量,makefile1中可直接使用
all:
@echo "QTDIR => $(QTDIR)"
@echo "val1 => $(val1)"
@echo "val2 => $(val2)"
@echo "---------make test -f makefile1---------"
@$(MAKE) test -f makefile1 #通过-f 指定make执行makefile1(下同)
@echo "---------make test -f makefile1 end---------"
@echo "---------make test -f makefile1 val1:=$(val1)---------"
@$(MAKE) test -f makefile1 val1:=$(val1) #通过命令行传递变量val1值
@echo "---------make test -f makefile1 val1:=$(val1) end---------"
#makefile1 内容
.PHONY : test
test:
@echo "QTDIR => $(QTDIR)"
@echo "val1 => $(val1)"
@echo "val2 => $(val2)"
make all结果如下,可以看到,在makefile更改的QTDIR环境变量会覆盖原有的系统环境变量;export定义的临时环境变量val2在makefile1中也可直接访问;而普通变量在makefile中通过make参数传递到makefile1中,才能为makefile1所用
/home/delphi>make all
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
---------make test -f makefile1---------
make[1]: 正在进入目录 `/home/delphi'
QTDIR => MY_QTDIR
val1 =>
val2 => this_is_val2
make[1]:正在离开目录 `/home/delphi'
---------make test -f makefile1 end---------
---------make test -f makefile1 val1:=this_is_val1---------
make[1]: 正在进入目录 `/home/delphi'
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
make[1]:正在离开目录 `/home/delphi'
---------make test -f makefile1 val1:=this_is_val1 end---------
/home/delphi>
七、目标变量及模式变量(二者均为局部变量,模式变量时目标变量的扩展)
- 目标变量:作用域只在指定目标及连带规则中
- 模式变量:作用域只在符合模式的目标及连带规则中
说的直白点就类似于C语言中函数内部的局部变量,只不过这里的函数换成的目标
下边直接来看一个例子
.PHONY : all test another
val := this_is_val #val作用域为整个makefile文件(同比C语言源文件中的全局变量)
test : val := this_is_test_val #val作用域为test目标或者连带规则中(同比C语言中函数中的局部变量)
%e : val := this_is_%e_val #val作用域为以e结尾的目标或者连带规则中(同比C语言中函数中的局部变量)
all:
@echo "all:"
@echo "val => $(val)"
test : another
@echo "test:"
@echo "val => $(val)"
another :
@echo "another:"
@echo "val => $(val)"
rule :
@echo "rule:"
@echo "val => $(val)"
/home/delphi>
/home/delphi>make all test rule
all:
val => this_is_val
another:
val => this_is_test_val
test:
val => this_is_test_val
rule:
val => this_is_%e_val
/home/delphi>
从运行结果可以看出,可以给变量限制不同的作用域。
以上7点便是变量的一些高级属性,需要在工作使用过程中慢慢体会。
以上内容参考《狄泰软件学院》操作系统篇之 - makefile专题