9

I am using define to create a macro. However, within a define construct, I am not able to create a variable. Assigning a variable doesn't cause an error, but when I try to use it a bit later, its value is empty.

For example

#######################################################
## 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

You can see from the code above that I'm duplicating the lines

$(call GENERATED_FILE,$(1),$(2),php) and $(call GENERATED_FILE,$(1),$(3),js) from multiple places. So I tried creating two variables as the first two statements in the macro, like so:

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

But if I try to use them later on (still within the define), like $(PHP_OUT_FILE) or $(JS_OUT_FILE), the variables are empty. I'd love to be able to remove that duplication from my macro. Am i doing it wrong? Is it not possible? Is there another way to do it?

TESTING BOTH ANSWERS

I tried eriktous's and Ise Wisteria's approach and they both "worked". Neither one creates private variables, just globals, which I am OK with, I'll just have to be careful with colliding variables and I can't make any recursive calls (which I was not planning on).

The problem with eriktous's approach is the the fact that adding $$ makes it so that the variable is evaluated outside, that is, the $$ is output as a single $ and only gets evaluated when the target is called. Therefore, calling the macro twice will override it.

Ise Wisteria's approach does do what I need though. Calling eval makes sure the variable is declared immediately and it's therefore available and expanded by the macro while the macro is being evaled. It does mean that I couldn't set it to two different values within the macro, but I'm OK with that too.

Here's the makefile I used to test it

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) 

And here's the output: I've ommitted make's echoing of the statement that is to be run to make it easier to see.

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
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Any special reason why you couldn't use the much easier pattern rules to achieve the same? Alright, that doesn't answer that particular question, but I think you want to find a solution to building the stuff ... :) – 0xC0000022L Apr 22 '11 at 00:15
  • 1
    It would take way too long to explain why and it wouldn't help with answering the question. I'm not looking for help in solving the actual problem. Just a way to define local variables within a define :) – Ruan Mendes Apr 22 '11 at 00:22
  • @STATUS_ACCESS_DENIED: Let me try to answer your question, maybe not fully; The idl.json files are in multiple folders and are not copied to the same folder they're in. Each call to the macro must specify the output directory. Please don't ask me why it's this way :) – Ruan Mendes Apr 22 '11 at 15:41
  • 1
    why use inner::, not inner: here? – Baiyan Huang Dec 27 '12 at 05:10

2 Answers2

15

Does applying eval to the assignment like the following solve the problem?

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

Hope this helps

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
5

I have been experimenting with this a bit, and I think I now understand the order of evaluation make follows.
Ise Wisteria has offered one solution to your problem, which I think should work. Another one is to simply add a second $ when referencing the variables, like this: $$(PHP_OUT_FILE).

eriktous
  • 6,569
  • 2
  • 25
  • 35
  • This almost works... If I only call the macro once, see me edit for an explanation. I'm still very thankful for the input. – Ruan Mendes Apr 22 '11 at 15:17