6

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

cerberos
  • 7,705
  • 5
  • 41
  • 43
  • There are several problems, and misunderstandings. Your `MY_VAR` is a `make` variable, set it without a `` to its value, and no need to `$(eval ...)`. `MY_VAR:=$(shell echo whatever)` is enough. No `` before `$(info ...)` neither, this is `make` – Zelnes Jul 26 '18 at 09:22
  • 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 – Zelnes Jul 26 '18 at 09:31
  • @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 Jul 26 '18 at 09:32
  • Ahh, a different form of if, works! Please make an answer so I can accept. – cerberos Jul 26 '18 at 09:42
  • 1
    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. – MadScientist Jul 26 '18 at 12:20
  • @MadScientist thanks, it doesn't need to be a make var – cerberos Jul 27 '18 at 11:17

4 Answers4

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

Zelnes
  • 403
  • 3
  • 10
  • Specifically, the `ifneq` conditionals happen at a different point to the inline `$(if )` conditionals: in parsing the Makefile, not when executing the rule. – Toby Speight Jul 26 '18 at 10:53
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.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • wow, such a good answer. Although I specified it should be a make var, it actually doesn't need to be. Thanks – cerberos Jul 27 '18 at 11:15
1

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
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
0

A bit left field, but was useful in my case so I guess it's worth a share: pipe the value to xargs with the --no-run-if-empty option:

echo $(POSSIBLY_EMPTY) | xargs --no-run-if-empty echo

Note that this will not work on OSX. See How to ignore xargs commands if stdin input is empty? for more details on xargs --no-run-if-empty

kuzyn
  • 1,905
  • 18
  • 30