变量

变量

  • makefile 中支持程序设计语言中的变量的概念
  • makefile 中的变量只代表文本数据(字符串)
  • makefile 中的变量名规则:
    • 变量名可以包含字符,数字,下划线
    • 不能包含 “:”, “#”, “=”, 或 “ “
    • 变量名大小写敏感

所以 makefile 中的变量不需要声明类型;

在 makefile 中使用变量前可以不去定义它,但是这样将会得到一个空值

变量的定义和使用

bianliangshiyong

makefile 中变量的赋值方式

  • 简单赋值(:=)
  • 递归赋值(=)
  • 条件赋值(?=)
  • 追加赋值(+=)

:= 简单赋值

只对当前语句的变量有效

jiandanfuzhi

= 递归赋值

赋值可能影响多个其他变量

所有与目标变量相关的其他变量都将受到影响

diguifuzhi

?= 条件赋值

如果变量未定义,使用赋值符号中的值定义变量

如果变量已经定义,则赋值无效

tiaojianfuzhi.png

+= 追加赋值

原变量值后加上一个新值

原变量值与新值之间由空格隔开

zhuijiafuzhi.png

小结

makefile 中支持变量的定义和使用

makefile 中存在四种变量的赋值方式

  • 简单赋值(:=)
  • 递归赋值(=)
  • 条件赋值(?=)
  • 追加赋值(+=)

预定义变量

  • 自动变量
    • $@,$^,$<
  • 特殊变量
    • $(MAKE), $(MAKECMDGOALS), $(MAKEFILE_LIST)
    • $(MAKE_VERSION),$(CURDIR),$(.VARIABLES)
    • ……

自动变量

  • $@

    • 当前规则中,触发命令被执行的目标(即当前规则中的目标);
  • $^

    • 当前规则(目标)中的所有依赖
  • $<

    • 当前规则中的第一个依赖
1
2
3
4
5
6
7
8
9
10
# 示例
.PHONY: all first second third

all: first second third
@echo "\$$@ => $@" # $@ => all
@echo "$$^ => $^" # $^ => first second third
@echo "$$< => $<" # $< => first
first:
second:
third:

“$” 对于 makefile 有特殊含义,输出 “$” 时需要加上一个 “$” 进行转义;

“$@” 对于 Bash shell 有特殊的含义,输出时需要加上 “\“ 进行转义;

特殊变量

  • $(MAKE)
    • 当前 make 解释器的文件名
  • $(MAKECMDGOALS)
    • 命令行中指定的目标名(make 的命令行参数)
  • $(MAKEFILE_LIST)
    • make 所需要处理的 makefile 文件列表
    • 当前 makefile 的文件名总是位于列表的最后
    • 文件名之间以空格进行分隔
1
2
3
4
5
6
7
# 示例
.PHONY: all first second third

all out: first second third # 执行 make
@echo "$(AMKE)" # make
@echo "$(MAKECMDGOALS)" #
@echo "$(MAKEFILE_LIST)" # makefile (前面有个空格)
  • $(MAKE_VERSION)
    • 当前 make 解释器的版本
  • $(CURDIR)
    • 当前 make 解释器的工作目录
  • $(.VARIABLES)
    • 所有已经定义的变量名列表(预定义变量和自定义变量)
1
2
3
4
5
6
7
# 示例
.PHONY: test1 test2

test1: # 执行 make
@echo "$(AMKE_VERSION)" # 3.81
@echo "$(CURDIR)" # /home/kay
@echo "$(.VSRLABLES)" # (...太多了)

小结

  • maekefile 提供了预定义变量供开发者使用
  • 预定义变量的使用能够使得 makefile 的开发更高效
  • 自动变量是 makefile 中最常见的元素
  • 使用 $(.VSRLABLES) 能够获取所有的特殊变量

变量高级用法

变量值的替换

  • 使用指定字符(串)替换变量值中的后缀字符(串)
  • 语法格式:$(var:a=b) 或 ${var:a=b}
    • 替换表达式中不能有任何的空格
    • make 中支持使用${}对变量进行取值
1
2
3
4
5
6
# 示例
src:= a.cc b.cc c.cc # 可以理解为编译语言中的字符串类型 "a.cc b.cc c.cc"
obj:= $(src:cc=o) # 将src中所有的cc替换为o

test:
@echo "obj => $(obj)" # a.o b.o c.o

变量的模式替换

  • 使用 % 保留变量值中的指定字符,替换其他字符
  • 语法格式:$(var:a%b=x%b) 或 ${var:a%b=x&b}
    • 替换表达式中不能有任何的空格
    • make 中支持使用${}对变量进行取值
1
2
3
4
5
6
# 示例
src:= a1b.c a2b.c a3b.c
obj:= $(src:a%b.c=x%y) # 将src中a替换为x,b替换为y,其余的保持不变

test:
@echo "obj => $(obj)" # x1y x2y x3y

规则中的模式替换

1
2
3
4
5
# 示例
target: target-pattern: prereq-patern
command1
command2
...

作用:通过 target-pattern 从 target 中匹配子目标;再通过 prereq-patern 从子目标生成依赖;进而构成完整的规则。

guizezhongmoshitihuan

此处,%.o 从 OBJS 中匹配以 .o 结尾的文件,这是就先找到了 func.o 这个文件名;%.o : %.c 表示对于每一个 .o 文件他都依赖于一个同名的 .c 文件(即文件名除了扩展名外完全相同,);基于以上规则第一个

func.o : func.c

​ gcc -o $@ -c $^

就被构建出来了,main.o 也是同理。

变量值的嵌套引用

  • 一个变量名之中可以包含对其它变量的引用
  • 嵌套引用的本质是使用一个变量表示另外一个变量

bianliangzhiqtyy

命令行变量

  • 运行 make 时可以在命令行定义变量,命令行中输入的默认覆盖 makefile 中定义的变量
1
2
3
4
5
6
7
# 示例
hm := hello makefile
test:
@echo "hm => $(hm)"

# 命令行输入 make hm=cmd
# 命令行输出 hm => cmd

在实际开发中,可用于零时改变某个变量的值,以得到一个特殊的可执行编译程序(测试程序)

override 关键字

  • 用于指示 makefile 中定义的变量不能被覆盖
  • 变量的定义和赋值都需要使用 override 关键字
1
2
3
4
5
6
# 示例
override var := test
test:
@echo "var => $(var)"
# 命令行输入 make var=cmd
# 命令行输出 hvarm => test

override 关键字仅对后续在 Makefile 中定义的变量生效,如果在 Makefile 中先定义了变量,然后在其后使用 override 关键字尝试覆盖它,那么这个 override 指令将不会有任何效果;

在 Makefile 中,即使一个变量被 override 修饰,它仍然可以在 Makefile 的后续部分进行二次赋值;

override 主要影响的是命令行参数(通过 make VAR=value 传递的)和通过 -e 选项传递给 make 的环境变量,让其不可被覆盖命令行变量覆盖;

define 关键字

  • 用于在 makefile 中定义多行变量
  • 多行变量的定义从变量名开始到 endef 结束
  • 可使用 override 关键字防止变量被覆盖
  • define 定义的变量等价于使用 = 定义的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
# 示例
define foo
I'm fool!
endef

# foo => I'm fool!

override define cmd
@echo "run cmd ls ..."
@ls
endef

# 此时 cmd 的值就是两条命令,当值为命令的时候,前面一定要加[Tab]键

环境变量(全局变量)

也是操作系统中的环境变量

  • makefile 中能够直接使用操作系统中的环境变量
    • 当定义的变量与系统环境变量同名时,环境变量将被覆盖
    • 运行 make 时指定 “-e” 选项,优先使用环境变量

