简体   繁体   中英

How to append file path in makefile

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

Several common mistakes here:

  1. Recipe lines run in separate shells, you cannot expect a shell variable assigned in one to be still defined in another.
  2. No need to use the $(shell...) make function in recipes: recipes are already shell scripts.
  3. You apparently mix-up shell variables and make variables.
    • Shell variables are assigned with 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.
    • Make variables are assigned with 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:

With make variables

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).

With shell variables

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM