在makefile宏(define)中定义变量

9
我正在使用define创建宏。然而,在define结构内部,我无法创建变量。分配变量不会导致错误,但当我稍后尝试使用它时,其值为空。
例如:
#######################################################
## JIDL_RULE
## Macro to build and install PHP and JS files 
## from idl.json files
#######################################################
## param $(1) Full path to idl.json file
## param $(2) Path to directory to copy PHP file into (relative to where make is run from)
## param $(3) Path to directory to copy JS file into (relative to where make is run from)
##########################################################
define JIDL_RULE
# Create the output directory if it doesn't exist
$(2):
    mkdir -p $(2)

$(3):
    mkdir -p $(3)

# Rule to generate a PHP file. Notice that we have to prepend `pwd`
$(call GENERATED_FILE,$(1),$(2),php): $(1)
    $(PHPHOME)/bin/php -f $(JIDL2PHP) -- `pwd`/$$< $(DATADEF_HOME) > $$@

# Rule to generate a JS file
$(call GENERATED_FILE,$(1),$(3),js): $(1)
    $(PHPHOME)/bin/php -f $(JIDL2JS) -- `pwd`/$$< $(DATADEF_HOME) > $$@

# Add those generated files to the all target
all:: $(call GENERATED_FILE,$(1),$(2),php) $(call GENERATED_FILE,$(1),$(3),js) $(2) $(3)

# Remove generated files on clean:
clean::
    -$(RM) -f $(call GENERATED_FILE,$(1),$(2),php) $(call GENERATED_FILE,$(1),$(3),js)

# Rules to install generated files
$(call PHP_RULE, $(call GENERATED_FILE,$(1),$(2),php))
$(call JS_RULE, $(call GENERATED_FILE,$(1),$(3),js))
endef

你可以从上面的代码中看到,我正在复制多行 $(call GENERATED_FILE,$(1),$(2),php)$(call GENERATED_FILE,$(1),$(3),js) 的内容。因此,我尝试在宏中的前两个语句中创建两个变量,如下所示:
PHP_OUT_FILE := $(call GENERATED_FILE,$(1),$(2),php)
JS_OUT_FILE := $(call GENERATED_FILE,$(1),$(3),js)

但是,如果我稍后尝试使用它们(仍在define内部),例如$(PHP_OUT_FILE)$(JS_OUT_FILE),那么变量为空。我希望能够从我的宏中删除这种重复。我做错了吗?不可能吗?还有其他方法吗?
测试两个答案
我尝试了eriktous和Ise Wisteria的方法,它们都“可行”。它们都没有创建私有变量,只是全局变量,这对我来说没问题,我只需要小心碰撞变量,并且不能进行任何递归调用(我也没有计划)。
eriktous的方法的问题在于添加$$使得该变量在外部被评估,也就是说,$$作为单个$输出,并且仅在调用目标时进行评估。因此,调用宏两次将覆盖它。
Ise Wisteria的方法确实做到了我所需要的。调用eval确保立即声明该变量,并且在宏被evaled时可用并扩展该变量。这意味着我无法在宏内将其设置为两个不同的值,但我也可以接受这一点。
这是我用来测试它的makefile。
define TEST
$(eval INNERVAR := Blah $(1))
inner::
    echo Using EVAL: INNERVAR = $(INNERVAR)
    echo 

endef

define TEST2
INNERVAR2 := Blah $(1)
inner::
    echo Using  double dollar sign: INNERVAR2 = $$(INNERVAR2)
    echo 
endef

$(eval $(call TEST,TEST_1_A))
$(eval $(call TEST,TEST_1_B))
$(eval $(call TEST2,TEST_2_A))
$(eval $(call TEST2,TEST_2_B))

inner::
    echo is that var really private? $(INNERVAR) 
    echo is that var really private? $(INNERVAR2) 

以下是输出结果:我省略了make运行语句的回显,以便更容易查看。

Using EVAL: INNERVAR = Blah TEST_1_A

Using EVAL: INNERVAR = Blah TEST_1_B

# Calling the macro twice overwrites the global variable and since
# it's not expanded immediately, calling the macro with different params
# will output the last value that we set the variable to
Using double dollar sign: INNERVAR2 = Blah TEST_2_B
Using double dollar sign: INNERVAR2 = Blah TEST_2_B

# As expected, the variables are not private to the macro.
is that var really private? Blah TEST_1_B
is that var really private? Blah TEST_2_B

有什么特殊原因你不能使用更简单的模式规则来实现相同的功能吗?好的,这并没有回答那个具体的问题,但我认为你想找到一个构建东西的解决方案... :) - 0xC0000022L
1
解释原因需要花费太长时间,而且对回答问题没有帮助。我不是在寻求解决实际问题的帮助,只是想找到一种在定义中定义局部变量的方法 :) - Ruan Mendes
@STATUS_ACCESS_DENIED:让我试着回答你的问题,可能不是完全的答案;idl.json文件在多个文件夹中,并且不会被复制到它们所在的同一文件夹中。每次调用宏时都必须指定输出目录。请不要问我为什么是这样 :) - Ruan Mendes
1
为什么要使用 inner:: 而不是 inner:? - Baiyan Huang
2个回答

15

eval 应用于如下的赋值语句会解决问题吗?

$(eval PHP_OUT_FILE := $(call GENERATED_FILE,$(1),$(2),php))
$(eval JS_OUT_FILE := $(call GENERATED_FILE,$(1),$(3),js))

希望这可以帮到你


它确实有效,太棒了,谢谢,查看我的编辑以比较eriktous的方法。 - Ruan Mendes
谢谢,很高兴能帮到你 :-) eriktous 的解决方案很有趣,它在稍微不同的情况下也会很有用。 - Ise Wisteria

5

我已经进行了一些实验,现在我认为我理解了make的评估顺序。
Ise Wisteria提供了一个解决方案,我认为应该可以解决你的问题。另一个方法是在引用变量时简单地添加第二个$,例如:$$(PHP_OUT_FILE)


这几乎可以工作...如果我只调用一次宏,详见我的编辑说明。我仍然非常感谢您的输入。 - Ruan Mendes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接