0

I know about the distinction between the two kinds of variables in GNU Make.

I am currently writing a build system where certain variables are defined in sub directories (e.g., VERSION). To make the life simpler for authors of subdirectories, I do not want to force them to make their variables globally unique. I also would like to avoid recursive make.

My problem is now that I need a way to provide some simple library targets that expand these common variables when the target is defined. As a simple example:

FOO := Bar
PHONY: $(FOO)
$(FOO):
    @echo $(FOO)

FOO := Definitely not Bar

PHONY: test2
test2: Bar
    @echo $(FOO)

I would need the output of make test2 to be

Bar
Definitely not Bar

I could, of course, use a temporary variable to force the expansion of FOO in the first rule, but then I need a way to reliably define a new temporary variable. Is there a way to expand a target, e.g. using eval?

edit: Made the curious expansion of FOO more clear in the example code.

choeger
  • 3,562
  • 20
  • 33

2 Answers2

1

It looks as if you simply want target-specific variables, e.g.

Makefile

.PHONY: Bar test2

Bar: FOO := Bar
Bar:
    @echo $(FOO)

test2: FOO := Definitely not Bar
test2: Bar
    @echo $(FOO)

which runs like:

$ make test2
Bar
Definitely not Bar

(Note that .PHONY, like all special targets, begins with .)

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
0

You can write some nicely functional code (pun intended) using $(eval …). You get to write closures, and decide what gets expanded when.

Consider this variable:

define rule =
PHONY: ${FOO}
${FOO}:
        @echo $$@ ${FOO}
endef

Nothing strange here. But look what happens when we pass it to eval:

FOO := Bar
$(eval ${rule})

We'd better look at that last line in detail.

  • make expands $(eval ${rule})
    • First it expands ${rule}
      • $$ expands to $
      • All occurences of ${FOO} are replaced by Bar
      • leaving us with this text:
        PHONY: Bar
        Bar:
        @echo $@ Bar
        Can you see how the current value of ${FOO} has been baked into the recipe?
    • Now the block of make syntax is passed to $(eval)
      • Make creates the new rule as a side effect of the eval
    • The expansion of the eval though is empty.

Nice.

FOO := Dead
$(eval ${rule})

FOO := Beef
$(eval ${rule})

You aren't very far away from eschewing global variables in favour of local parameters:

$(call makerule,Dead)
$(call makerule,Beef)

Gotta be a good thing, hasn't it?

[AFAICR eval didn't work very well in make 3.81, so use at least version 3.82]

bobbogo
  • 14,989
  • 3
  • 48
  • 57