Reputation: 3024
When I execute this GNU Makefile:
foo: BUILD_DIR = foo_dir
bar: BUILD_DIR = bar_dir
BINARY = $(BUILD_DIR)/my_binary
.PHONY: foo
foo: $(BINARY)
.PHONY: bar
bar: $(BINARY)
$(BINARY):
@echo $(BINARY)
@echo $@
I get:
$ make foo
foo_dir/my_binary # <= this is what I want
/my_binary # <= this is NOT what I want
$ make bar
bar_dir/my_binary # <= this is what I want
/my_binary # <= this is NOT what I want
Instead, I want:
$ make foo
foo_dir/my_binary
foo_dir/my_binary
$ make bar
bar_dir/my_binary
bar_dir/my_binary
How can I do that? I use GNU Make 3.81.
Upvotes: 0
Views: 64
Reputation: 15523
The trick here is understanding how make parses the Makefile. First off, make reads in the Makefile, squirreling away a dependency graph as it goes. It expands some macros, while storing others for later expansion.
foo: $(BINARY)
make expands this dependency line.
Note that at this point make has no concept of building a target,
so target specific variables don't enter the discussion yet.
${BINARY}
is $(BUILD_DIR)/my_binary
,
but ${BUILD_DIR}
is empty.
You have told make that before it builds foo
,
it must build /my_binary
.
$(BINARY): @echo $(BINARY) @echo $@
Now, when make reads this snippet it again expands the target line.
Again,
since you have not given a default value for ${BUILD_DIR}
,
the expansion of ${BINARY}
in the dependency line is /my_binary
.
The recipe
(that block of shell commands)
is stored as a single recursively expanded variable.
It is not expanded at this stage.
The upshot of this is that you have told make how to build /my_binary
,
and that foo
depends on it.
Phase two is make looking at what you asked it to make,
and it checking the dependency graph it now has in memory.
It checks that it knows how to build everything
(this is where the dreaded make: *** No rule to make target 'blah'. Stop.
message may be generated),
and it checks which targets are already up-to-date.
You asked it to build foo
,
so it dutifully decides to build /my_binary
first.
Make takes no notice of the recipes (the shell commands) in this phase.
The final phase is actually executing the required recipes, in the required order. It is at this point that make gets its hands on the required shell commands by expanding the recipe that it squirreled away earlier.
@echo $(BINARY) @echo $@
Since we are building,
target specific variables are in force.
We are building foo
,
so now ${BUILD_DIR}
is foo_dir
.
$@
is /my_binary
.
The expansion of the recipe is
@echo foo_dir/my_binary
@echo /my_binary
Only after this expansion,
make
executes each resulting line,
one by one,
in a separate invocation of the shell.
This expand-the-whole-recipe-before-executing-anything is important to understand!
(${BINARY}
for example could expand into more than one line.)
Sheesh.
You have a problem because make is unexpectedly expanding the non-target-specific version of a variable.
--warn
Provide an assertion:
BUILD_DIR = $(error Ooops, unexpected expansion of $$BUILD_DIR)
There are many ways of doing what you want. I'm a big fan of static pattern rules. These work nicely if your targets and their dependencies can be described by make's noddy pattern matching.
.PHONY: foo bar
foo bar: %: %_dir/my_binary
echo $@
This should be tidied up with a macro. Hopefully you now have a better idea how this is parsed:
targets := foo bar
.PHONY: ${targets}
${targets}: %: %_dir/my_binary
echo $@
Oh, I should mention that $*
in the recipe will expand to whatever the %
matched in the pattern.
This is often helpful (though boringly in this case $*
is the same as $@
).
Upvotes: 1