I am trying to use canned recipes in my makefile to dynamically define recipes for several subdirectories in my project. However, the variables I have defined are not expanding as I expect them to:
define project
$(1)_dir := $(realpath $(2))
.PHONY: $(1)
$(1):
$(info $(1)_dir = "$($(1)_dir)")
endef
$(eval $(call project,hello_world,./hello-world))
I expect the above makefile to print hello_world_dir = "/path/to/hello-world"
when I run make hello_world
. However, it prints hello_world_dir = ""
instead. I don't understand why the nested expansion is not working properly when the variable name is substituted correctly.
I don't understand why the nested expansion is not working properly when the variable name is substituted correctly.
It's a timing issue. $(call)
is just a fancy way to expand a variable. When you use it to expand variable project
, all variable and function references within are expanded, too. At that point, $(1)
is a reference to a temporary variable provided by call
, so $(1)_dir
is expanded as you expect, everywhere it appears in the value of project
. The assignment to that variable is properly expanded too, but it's just text at that point. The $(info ...)
is also expanded at that time, but no variable hello_world_dir
has yet been defined -- that doesn't happen until the result is $(eval)
ed. The output of the $(info)
properly reflects that at that time, $(hello_world_dir)
expands to nothing.
First of all, do yourself a favor and dump the embedded $(info)
. In addition to timing issues with respect to expansion, it also has content issues: it expands to nothing, leaving your rule for hello_world_dir
with a recipe containing only an empty command. If you want a rule to display text when it runs, then use an echo
command within.
Second, anything you don't want expanded until the rule's recipe runs needs to be protected from expansion by $(call)
. One of the easier ways to do that is to quote $
symbols by doubling them.
Overall, you might end up with this:
define project
$(1)_dir := $(realpath $(2))
.PHONY: $(1)
$(1):
@echo '$(1)_dir = "$$($(1)_dir)"'
endef
$(eval $(call project,hello_world,./hello-world))
Of course, do make sure that ./hello-world
actually designates an existing file or directory.