Peter Smith
Peter Smith

Reputation: 33

Variable in makefile conditional?

Apparently there is no boolean type in GNU Make conditionals so this seemed like the best solution:

$(DEF_TARGET):
    if [ "$(CHECK)" != "y" ]; then \
            var=foo; \
            $(if $(filter foo,$(var)),result=true,result=false); \
    fi

The problem is that no matter if var=foo or var=bar, result will always be false. Replacing $(var) with foo or bar will yeld correct result.

Why will this not work? Are there any better solutions to the problem?

Following makefile is run with the command make -f make.txt

.PHONY: all
all:
     X=aaa; \
     Y=aaa; \
     if [[ '$(filter $(X),$(Y))' ]]; then echo "matched!"; else echo "not matched!"; fi

output:

X=aaa; \
Y=aaa; \
if [[ '' ]]; then echo "matched!"; else echo "not matched!"; fi
not matched!

Why does it fail when X and Y are assigned values in the target recipe?

Upvotes: 2

Views: 15697

Answers (2)

bobbogo
bobbogo

Reputation: 15483

Apparently there is no boolean type in GNU Make conditionals

Clearly there is, else the presence of an $(if ...) macro would not make sense!

The way to read recipes (i.e., the block of shell commands that build the target) is to understand that make stores a recipe as a single recursively expanded variable. The recipe is only expanded when make needs to pass some commands to the shell.

The recipe is expanded once in its entirety. Each line of the resulting expansion is then executed one-by-one. Each execution is run in a new instance of the shell.

So, taking your original makefile, let's assume that you have asked make to build ${DEF_TARGET}. Make expands the recipe:

if [ "$(CHECK)" != "y" ]; then \
        var=foo; \
        $(if $(filter foo,$(var)),result=true,result=false); \
fi
  • ${CHECK} becomes nothing (say)
  • $(if $(filter foo,$(var)),result=true,result=false)
    • First, ${var} is expanded and also becomes empty (say). Note that the line var=foo in the recipe is not interpreted by make! It is a shell command.

    • Next, $(filter foo,) is expanded and also is empty.

    • Next make expands $(if ,result=true,result=false), which produces result=false of course.

      if [ "" != "y" ]; then \
         var=foo; \
         result=false; \
      fi
      

      Make sees this as a just one line due to the back-slashes.

So, do not confuse shell variables and make variables. All the make variables are gone by the time the shell gets its hands on the recipe. Make does not know anything about shell variables.

Your original shell snippet could be something like:

result=$(if $(filter y,${CHECK}),true,false)

(TIMTOWTDI applies). The shell gets result=true or result=false depending on the value of the make variable CHECK.

Upvotes: 7

bobah
bobah

Reputation: 18864

Makefile

.PHONY: all
all:
        @echo "$(filter $(X),$(Y))"

Tests

$ make -f make.txt X='xxx yyy' Y='aaa bbb'

$ make -f make.txt X='xxx yyy' Y='aaa xxx'
xxx
$ make -f make.txt X='bbb yyy' Y='aaa bbb'
bbb

GNU Bash treats non empty strings as true in a boolean context. So a recipe with shell level condition might be:

all:
        if [[ '$(filter $(X),$(Y))' ]]; then echo "matched!"; else echo "not matched!"; fi

Upvotes: 0

Related Questions