Sean Walton
Sean Walton

Reputation: 146

How to access make options in Makefile

I want to detect if "-s" is set in the Makefile command line. Is there a variable that captures make options that I can test?

For example, I want to do something like:

Makefile:

if ($(MAKE_OPTIONS), -s)
    #do something
endif

Invocation:

make $(MAKEFILE_OPTIONS) -C some_dir

Upvotes: 0

Views: 2897

Answers (5)

Andrew Johnson
Andrew Johnson

Reputation: 11

This is an (updated June 2023) solution that can test for other single-letter flags too and works on GNU Make 3.81 through 4.4.1 (which broke some of the other approaches here). It won't work on anything older than 3.81 though since it uses $(eval …).

As described in the comment it defines variables for the flags -s, -k, -n and -q named $(make-s), $(make-k), $(make-n) and $(make-q) respectively. The flags -i, -r and -w and maybe others too can be detected by extending the list in the $(foreach ...) function.

makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS))))
define checkflags
  make-$1 := $(findstring $1,$(makeflags))
endef

# Define $(make-s), $(make-k), $(make-n) and $(make-q)
$(foreach flag,s k n q, $(eval $(call checkflags,$(flag))))

# Answer: Checking for -s:
$(if $(make-s), …silent case…, …noisy case…)

My test Makefile is:

makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS))))
define checkflags
  make-$1 := $(findstring $1,$(makeflags))
endef
$(foreach flag,i r w s k n q, $(eval $(call checkflags,$(flag))))

$(info MAKEFLAGS = "$(MAKEFLAGS)")
$(foreach flag,i r w s k n q, \
    $(info make-$(flag) = "$(make-$(flag))" $(if $(make-$(flag)),YES,NO)))

all:
    @+echo 'all: MAKEFLAGS = "$(MAKEFLAGS)"'
    @+echo 'all: $(foreach f,i r w s k n q,$(make-$f))'
    @+echo ''

Upvotes: 0

Michael Henry
Michael Henry

Reputation: 546

It turns out there are even more corner cases for MAKEFLAGS that the below function doesn't quite cover:

find_s = $(findstring s,$(firstword -$(MAKEFLAGS)))$(filter -s,$(MAKEFLAGS))

Consider the Makefile below that displays the value of MAKEFLAGS, both inside and outside of a recipe, and the result of the find_s function:

find_s = $(findstring s,$(firstword -$(MAKEFLAGS)))$(filter -s,$(MAKEFLAGS))

$(info Outside a recipe, MAKEFLAGS="$(MAKEFLAGS)")
$(info $(if $(find_s),-s was detected,-s not found))

all:
    @echo 'Inside a recipe, MAKEFLAGS="$(MAKEFLAGS)"'
    @echo '$(if $(find_s),-s was detected,-s not found)'

This correctly detects the absence or presence of the flag -s, with or without other single-character switches such as -n, e.g.:

$ make-3.81
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS=""
-s not found

$ make-4.1
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS=""
-s not found

$ make-3.81 -s
Outside a recipe, MAKEFLAGS="s"
-s was detected
Inside a recipe, MAKEFLAGS="s"
-s was detected

$ make-4.1 -s
Outside a recipe, MAKEFLAGS="s"
-s was detected
Inside a recipe, MAKEFLAGS="s"
-s was detected

$ make-3.81 -s -n
Outside a recipe, MAKEFLAGS="sn"
-s was detected
echo 'Inside a recipe, MAKEFLAGS="sn"'
echo '-s was detected'

$ make-4.1 -s -n
Outside a recipe, MAKEFLAGS="ns"
-s was detected
echo 'Inside a recipe, MAKEFLAGS="ns"'
echo '-s was detected'

One corner case is when make is invoked with "long" switches (starting with --) in addition to multiple single-character switches. These single-character switches will be grouped together in a single word in MAKEFLAGS in Make 3.81:

$ make-3.81 -n -s --warn-undefined-variables
Outside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn"
-s not found
echo 'Inside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn"'
echo '-s not found'

$ make-4.1 -n -s --warn-undefined-variables
Outside a recipe, MAKEFLAGS="ns --warn-undefined-variables"
-s was detected
echo 'Inside a recipe, MAKEFLAGS="ns --warn-undefined-variables"'
echo '-s was detected'

