gnu-make debug - How to print out a variable in makefile




all variables (13)

The problem is that echo works only under an execution block. i.e. anything after "xx:"

So anything above the first execution block is just initialization so no execution command can used.

So create a execution blocl

In my makefile, I have a variable 'NDK_PROJECT_PATH', my question is how can I print it out when it compiles?

I read Make file echo displaying "$PATH" string and I tried:

@echo $(NDK_PROJECT_PATH)
@echo $(value NDK_PROJECT_PATH)

Both gives me

"build-local.mk:102: *** missing separator.  Stop."

Any one knows why it is not working for me?


If you simply want some output, you want to use $(info) by itself. You can do that anywhere in a Makefile, and it will show when that line is evaluated:

$(info VAR="$(VAR)")

Will output VAR="<value of VAR>" whenever make processes that line. This behavior is very position dependent, so you must make sure that the $(info) expansion happens AFTER everything that could modify $(VAR) has already happened!

A more generic option is to create a special rule for printing the value of a variable. Generally speaking, rules are executed after variables are assigned, so this will show you the value that is actually being used. (Though, it is possible for a rule to change a variable.) Good formatting will help clarify what a variable is set to, and the $(flavor) function will tell you what kind of a variable something is. So in this rule:

print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
  • $* expands to the stem that the % pattern matched in the rule.
  • $($*) expands to the value of the variable whose name is given by by $*.
  • The [ and ] clearly delineate the variable expansion. You could also use " and " or similar.
  • $(flavor $*) tells you what kind of variable it is. NOTE: $(flavor) takes a variable name, and not its expansion. So if you say make print-LDFLAGS, you get $(flavor LDFLAGS), which is what you want.
  • $(info text) provides output. Make prints text on its stdout as a side-effect of the expansion. The expansion of $(info) though is empty. You can think of it like @echo, but importantly it doesn't use the shell, so you don't have to worry about shell quoting rules.
  • @true is there just to provide a command for the rule. Without that, make will also output print-blah is up to date. I feel @true makes it more clear that it's meant to be a no-op.

Running it, you get

$ make print-LDFLAGS
LDFLAGS is a recursive variable set to [-L/Users/...]

No need to modify the Makefile.

$ cat printvars.mak
print-%:
        @echo '$*=$($*)'

$ cd /to/Makefile/dir
$ make -f ~/printvars.mak -f Makefile print-VARIABLE


To print the value of a variable you can use:

rule:
<tab>@echo $(VAR_NAME)

When the rule runs the variable will get printed.


You can print out variables as the makefile is read (assuming GNU make as you have tagged this question appropriately) using this method (with a variable named "var"):

$(info $$var is [${var}])

You can add this construct to any recipe to see what make will pass to the shell:

.PHONY: all
all: ; $(info $$var is [${var}])echo Hello world

Now, what happens here is that make stores the entire recipe ($(info $$var is [${var}])echo Hello world) as a single recursively expanded variable. When make decides to run the recipe (for instance when you tell it to build all), it expands the variable, and then passes each resulting line separately to the shell.

So, in painful detail:

  • It expands $(info $$var is [${var}])echo Hello world
  • To do this it first expands $(info $$var is [${var}])
    • $$ becomes literal $
    • ${var} becomes :-) (say)
    • The side effect is that $var is [:-)] appears on standard out
    • The expansion of the $(info...) though is empty
  • Make is left with echo Hello world
    • Make prints echo Hello world on stdout first to let you know what it's going to ask the shell to do
  • The shell prints Hello world on stdout.

Run make -n; it shows you the value of the variable..

Makefile...

all:
        @echo $(NDK_PROJECT_PATH)

Command:

export NDK_PROJECT_PATH=/opt/ndk/project
make -n 

Output:

echo /opt/ndk/project

This can be done in a generic way and can be very useful when debugging a complex makefile. Following the same technique as described in another answer, you can insert the following into any makefile:

# if the first command line argument is "print"
ifeq ($(firstword $(MAKECMDGOALS)),print)

  # take the rest of the arguments as variable names
  VAR_NAMES := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

  # turn them into do-nothing targets
  $(eval $(VAR_NAMES):;@:))

  # then print them
  .PHONY: print
  print:
          @$(foreach var,$(VAR_NAMES),\
            echo '$(var) = $($(var))';)
endif

Then you can just do "make print" to dump the value of any variable:

$ make print CXXFLAGS
CXXFLAGS = -g -Wall

@echo $(NDK_PROJECT_PATH) is the good way to do it. I don't think the error comes from there. Generally this error appears when you mistyped the intendation : I think you have spaces where you should have a tab.


If you don't want to modify the Makefile itself, you can use --eval to add a new target, and then execute the new target, e.g.

make --eval='print-tests: @echo TESTS $(TESTS) ' print-tests

You can insert the required TAB character in the command line using CTRL-V, TAB

example Makefile from above:

all: do-something

TESTS=
TESTS+='a'
TESTS+='b'
TESTS+='c'

do-something:
        @echo "doing something"
        @echo "running tests $(TESTS)"
        @exit 1

All versions of make require that command lines be indented with a TAB (not space) as the first character in the line. If you showed us the entire rule instead of just the two lines in question we could give a clearer answer, but it should be something like:

myTarget: myDependencies
        @echo hi

where the first character in the second line must be TAB.


As 'bobbogo' in the above answer pointed and as per the GNU Make manual, you can use info / warning / error to display text.

$(error   text…)
$(warning text…)
$(info    text…)

To print variables,

$(error   VAR is $(VAR))
$(warning VAR is $(VAR))
$(info    VAR is $(VAR))

when you use 'error' the make execution will stop after showing the error string


Let's assume you have install target, which is a very common in makefiles. If you do not use .PHONY, and a file named install exists in the same directory as the Makefile, then make install will do nothing. This is because Make interprets the rule to mean "execute such-and-such recipe to create the file named install". Since the file is already there, and its dependencies didn't change, nothing will be done.

However if you make the install target PHONY, it will tell the make tool that the target is fictional, and that make should not expect it to create the actual file. Hence it will not check whether the install file exists, meaning: a) its behavior will not be altered if the file does exist and b) extra stat() will not be called.

Generally all targets in your Makefile which do not produce an output file with the same name as the target name should be PHONY. This typically includes all, install, clean, distclean, and so on.







makefile gnu-make