Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile test if variable is not empty

Tags:

makefile

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

like image 888
cerberos Avatar asked Jul 26 '18 09:07

cerberos


Video Answer


2 Answers

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

like image 155
Zelnes Avatar answered Nov 05 '22 12:11

Zelnes


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.

like image 25
Renaud Pacalet Avatar answered Nov 05 '22 13:11

Renaud Pacalet