Reputation: 4382
I used below sample code in makefile in MacOS to construct path, but it always not works.
all:
ROOT=$(shell pwd)
@echo $(ROOT)
AGENT:=$(ROOT)/agent
@echo $(AGENT)
@mkdir -p $(AGENT)
It will output as AGENT:=/agent
, obviously, the AGENT
variable is not expanded as ROOT
Upvotes: 2
Views: 3662
Reputation: 29040
Several common mistakes here:
$(shell...)
make function in recipes: recipes are already shell scripts.var=xxx
(no spaces around the =
), only in recipes (well, almost only). They are expanded with $var
or ${var}
but never with $(var)
, still almost only in recipes.var := xxx
or var = xxx
(with or without spaces around :=
or =
), only outside from recipes (well, almost only). They are expanded with $(var)
or ${var}
, never with $var
, anywhere. Note that =
and :=
are different but this is out of scope.If you are a make beginner please ignore the "almost" in the above explanation, you will not need this for now.
A Makefile is written with two different languages: the shell language in recipes and the make language everywhere else. A bit like a web page is written in HTML and in CSS. The shell language and the make language look sometimes similar but they are different. Something important to consider is that make will pass the recipes to the shell... after having expanded them first to substitute make variables or make function calls. It is thus frequently necessary to protect parts of the shell syntax from this make expansion (see below for an example).
Anyway, try something like this, maybe:
ROOT := $(shell pwd)
AGENT := $(ROOT)/agent
all:
@echo $(ROOT)
@echo $(AGENT)
@mkdir -p $(AGENT)
ROOT
and AGENT
are make variables, assigned outside any recipe with :=
and available anywhere, including in recipes. They are expanded with $(var)
. Before passing the recipe to the shell make will expand it as:
echo /some/where
echo /some/where/agent
mkdir -p /some/where/agent
Each line will be executed in a separate shell but it does not matter because there is no shell variables passing between them.
Note: if you are using GNU make, the CURDIR
make variable is defined and expands as the current working directory. No need to use pwd
, just use $(CURDIR)
instead of $(ROOT)
:
AGENT := $(CURDIR)/agent
all:
@echo $(CURDIR)
@echo $(AGENT)
@mkdir -p $(AGENT)
Note: if ROOT
refers to where the Makefile is located and if you invoke make from elsewhere using the -f
make option, $(CURDIR)
does not work any more because it points to where you invoked make from, not where the Makefile is located. But you can also get the correct ROOT
with:
ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
at the very beginning of your Makefile (before any include
statement).
Use only shell syntax and a one-line recipe:
all:
@ROOT="`pwd`"; AGENT="$$ROOT"/agent; echo "$$ROOT"; echo "$$AGENT"; mkdir -p "$$AGENT"
ROOT
and AGENT
are shell variables, assigned with =
inside the recipe and available only in the very same line of the recipe. Note the double $
used to expand shell variables. They are needed to escape the first expansion that make performs before passing the result to the shell. After expansion by make the recipe is passed to the shell as:
ROOT="`pwd`"; AGENT="$ROOT"/agent; echo "$ROOT"; echo "$AGENT"; mkdir -p "$AGENT"
(make expands $$
as $
and stops there) which is what you want. It is executed in one single shell, reason why the shell variables are passed from one command of the list to the next.
If you wish, for better readability, you can use the \
line continuation to split the recipe on several lines, but still have only one single recipe executed in one single shell:
all:
@ROOT="`pwd`"; \
AGENT="$$ROOT/agent"; \
echo "$$ROOT"; \
echo "$$AGENT"; \
mkdir -p "$$AGENT"
This is strictly equivalent to the other form, just a bit easier to read. But it is completely different from:
all:
@ROOT="`pwd`"
@AGENT="$$ROOT/agent"
@echo "$$ROOT"
@echo "$$AGENT"
@mkdir -p "$$AGENT"
because with this last form the 5 lines are executed by 5 different shells. When the second one runs, for example, the ROOT
shell variable assigned by the first shell is not defined any more and the result is the same as:
AGENT="/agent"
Then, the two echo
lines echo nothing at all, just a newline. And finally you get an error because:
mkdir -p
is not valid.
Upvotes: 5