GNU make 中的高级变量继承
Advanced variable inheritance in GNU make
我正在阅读 GNU make 手册,对变量继承机制感到困惑。让我先了解一下基础知识。
我引用手册章节6.10 来自环境的变量:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value.
所以假设我打开一个 shell(称之为 "shell 1")并定义两个变量。然后我启动 make 有两个选项,"op1" 和 "op2"。程序 make 本身读取一个 makefile 并构造第三个变量,称为 "varC"。我们得到如下图的情况:
我继续引用手册中的一句话:
When make runs a recipe, variables defined in the makefile are placed into the environment of each shell.
这就是我现在要做的。执行目标的第一行配方,make 打开一个临时的 shell(称之为 "shell 2")。我假设所有变量 "varA"、"varB" 和 "varC" 都存在于此 shell 中,因此可以由配方行使用。虽然我不是100%确定。
手册继续讲述食谱递归调用make的情况:
By default, only variables that came from the environment or the command line are passed to recursive invocations. You can use the export directive to pass other variables.
下一个配方行是递归 $(MAKE)
调用。顶层 make 打开一个临时的 shell (称之为 "shell 3")到 运行 这个 sub-make实例。因为 varC 没有明确导出,我相信它不存在于 shell 3 中,也不存在于 sub-make 中。我说得对吗?
我 post 编辑这个主题是为了从有经验的 makefile 编写者那里得到澄清。我是这个主题的新手,但我正在尽我最大的努力学习手册并在那之后开始。非常感谢所有帮助:-)
PS:如果您post回答,请说明您的回答是否适用于Linux、Windows或两者。
我会回答 Windows 只是因为我这里没有 Unix 环境。它应该已经很好地展示了它在 GNU make
.
中的工作原理。
首先我假设你所说的环境变量的生命周期与运行shell的生命周期相联系,所以它不是系统环境变量。
在 Windows 上有两个设置变量的程序:SET
和 SETX
。可能有更多的微妙之处,但为了简单起见,SET 将仅为当前 shell 及其子进程设置一个变量,而 SETX
将设置一个系统环境变量。我将只使用 SET
因为我不想处理系统环境变量。
我会给出一个经验性的答案。我已经对此设置进行了测试:
\---level1
| Makefile
|
\---level2
| Makefile
|
\---level3
Makefile
level1 - Makefile
LEVEL = LEVEL1
LEVEL1VAR = VAR1
varB = 12
export varB
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
cd level2 & $(MAKE) foo
level2 - 生成文件
LEVEL = LEVEL2
LEVEL2VAR = VAR2
MKID = MKID2
varC = 13
export varC
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) var level 2 : $(LEVEL2VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
cd level3 & $(MAKE) foo
level3 - 生成文件
LEVEL = LEVEL3
LEVEL3VAR = VAR3
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) var level 2 : $(LEVEL2VAR)
@echo $(LEVEL) var level 3 : $(LEVEL3VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
@echo $(LEVEL) varC is $(varC)
在测试开始时,我在 level1 文件夹中打开一个 shell(Windows 命令提示符)。我创建了一个值为 11 的变量 varA
:
SET varA=11
然后我调用第一个 Makefile,它会调用第二个,它会调用第三个。
make foo
这是输出:
LEVEL1 var level 1 : VAR1
LEVEL1 varA is 11
LEVEL1 varB is 12
cd level2 & make foo
LEVEL2 var level 1 :
LEVEL2 var level 2 : VAR2
LEVEL2 varA is 11
LEVEL2 varB is 12
cd level3 & make foo
LEVEL3 var level 1 :
LEVEL3 var level 2 :
LEVEL3 var level 3 : VAR3
LEVEL3 varA is 11
LEVEL3 varB is 12
LEVEL3 varC is 13
所以我们可以看到:
- 可以从 make
的所有子调用中访问 shell 变量
- 导出的 Makefile 变量可以从该 Makefile 的 make 的所有子调用中访问
- 无法从此 Makefile 的 make 子调用中访问未导出的 Makefile 变量
您可以轻松重现此示例以执行更多测试。
请注意,您实际上可以包含另一个 Makefile 并因此获取其变量和规则,请参阅 GNU make: Include。如果您没有真正密切注意发生的情况,我不建议您使用它,因为如果规则在包含的 Makefile 中与包含的 Makefile 中的名称相同,您可以覆盖规则。
我认为您过度剖析了该段落:
When 'make' runs a command script, variables defined in the makefile
are placed into the environment of that command. This allows you to
pass values to sub-'make' invocations. By default, only variables that came from the environment or the command line are passed to recursive invocations. You can use the 'export' directive to pass other variables.
所有这些都放在一起,因此只有在传入环境或命令行中设置的环境变量,或在 Makefile 中显式 'export' 编辑的环境变量才会放置在调用的命令环境中(该命令是 $(MAKE)
还是其他命令)。
一个有趣的特殊情况是在传入环境和 Makefile 中设置的变量(但未显式导出)。 Makefile 值然后覆盖传入环境值,并且也被导出(因为它在传入环境中,尽管具有不同的值)。
生成文件:
TEST = test
default:
@echo TEST="\"$$TEST\""
结果:
$ make
TEST=""
$ TEST=xx make
TEST="test"
$ make TEST=xx
TEST="xx"
需要注意的一件事是 $(shell) 的规则似乎与作为配方的一部分执行命令时的规则略有不同。
在 Chris Dodd 的 "interesting corner case" 中,使用 $(shell) 时似乎不会传递修改。使用
的 makefile
IN_ENV = hello
$(info $(shell echo IN_ENV is $$IN_ENV))
all:
@echo IN_ENV is $$IN_ENV
如果你运行:
export IN_ENV=goodbye
make
您得到以下输出:
IN_ENV is goodbye
IN_ENV is hello
我正在阅读 GNU make 手册,对变量继承机制感到困惑。让我先了解一下基础知识。
我引用手册章节6.10 来自环境的变量:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value.
所以假设我打开一个 shell(称之为 "shell 1")并定义两个变量。然后我启动 make 有两个选项,"op1" 和 "op2"。程序 make 本身读取一个 makefile 并构造第三个变量,称为 "varC"。我们得到如下图的情况:
我继续引用手册中的一句话:
When make runs a recipe, variables defined in the makefile are placed into the environment of each shell.
这就是我现在要做的。执行目标的第一行配方,make 打开一个临时的 shell(称之为 "shell 2")。我假设所有变量 "varA"、"varB" 和 "varC" 都存在于此 shell 中,因此可以由配方行使用。虽然我不是100%确定。
手册继续讲述食谱递归调用make的情况:
By default, only variables that came from the environment or the command line are passed to recursive invocations. You can use the export directive to pass other variables.
下一个配方行是递归 $(MAKE)
调用。顶层 make 打开一个临时的 shell (称之为 "shell 3")到 运行 这个 sub-make实例。因为 varC 没有明确导出,我相信它不存在于 shell 3 中,也不存在于 sub-make 中。我说得对吗?
我 post 编辑这个主题是为了从有经验的 makefile 编写者那里得到澄清。我是这个主题的新手,但我正在尽我最大的努力学习手册并在那之后开始。非常感谢所有帮助:-)
PS:如果您post回答,请说明您的回答是否适用于Linux、Windows或两者。
我会回答 Windows 只是因为我这里没有 Unix 环境。它应该已经很好地展示了它在 GNU make
.
首先我假设你所说的环境变量的生命周期与运行shell的生命周期相联系,所以它不是系统环境变量。
在 Windows 上有两个设置变量的程序:SET
和 SETX
。可能有更多的微妙之处,但为了简单起见,SET 将仅为当前 shell 及其子进程设置一个变量,而 SETX
将设置一个系统环境变量。我将只使用 SET
因为我不想处理系统环境变量。
我会给出一个经验性的答案。我已经对此设置进行了测试:
\---level1
| Makefile
|
\---level2
| Makefile
|
\---level3
Makefile
level1 - Makefile
LEVEL = LEVEL1
LEVEL1VAR = VAR1
varB = 12
export varB
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
cd level2 & $(MAKE) foo
level2 - 生成文件
LEVEL = LEVEL2
LEVEL2VAR = VAR2
MKID = MKID2
varC = 13
export varC
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) var level 2 : $(LEVEL2VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
cd level3 & $(MAKE) foo
level3 - 生成文件
LEVEL = LEVEL3
LEVEL3VAR = VAR3
.PHONY: foo
foo:
@echo $(LEVEL) var level 1 : $(LEVEL1VAR)
@echo $(LEVEL) var level 2 : $(LEVEL2VAR)
@echo $(LEVEL) var level 3 : $(LEVEL3VAR)
@echo $(LEVEL) varA is $(varA)
@echo $(LEVEL) varB is $(varB)
@echo $(LEVEL) varC is $(varC)
在测试开始时,我在 level1 文件夹中打开一个 shell(Windows 命令提示符)。我创建了一个值为 11 的变量 varA
:
SET varA=11
然后我调用第一个 Makefile,它会调用第二个,它会调用第三个。
make foo
这是输出:
LEVEL1 var level 1 : VAR1
LEVEL1 varA is 11
LEVEL1 varB is 12
cd level2 & make foo
LEVEL2 var level 1 :
LEVEL2 var level 2 : VAR2
LEVEL2 varA is 11
LEVEL2 varB is 12
cd level3 & make foo
LEVEL3 var level 1 :
LEVEL3 var level 2 :
LEVEL3 var level 3 : VAR3
LEVEL3 varA is 11
LEVEL3 varB is 12
LEVEL3 varC is 13
所以我们可以看到:
- 可以从 make 的所有子调用中访问 shell 变量
- 导出的 Makefile 变量可以从该 Makefile 的 make 的所有子调用中访问
- 无法从此 Makefile 的 make 子调用中访问未导出的 Makefile 变量
您可以轻松重现此示例以执行更多测试。
请注意,您实际上可以包含另一个 Makefile 并因此获取其变量和规则,请参阅 GNU make: Include。如果您没有真正密切注意发生的情况,我不建议您使用它,因为如果规则在包含的 Makefile 中与包含的 Makefile 中的名称相同,您可以覆盖规则。
我认为您过度剖析了该段落:
When 'make' runs a command script, variables defined in the makefile are placed into the environment of that command. This allows you to pass values to sub-'make' invocations. By default, only variables that came from the environment or the command line are passed to recursive invocations. You can use the 'export' directive to pass other variables.
所有这些都放在一起,因此只有在传入环境或命令行中设置的环境变量,或在 Makefile 中显式 'export' 编辑的环境变量才会放置在调用的命令环境中(该命令是 $(MAKE)
还是其他命令)。
一个有趣的特殊情况是在传入环境和 Makefile 中设置的变量(但未显式导出)。 Makefile 值然后覆盖传入环境值,并且也被导出(因为它在传入环境中,尽管具有不同的值)。
生成文件:
TEST = test
default:
@echo TEST="\"$$TEST\""
结果:
$ make
TEST=""
$ TEST=xx make
TEST="test"
$ make TEST=xx
TEST="xx"
需要注意的一件事是 $(shell) 的规则似乎与作为配方的一部分执行命令时的规则略有不同。
在 Chris Dodd 的 "interesting corner case" 中,使用 $(shell) 时似乎不会传递修改。使用
的 makefileIN_ENV = hello
$(info $(shell echo IN_ENV is $$IN_ENV))
all:
@echo IN_ENV is $$IN_ENV
如果你运行:
export IN_ENV=goodbye
make
您得到以下输出:
IN_ENV is goodbye
IN_ENV is hello