为什么要在 makefile 中使用环境变量?

  • 优势
    • 环境变量可以在所有 makefile 中使用
  • 劣势
    • 过多的依赖于环境变量会导致移植性变差

变量在不同 makefile 之间的传递方式

  • 直接使用外部定义的环境变量(操作系统的环境变量)进行传递
    • 存在操作系统之间移植性的问题
  • 使用 export 定义变量进行传递(定义临时环境变量)
    • 因为变量都是在各自 makefile 文件中进行定义,所以不存在移植性的问题
  • 定义 make 命令行变量进行传递(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 示例1
JAVA_HOME := java home
tset:
@echo "JAVA_HOME => $(JAVA_HOME)"

# make
# JAVA_HOME => java home
# echo $JAVA_HOME 系统环境变量中的 JAVA_HOME
# /home/kay/...
# make -e
# JAVA_HOME => /home/kay/...

# 示例2
# makefile.1
JAVA_HOME := java home
ver := kay
tset:
@echo "JAVA_HOME => $(JAVA_HOME)"
@echo "make another file ..."
@(MAKE) -f makefile.2

# makefile.2
test:
@echo "JAVA_HOME => $(JAVA_HOME)"
@echo "var => $(var)"

# make
# JAVA_HOME => java home
# make another file ...
# 正在进入目录 '/home/xxx'
# JAVA_HOME => java home
# var =>
# 正在离开目录 '/home/xxx'

此处 makefile.2 中输出的 JAVA_HOME 为何还是 makefile.1 中的值呢?

因为 JAVA_HOME 为系统的环境变量,当我们子啊 makefile.1 中改写了 JAVA_HOME 之后,它的生命周期在整个 make 环境中都是有效的;

为什么在 makefile.2 中 var 是无效的呢?

因为 var 是在 makefile.1 中定义的变量,他的生命周期或者说作用域只在 makefile.1 这个文件中有效;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 示例
# makefile.1
JAVA_HOME := java home
export ver := kay
new := hello
tset:
@echo "JAVA_HOME => $(JAVA_HOME)"
@echo "make another file ..."
@(MAKE) -f makefile.2
@(MAKE) -f makefile.2 new:=$(new)

# makefile.2
test:
@echo "JAVA_HOME => $(JAVA_HOME)"
@echo "var => $(var)"
@echo "new => $(new)"

# make
# JAVA_HOME => java home
# make another file ...
# 正在进入目录 '/home/xxx'
# JAVA_HOME => java home
# var => kay
# new => hello
# 正在离开目录 '/home/xxx'

使用 export 定义的变量可以进行传递,这个变量将在该 makefile 及其所有通过它直接或间接调用(如使用 -f 参数指定或通过 include 指令包含)的子 makefile 中可用;

使用命令行也可以将变量进行传递,作用域和 exprot 类似;

目标变量(局部变量)

  • 作用域只在指定目标及连带规则中
    • target : name value
    • target : override name value
1
2
3
4
5
6
7
8
9
10
11
12
# 示例
var := kay.wang # 在当前整个 makefile 中可用
test : var := tset-var # 只在 tset 这个目标及其连带规则中可用

test: another
@echo "test:"
@echo "var => $(var)"
another:
@echo "another:"
@echo "var => $(var)"

# 在 test 和 another 中 tset-var 是可用的

模式变量

  • 模式变量是目标变量的扩展
  • 作用域只在符合模式的目标及连带规则中
    • target : name value
    • target : override name value
1
2
3
4
5
6
7
# 示例
new := kay.wang # 在当前整个 makefile 中可用
%e : override new := test-new # 只在符合 %e 这个模式的目标及其连带规则中可用

rule:
@echo "rule:"
@echo "new => $(new)"

小结

  • 全局变量:makefile 外部定义的环境变量
  • 文件变量:makefile 中定义的变量
  • 局部变量:制定目标的变量