The grouping of the switches means that the $(filter -s,$(MAKEFLAGS)) expression fails to detect -s with Make 3.81.

This can be fixed by changing find_s as follows:

find_s = $(findstring s,$(filter-out --%,$(MAKEFLAGS)))

Switches starting with -- are filtered out, then the remaining content is scanned for the desired switch (s), yielding correct detection:

$ make-3.81 -n -s --warn-undefined-variables
Outside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn"
-s was detected
echo 'Inside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn"'
echo '-s was detected'

An additional corner case crops up within recipes when variable assignments are passed on the command-line (e.g., make var=value); in this case, the variable MAKEFLAGS will additionally contain those variable assignments. With carefully chosen values, both versions of the detection logic find_s can still be fooled, e.g.:

$ make-3.81 var='fake -s'
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS="var=fake\ -s"
-s was detected

$ make-4.1 var='fake -s'
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS=" -- var=fake\ -s"
-s was detected

The simplest cure for this second problem is to save off a copy of MAKEFLAGS in another variable and use that within recipes, because fortunately outside of recipes, MAKEFLAGS won't contain these variable assignments; otherwise, the more complicated logic below will parse MAKEFLAGS and stop processing after it encounters the words = or -- while filtering out switches starting with --:

empty :=
space := $(empty) $(empty)
# Define variable " " to avoid warnings with --warn-undefined-variables.
$(space) :=

parseFlags = $\
  $(if $(findstring =,$(firstword $1)),$\
       $(empty),$\
       $(if $(firstword $1),$\
            $(if $(filter-out --%,$(firstword $1)),$\
                 $(subst -,$(empty),$(firstword $1)))$\
            $(if $(filter-out --,$(firstword $1)),$\
                 $(call $0,$(wordlist 2,$(words $1),$1)))))

find_s = $(findstring s,$(call parseFlags,$(MAKEFLAGS)))

This new find_s correctly detects -s in all of the below situations:

$ make-3.81 var='fake -s'
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS="var=fake\ -s"
-s not found

$ make-4.1 var='fake -s'
Outside a recipe, MAKEFLAGS=""
-s not found
Inside a recipe, MAKEFLAGS=" -- var=fake\ -s"
-s not found

$ make-3.81 var='fake -s' -ns --warn-undefined-variables
Outside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn"
-s was detected
echo 'Inside a recipe, MAKEFLAGS=" --warn-undefined-variables -sn -- var=fake\ -s"'
echo '-s was detected'

$ make-4.1 var='fake -s' -ns --warn-undefined-variables
Outside a recipe, MAKEFLAGS="ns --warn-undefined-variables"
-s was detected
echo 'Inside a recipe, MAKEFLAGS="ns --warn-undefined-variables -- var=fake\ -s"'
echo '-s was detected'

I'm hopeful that the above techniques work across a broad range of Make versions, but with so many corner cases it's hard to tell; perhaps MadScientist (GNU Make maintainer) can weigh in on this aspect.

Upvotes: 0

MadScientist
MadScientist

Reputation: 100926

Unfortunately it's tricky to find a really portable (across different releases of GNU make) way to do this. Mark's solution gives false positives, if you have set any flag with an "s" in the name (e.g., --warn-undefined-variables).

In GNU make 4.0 and above, the layout of MAKEFLAGS is well-defined and you can use this:

$(findstring s,$(firstword -$(MAKEFLAGS))

to robustly and reliably tell if -s was given. However this won't work reliably in versions of GNU make prior to 4.0. You can use this:

$(findstring s,$(firstword -$(MAKEFLAGS)))$(filter -s,$(MAKEFLAGS))

This expands to the empty string if -s was not given, or a non-empty string if it was. I believe that will work in all versions of GNU make.

Upvotes: 2

Mark Galeck
Mark Galeck

Reputation: 6395

No, filter is not the one. Use findstring:

ifneq ($(findstring s, $(MAKEFLAGS)),)

...

endif

or

$(if $(findstring s, $(MAKEFLAGS)),...)

Upvotes: 0

Beta
Beta

Reputation: 99144

The variable you're looking for is MAKEFLAGS.

ifeq ($(filter s, $(MAKEFLAGS)),s)
# do something
endif

Upvotes: 0

Related Questions