输入包装foreach的自定义定义后如何扩展变量?
How to expand variables after entering custom define that wraps foreach?
我在 makefile 中有许多非常相似的 $(foreach..)
循环,它们的工作方式类似于下面的 target_old
目标:
define NL
endef
extras=some/path/
vars=a b c
all: target_old target_new
target_old:
# foreach and some multiple evals inside and some multiple commands inside
$(foreach var, ${vars}, \
$(eval extra := ${extras}${var}) \
@echo var is ${var} and extras is ${extra}$(NL) \
)
# my try to wrap it in a function
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$(NL) \
)
endef
target_new:
@echo this is wrong:
$(call foreach_vars, \
@echo var is ${var} and extras is ${extra} \
)
我有很多多个这样的 foreach
循环,在 foreach
中有所有相同的 eval
。所以我想在我自己的 foreach_vars
函数中用 eval
包装 foreach
循环。所以我不必 $(eval extra := ${extras}${var})
在每个 foreach
调用中。我创建了 target_new
目标来测试它。我希望两个目标的输出相同,make target_old
打印:
$ make target_old
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
但是 target_new
没有从循环内部选择 ${var}
,${var}
只是展开为空:
$ make target_new
this is wrong:
var is and extras is
var is and extras is
var is and extras is
我猜这是因为扩展发生在进入 $(call...)
之前。有什么方法可以用来 "defer" 在 $(call...)
调用中扩展参数,直到在我的函数中 foreach
内?是否可以在 make 中编写自定义的类似 foreach 的宏?是否只有其他方法可用于实现此类功能?
是的,您的问题来自扩展,这些扩展没有按您希望的时间和顺序发生。
您对 make 的使用非常不寻常,因为您在通常很简单的食谱中使用 make 构造(foreach
、eval
、call
...)shell.我猜你有一个很好的理由,但如果你将 make 世界和 shell 世界分开,会不会容易得多?像下面这样,例如:
extras := some/path/
vars := a b c
target_old:
@for var in $(vars); do \
extra=$(extras)$${var}; \
echo var is $${var} and extra is $${extra}; \
done
它使用make变量(vars
、extras
)和shell变量(extra
、var
)。食谱很简单shell。注意 $$
用于逃避 make 的第一次扩展,这样 shell 扩展 ${xxx}
由 shell 完成。另请注意构成单行配方的行延续 (\
),尽管看起来如此。由于 make 配方的每一行都由单独的 shell 执行,因此需要在 shell 脚本的命令之间传递 shell 变量。
如果你愿意,你也可以将 shell for 循环包装在一个 make 递归扩展的变量中:
for = for var in $(vars); do $(1); done
target_new:
@$(call for,extra=$(extras)$${var}; echo var is $${var} and extra is $${extra})
${var}
立即展开,因此需要将其转义为 $${var}
。这本身并不能解决问题,因为现在 </code> 包含文字 <code>${var}
,它不会在 foreach
中展开。我会做一个简单的 subst
来修复它,例如:
$ cat Makefile
define NL
endef
extras=some/path/
vars=a b c
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$(subst $$(var),$(var), \
$(subst $$(extra),$(extra), \
$(1))) \
$(NL) \
)
endef
target_new:
$(call foreach_vars, \
@echo var is $$(var) and extras is $$(extra) \
)
输出:
$ make target_new
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
当 make 开始构建 target_new 时(例如当您键入 make target_new
时):
它扩展了整个配方
重要:配方在启动之前扩展shell
对于生成的扩展的每一行,它一次传递一个到 shell
的新调用
值得展示扩展 make 所做的令人痛苦的细节。我们有食谱:
@echo this is wrong:
$(call foreach_vars, \
@echo var is ${var} and extras is ${extra} \
)
- 首先,
${var}
变为空,${extra}
- make剩下
$(call foreach_vars, @echo var is and extras is )
。现在开始通话:
1
设置为 @echo var is and extras is
- make 展开
$(foreach var, ${vars}, $(eval extra := ${extras}${var}) $(NL) )
${vars}
是 a b c
- 第一次迭代:
var
设置为 a
- 生成 求值
extra := some/path/a
- eval 的扩展是空的,我们只剩下
$(NL)
(模一些空格),留下 @echo var is and extras is
- 第二次迭代:
${extra}
变成some/path/b
,我们又剩下@echo var is and extras is
- 最后一次迭代:
${extra}
变为 some/path/c
,我们再次剩下 @echo var is and extras is
最后的配方:
@echo this is wrong:
@echo var is and extras is
@echo var is and extras is
@echo var is and extras is
产生您描述的输出。
那么如何避免参数过早膨胀呢?
一旦好的解决方案是将您想要的命令行粘贴到变量中,
并改为传递该变量的名称。
define foreach_vars # 1: variable containing command-line
$(foreach var,${vars},
$(eval extra := ${extras}${var}) \
${}$(NL) \
)
endef
cmds<target_new> = @echo var is ${var} and extras is ${extra}
target_new:
@echo this is right:
$(call foreach_vars,cmds<$@>)
为什么要将变量名称与目标名称混淆?查找表很好,您可能会发现许多目标都以相同的配方结束。
cmds<target_new> = @echo var is ${var} and extras is ${extra}
cmds<target_beta> = ${MAKE} ${var}-${extra}
cmds<target_release> = script ${var} | eat ${extra}
target_new target_beta target_release:
$(call foreach_vars,cmds<$@>)
等等
我在 makefile 中有许多非常相似的 $(foreach..)
循环,它们的工作方式类似于下面的 target_old
目标:
define NL
endef
extras=some/path/
vars=a b c
all: target_old target_new
target_old:
# foreach and some multiple evals inside and some multiple commands inside
$(foreach var, ${vars}, \
$(eval extra := ${extras}${var}) \
@echo var is ${var} and extras is ${extra}$(NL) \
)
# my try to wrap it in a function
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$(NL) \
)
endef
target_new:
@echo this is wrong:
$(call foreach_vars, \
@echo var is ${var} and extras is ${extra} \
)
我有很多多个这样的 foreach
循环,在 foreach
中有所有相同的 eval
。所以我想在我自己的 foreach_vars
函数中用 eval
包装 foreach
循环。所以我不必 $(eval extra := ${extras}${var})
在每个 foreach
调用中。我创建了 target_new
目标来测试它。我希望两个目标的输出相同,make target_old
打印:
$ make target_old
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
但是 target_new
没有从循环内部选择 ${var}
,${var}
只是展开为空:
$ make target_new
this is wrong:
var is and extras is
var is and extras is
var is and extras is
我猜这是因为扩展发生在进入 $(call...)
之前。有什么方法可以用来 "defer" 在 $(call...)
调用中扩展参数,直到在我的函数中 foreach
内?是否可以在 make 中编写自定义的类似 foreach 的宏?是否只有其他方法可用于实现此类功能?
是的,您的问题来自扩展,这些扩展没有按您希望的时间和顺序发生。
您对 make 的使用非常不寻常,因为您在通常很简单的食谱中使用 make 构造(foreach
、eval
、call
...)shell.我猜你有一个很好的理由,但如果你将 make 世界和 shell 世界分开,会不会容易得多?像下面这样,例如:
extras := some/path/
vars := a b c
target_old:
@for var in $(vars); do \
extra=$(extras)$${var}; \
echo var is $${var} and extra is $${extra}; \
done
它使用make变量(vars
、extras
)和shell变量(extra
、var
)。食谱很简单shell。注意 $$
用于逃避 make 的第一次扩展,这样 shell 扩展 ${xxx}
由 shell 完成。另请注意构成单行配方的行延续 (\
),尽管看起来如此。由于 make 配方的每一行都由单独的 shell 执行,因此需要在 shell 脚本的命令之间传递 shell 变量。
如果你愿意,你也可以将 shell for 循环包装在一个 make 递归扩展的变量中:
for = for var in $(vars); do $(1); done
target_new:
@$(call for,extra=$(extras)$${var}; echo var is $${var} and extra is $${extra})
${var}
立即展开,因此需要将其转义为 $${var}
。这本身并不能解决问题,因为现在 </code> 包含文字 <code>${var}
,它不会在 foreach
中展开。我会做一个简单的 subst
来修复它,例如:
$ cat Makefile
define NL
endef
extras=some/path/
vars=a b c
define foreach_vars
$(foreach var, ${vars},
$(eval extra := ${extras}${var}) \
$(subst $$(var),$(var), \
$(subst $$(extra),$(extra), \
$(1))) \
$(NL) \
)
endef
target_new:
$(call foreach_vars, \
@echo var is $$(var) and extras is $$(extra) \
)
输出:
$ make target_new
var is a and extras is some/path/a
var is b and extras is some/path/b
var is c and extras is some/path/c
当 make 开始构建 target_new 时(例如当您键入 make target_new
时):
它扩展了整个配方
重要:配方在启动之前扩展shell对于生成的扩展的每一行,它一次传递一个到 shell
的新调用
值得展示扩展 make 所做的令人痛苦的细节。我们有食谱:
@echo this is wrong:
$(call foreach_vars, \
@echo var is ${var} and extras is ${extra} \
)
- 首先,
${var}
变为空,${extra}
- make剩下
$(call foreach_vars, @echo var is and extras is )
。现在开始通话:1
设置为@echo var is and extras is
- make 展开
$(foreach var, ${vars}, $(eval extra := ${extras}${var}) $(NL) )
${vars}
是a b c
- 第一次迭代:
var
设置为a
- 生成 求值
extra := some/path/a
- eval 的扩展是空的,我们只剩下
$(NL)
(模一些空格),留下@echo var is and extras is
- 第二次迭代:
${extra}
变成some/path/b
,我们又剩下@echo var is and extras is
- 最后一次迭代:
${extra}
变为some/path/c
,我们再次剩下@echo var is and extras is
- 第一次迭代:
最后的配方:
@echo this is wrong:
@echo var is and extras is
@echo var is and extras is
@echo var is and extras is
产生您描述的输出。
那么如何避免参数过早膨胀呢? 一旦好的解决方案是将您想要的命令行粘贴到变量中, 并改为传递该变量的名称。
define foreach_vars # 1: variable containing command-line
$(foreach var,${vars},
$(eval extra := ${extras}${var}) \
${}$(NL) \
)
endef
cmds<target_new> = @echo var is ${var} and extras is ${extra}
target_new:
@echo this is right:
$(call foreach_vars,cmds<$@>)
为什么要将变量名称与目标名称混淆?查找表很好,您可能会发现许多目标都以相同的配方结束。
cmds<target_new> = @echo var is ${var} and extras is ${extra}
cmds<target_beta> = ${MAKE} ${var}-${extra}
cmds<target_release> = script ${var} | eat ${extra}
target_new target_beta target_release:
$(call foreach_vars,cmds<$@>)
等等