132

How do you perform a logical OR using make's ifeq operator?

e.g., I have (simplified):

ifeq ($(GCC_MINOR), 4)
    CFLAGS += -fno-strict-overflow
endif
ifeq ($(GCC_MINOR), 5)
    CFLAGS += -fno-strict-overflow
endif

but would like to consolidate these lines.

(yes, yes, autotools, configure, etc etc; too heavy-handed for the current situation, would like to keep everything within the Makefile here)

[logical opposite of this question: How to Use of Multiple condition in 'ifeq' statement ]

Community
  • 1
  • 1
Pat
  • 1,882
  • 2
  • 15
  • 22
  • Possible duplicate of *[Complex conditions check in Makefile](http://stackoverflow.com/questions/5584872/complex-conditions-check-in-makefile)*. – Peter Mortensen Jun 27 '16 at 11:08
  • 1
    Don't think it's a dupe, the linked question relates to chaining `ifdef` operators, this question is related to chaining `ifeq` operators, similar, but the answers are definitely different. – Pat Jun 27 '16 at 15:43
  • Similar to [Is there a logical `OR` operator for the `ifneq`?](http://stackoverflow.com/q/8296723/471376) – JamesThomasMoon Sep 15 '16 at 19:20

7 Answers7

143

As found on the mailing list archive,

one can use the filter function.

For example

ifeq ($(GCC_MINOR),$(filter $(GCC_MINOR),4 5))

filter X, A B will return those of A,B that are equal to X. Note, while this is not relevant in the above example, this is a XOR operation. I.e. if you instead have something like:

ifeq (4, $(filter 4, $(VAR1) $(VAR2)))

And then do e.g. make VAR1=4 VAR2=4, the filter will return 4 4, which is not equal to 4.

A variation that performs an OR operation instead is:

ifneq (,$(filter $(GCC_MINOR),4 5))

where a negative comparison against an empty string is used instead (filter will return en empty string if GCC_MINOR doesn't match the arguments). Using the VAR1/VAR2 example it would look like this:

ifneq (, $(filter 4, $(VAR1) $(VAR2)))

The downside to those methods is that you have to be sure that these arguments will always be single words. For example, if VAR1 is 4 foo, the filter result is still 4, and the ifneq expression is still true. If VAR1 is 4 5, the filter result is 4 5 and the ifneq expression is true.

One easy alternative is to just put the same operation in both the ifeq and else ifeq branch, e.g. like this:

ifeq ($(GCC_MINOR),4)
    @echo Supported version
else ifeq ($(GCC_MINOR),5)
    @echo Supported version
else
    @echo Unsupported version
endif
sneep
  • 1,828
  • 14
  • 19
misiu_mp
  • 909
  • 1
  • 12
  • 17
  • 8
    A late note: in the two variations you have above, the first will resolve to true if $(GCC_MINOR) is blank, whereas the second will not (thus the second is the better solution generically speaking). – John Nov 25 '13 at 19:59
  • 3
    Actually, it should be `ifneq (,$(filter 4 5,$(GCC_MINOR)))` :) – Tuxdude Jun 05 '14 at 18:48
  • 2
    Perhaps it's better to leave as-is for simplicity? – Jason Sep 12 '14 at 15:46
  • Another note: This doesn't work as a *general* OR statement. We have something where we want to check `if(flagA == TRUE || flagB == true)`, but if you do `$(filter true, $(flagA) $(flagB)` and both are true, you get `ifeq(true, true true)` – Charlie Su Apr 23 '20 at 21:38
35

You can introduce another variable. It doesnt consolidate both checks, but it at least avoids having to put the body in twice:

do_it = 
ifeq ($(GCC_MINOR), 4)
    do_it = yes
endif
ifeq ($(GCC_MINOR), 5)
    do_it = yes
endif
ifdef do_it
    CFLAGS += -fno-strict-overflow
endif
Foo Bah
  • 25,660
  • 5
  • 55
  • 79
  • 7
    The maintainability of this at scale doesn't seem very good to me :/ Then again, once we start talking scale, I guess we're talking autotools – Pat Oct 05 '11 at 04:00
  • 3
    @Pat: I rather prefer this approach where I can separate the assignment in one place in the Makefile, possibly comparing against more values using ifeq/else ifeq/else, rather than using the filter function that could just make awfully long hard to read lines. – jcarballo Mar 21 '14 at 16:01
19

I don't think there's a concise, sensible way to do that, but there are verbose, sensible ways (such as Foo Bah's) and concise, pathological ways, such as

ifneq (,$(findstring $(GCC_MINOR),4-5))
    CFLAGS += -fno-strict-overflow
endif

(which will execute the command provided that the string $(GCC_MINOR) appears inside the string 4-5).

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • 4
    For better or worse (probably worse), just the kind of dirty hack I was looking for; thanks – Pat Oct 05 '11 at 04:01
  • 1
    I don't think this is pathological at all, other than the fact that *everything* in a Makefile is pathological. It's really quite elegant. I've read four or five different ways to accomplish this, and yours is by far the easiest to understand. – Michael Geary Feb 05 '16 at 02:37
10

Here more flexible variant: it uses external shell, but allows to check for arbitrary conditions:

ifeq ($(shell test ".$(GCC_MINOR)" = .4  -o  \
                   ".$(GCC_MINOR)" = .5  -o  \
                   ".$(TODAY)"     = .Friday  &&  printf "true"), true)
    CFLAGS += -fno-strict-overflow
endif
sqr163
  • 1,074
  • 13
  • 24
3

Note that ifeq ($(GCC_MINOR),$(filter $(GCC_MINOR),4 5)) will catch the case where GCC_MINOR is not defined at all.

If you want to catch GCC_MINOR==4 or GCC_MINOR==5 this will do trick:

ifneq ($(filter $(GCC_MINOR),4 5),)
    echo "def"
endif
eerez
  • 41
  • 5
1
ifeq ($(GCC_MINOR), 4)
    CFLAGS += -fno-strict-overflow
endif
ifeq ($(GCC_MINOR), 5)
    CFLAGS += -fno-strict-overflow
endif

Another you can consider using in this case is:

GCC42_OR_LATER = $(shell $(CXX) -v 2>&1 | $(EGREP) -c "^gcc version (4.[2-9]|[5-9])")

# -Wstrict-overflow: http://www.airs.com/blog/archives/120
ifeq ($(GCC42_OR_LATER),1)
  CFLAGS += -Wstrict-overflow
endif

I actually use the same in my code because I don't want to maintain a separate config or Configure.

But you have to use a portable, non-anemic make, like GNU make (gmake), and not Posix's make.

And it does not address the issue of logical AND and OR.

jww
  • 97,681
  • 90
  • 411
  • 885
0

In the case that you are looking to logically "or" several boolean flags together, one practical hack can be to simply let strings concatenate: if the end result is an empty string, then none of the options were true, else non-empty then at least one of them was enabled:

# Use `docker build --pull` in case either `PULL=whatever` is set OR if the `CI` environment variable is present.
ifneq ($(PULL)$(CI),)
PULL_OR_NOT := --pull
endif

build:
    docker build $(PULL_OR_NOT)

conny
  • 9,973
  • 6
  • 38
  • 47