2

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?

3
  • [ "CHECK" != "y" ] (a Shell conditional, part of the target recipe) compares literal string "CHECK" with the literal string "y" and will always evaluate true. What is the logic you need to build? Commented Jan 31, 2016 at 15:37
  • Sorry, that was a typing error. I want to build an OR operation so I can check for multiple words in $(var) and have result=true if a match is found. In the example I just check for foo until I get it to work. Commented Jan 31, 2016 at 16:18
  • @PeterSmith the reason that you are having trouble, is that you are trying to do with a makefile recipe, what they are not designed to do. What you want is best done outside of a recipe, or perhaps even without Make at all, just with a shell script. Commented Feb 1, 2016 at 2:07

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Conclusion: I try to do something with a tool I don't understand well enough. I will try and fix that now.
0

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

2 Comments

Your example works fine when X and Y are assigned values on the command line, but when I try to assign them in the target recipe it fails. I have updated the question with an example. Do you know what I'm doing wrong?
Recipe is fully evaluated by the makefile before the control passed to it. You can use $(shell ...) macro to get output of a shell command.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.