Makefile test if variable is not empty

11,116

Solution 1

Note that, due to the time of evaluation, make conditionals (ifeq, ifneq...) cannot be used in recipes the way you tried. Use shell conditionals, instead, as shown below.

As your MY_VAR variable is used only in recipes, is target-dependent and you want it to be computed only when needed, why don't you use shell variables, instead of make variables?

$ cat Makefile
.PHONY: a b

a:
    MY_VAR=$$(echo 'whatever') && \
    echo '$@: MY_VAR is $$MY_VAR' && \
    if [ -n "$$MY_VAR" ]; then \
      echo '$@: should be executed'; \
    fi && \
    echo '$@: done'

b:
    MY_VAR=$$(echo '') && \
    echo '$@: MY_VAR is $$MY_VAR' && \
    if [ -n "$$MY_VAR" ]; then \
      echo '$@: should not be executed'; \
    fi && \
    echo '$@: done'

$ make a
a: MY_VAR is whatever
a: should be executed
a: done

$ make b
b: MY_VAR is
b: done

In case you absolutely need MY_VAR to be a target-specific make variable, but want to execute only once (per target) the shell command that produces its value, MadScientist has a wonderful trick that you should probably look at. Applied to your case, it should look like:

$ make --version
GNU Make 4.1
...

$ cat Makefile
a: MY_VAR = $(eval a: MY_VAR := $$(shell echo 'whatever'))$(MY_VAR)
b: MY_VAR = $(eval b: MY_VAR := $$(shell echo ''))$(MY_VAR)

a:
    @echo '$@: MY_VAR is $(MY_VAR)' && \
    if [ -n "$(MY_VAR)" ]; then \
      echo '$@: should be executed'; \
    fi && \
    echo '$@: done'

b:
    @echo '$@: MY_VAR is $(MY_VAR)' && \
    if [ -n "$(MY_VAR)" ]; then \
      echo '$@: should not be executed'; \
    fi && \
    echo '$@: done'

$ make a
a: MY_VAR is whatever
a: should be executed
a: done

$ make b
b: MY_VAR is
b: done

$ make b a
b: MY_VAR is
b: done
a: MY_VAR is whatever
a: should be executed
a: done

It may look extremely strange but it guarantees that MY_VAR is computed if and only if targets a or b are invoked, and only at most once for each. Have a look at MadScientist's post for detailed explanations. Go, it's brilliant.

Solution 2

If you want to dynamically test the content of MY_VAR, you may have to :

a:
    $(eval MY_VAR = $(shell echo ''))
    $(if $(strip $(MY_VAR)),echo ok,echo no)

if evaluation will become echo ok if MY_VAR is not empty, otherwise it will become echo no

Solution 3

The ifeq and family of conditionals are evaluated when parsing the Makefile. If you want a conditional for a Make variable when expanding a rule, you'll want to use the $(if ) function:

.PHONY: a b

a b:
    @$(if $(strip $(MY_VAR)),echo "MY_VAR isn't empty",)
    @echo done

a: MY_VAR =
b: MY_VAR = something
Share:
11,116
cerberos
Author by

cerberos

Updated on June 17, 2022

Comments

  • cerberos
    cerberos almost 2 years

    In a makefile I'm trying to

    • run a shell command and capture the output in a make variable
    • do something if the variable is not empty

    I've created this simplified makefile to demonstrate my problem. Neither make a or make b executes the body of the if, I don't understand why not.

    .PHONY: a b
    
    a:
        $(eval MY_VAR = $(shell echo whatever))
        @echo MY_VAR is $(MY_VAR)
        $(info $(MY_VAR))
    ifneq ($(strip $(MY_VAR)),)
        @echo "should be executed"
    endif
        @echo done
    
    b:
        $(eval MY_VAR = $(shell echo ''))
        @echo MY_VAR is $(MY_VAR)
        $(info $(MY_VAR))
    ifneq ($(strip $(MY_VAR)),)
        @echo "should not be executed"
    endif
        @echo done
    

    I'm using

    $ make --version
    GNU Make 3.81
    

    Edit: as pointed out, the vars don't need to be make vars

    • Zelnes
      Zelnes almost 6 years
      There are several problems, and misunderstandings. Your MY_VAR is a make variable, set it without a <tab> to its value, and no need to $(eval ...). MY_VAR:=$(shell echo whatever) is enough. No <tab> before $(info ...) neither, this is make
    • Zelnes
      Zelnes almost 6 years
      If you want to do something if not empty, you need to use $(if $(strip $(MY_VAR)),do if,do else) in the recipe line
    • cerberos
      cerberos almost 6 years
      @Zelnes I only want to set MY_VAR when target a or b is run (the real commands are expensive and there are other targets that don't need it), if I remove the tabs I get syntax errors.
    • cerberos
      cerberos almost 6 years
      Ahh, a different form of if, works! Please make an answer so I can accept.
    • MadScientist
      MadScientist almost 6 years
      If you only want these variables to be set/used within the a and b targets, why make them make variables? Why not just use shell variables within the recipe? Trying to use make variables here adds a lot of complexity.
    • cerberos
      cerberos almost 6 years
      @MadScientist thanks, it doesn't need to be a make var
  • Toby Speight
    Toby Speight almost 6 years
    Specifically, the ifneq conditionals happen at a different point to the inline $(if ) conditionals: in parsing the Makefile, not when executing the rule.
  • cerberos
    cerberos almost 6 years
    wow, such a good answer. Although I specified it should be a make var, it actually doesn't need to be. Thanks