0

I am trying to run a make target where a particular value is passed on CLI, but this value should be compared with 2 or more possible values, like the value being passed on cli should be equal to any one from the pre existing list, so that the condition becomes true and if block gets executed!

.PHONY:plan-infra
plan-infra: configure  ## Plan Infra
    @if [ "${infra_module_path}" = "emr" or "gmr" ]; then\
        echo "test";\
    fi


$ make plan-infra -e infra_module_path=emr

So if the variable "infra_module_path" is either "emr" or "gmr" the if block should get executed!

Mohammed Ali
  • 145
  • 7
  • 1
    In any POSIX-compliant shell: `case $infra_module_path in emr|gmr) echo "test";; esac` – Charles Duffy Jan 06 '21 at 17:23
  • Closely related (but for the Make-specificity, and lack of a specific shell spec): [string compare to multiple correct values](https://stackoverflow.com/questions/21157435/bash-string-compare-to-multiple-correct-values) – Charles Duffy Jan 06 '21 at 17:25

4 Answers4

0

The filter GNU make function is your friend:

.PHONY:plan-infra

MATCH := $(filter emr gmr,$(infra_module_path))

plan-infra: configure  ## Plan Infra
    @if [ -n "$(MATCH)" ]; then\
        echo "test";\
    fi

But note that this will also match if you pass values containing several space-separated tokens with at least one matching, e.g.,

make plan-infra -e infra_module_path="emr foo bar"

As noted by MadScientist, filter-out is a bit better because it returns the empty string when the string contains only the filtered tokens; it is thus more accurate:

.PHONY:plan-infra

MATCH := $(filter-out emr gmr,$(infra_module_path))

plan-infra: configure  ## Plan Infra
    @if [ -z "$(MATCH)" ]; then\
        echo "test";\
    fi

But it is still not 100% accurate:

make plan-infra -e infra_module_path="emr qmr"

still matches. If your really need exact match, while it is doable with make functions (at least in your case), as noted by Charles Duffy and MadScientist, it would be better to use shell constructs. Just in case you absolutely need exact match using make functions:

.PHONY:plan-infra

ifeq ($(words $(infra_module_path)),1)
MATCH := $(filter-out emr gmr,$(infra_module_path))
else
MATCH := no
endif

plan-infra: configure  ## Plan Infra
    @if [ -z "$(MATCH)" ]; then\
        echo "test";\
    fi
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • 1
    Actually `filter-out` works better for exact matches: `$(filter-out emr gmr,$(infra_module_path))` will be empty if the string consists of only those two words else it will be non-empty. If you really want to check for one and only one instance of one or the other, it's harder. My recommendation is to use shell constructs, not make constructs, unless there's some specific reason to avoid that. – MadScientist Jan 06 '21 at 17:43
0

You can use the shell OR (-o) condition, or use the case statement to check for one of two possible values. The case statement is a little bit easier to understand.

Using if:

.PHONY:plan-infra
plan-infra: configure  ## Plan Infra
    @if [ "${infra_module_path}" = "emr" -o "${infra_module_path}" = "gmr" ]; then\
        echo "test";\
    fi


$ make plan-infra -e infra_module_path=emr

Using case:

.PHONY:plan-infra
plan-infra: configure  ## Plan Infra
    @case "${infra_module_path"}" in emr | gmr) echo "test";; esac

$ make plan-infra -e infra_module_path=emr
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • `-o` should be avoided (it's considered an obsolete extension to the POSIX standard); use `[ ... ] || [ ... ]` instead of `[ ... -o ... ]`. – chepner Jan 06 '21 at 18:36
0

I might suggest to use make conditionals rather than bash ones. You might want to do:

plan-infra: configure  ## Plan Infr
   ifeq ($(filter-out emr gmr,$(infra_module_path)),)
        @echo "in emr or gmr";
   else
        @echo "not in emr or gmr"
   endif

(notice there are spaces in front of ifeq else and endif instead of tabs -- you don't actually need the spaces but they're useful for readability)

HardcoreHenry
  • 5,909
  • 2
  • 19
  • 44
0

I think, the problem could be thoroughly solved by these means:

  1. using filter, not findsting - that picks exact matches.
  2. get use of words, to filter out (a-priori incorrect) multi-word parameters.
  3. maybe split, if spaces allowed inside (not my case).

This approach allows to compare against two or, maybe more parameter values.

This is the illustration from my project, but with appropriate renaming:

ifeq ($(words ${infra_module_path}),1)
  MATCH:=$(or $(filter emr,${infra_module_path}),$(filter gmr,${infra_module_path}),$(filter qa,${infra_module_path}),$(filter pre-prod,${infra_module_path}))
else
  MATCH:=
endif
$(if ${MATCH},,$(error not recognised infra_module_path))
Jackal
  • 9
  • 2