12

How can I use $(MAKEFLAGS) (or another way of passing variables defined on the command line to sub-make) in a way that supports invocation from shell with both make VAR=val and make -args?

I need my subprojects configurable, but I hate autotools, so I'm using make variables for this, e.g. invoking from shell:

$ make USE_SSE3=1

and USE_SSE3 needs to apply to all builds in all sub-makefiles.

The manual states that:

if you do ‘make -ks’ then MAKEFLAGS gets the value ‘ks’.

Therefore I'm using -$(MAKEFLAGS) (with a dash prefix) in my Makefile.

However, that expands into invalid arguments when variables with no flags are used. If I run:

$ make FOO=bar

then sub-make gets invalid -FOO=bar. OTOH without the dash prefix variable definitions work, then but make -s, etc. don't.

Is there a syntax/variable/hack that makes passing of arguments and lone variable definitions work with sub-makefiles?

The legacy $(MKFLAGS) doesn't have the weird dash prefix problem, but it doesn't include variable definitions either. I've tried fixing the variable with $(patsubst), but that only made things worse by trimming whitespace.

I need the solution to be compatible with the outdated GNU Make 3.81 shipped with Mac OS X Mavericks.


foo:
    $(MAKE) -C subproject -$(MAKEFLAGS)

$ make foo -s       # MAKEFLAGS = 's'
$ make foo BAR=baz  # MAKEFLAGS = 'BAR=baz'
$ make foo -j8      # MAKEFLAGS = ' --job-server=…'
Kornel
  • 97,764
  • 37
  • 219
  • 309
  • This seems to work without any special effort. Try `foo: $(MAKE) bar` and `bar: echo flags are $(MAKEFLAGS), BAR is $(BAR)`. – Beta Oct 27 '13 at 00:59

3 Answers3

21

You shouldn't set MAKEFLAGS at all. Why do you want to? You didn't give any reason to do so.

MAKEFLAGS is intended, really, to be an internal implementation passing arguments from a parent make to a child make. It's not intended, generally, to be modified by a makefile. About the only thing you can usefully do to it is add new flags.

If you just run the recursive make using the $(MAKE) variable rather than hardcoding make, it will Just Work:

recurse:
        @$(MAKE) all FOO=bar

or whatever.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • I don't need to modify `MAKEFLAGS`, I just want them to *work*. Your example loses variables that were passed to top-level make. I need to allow user to set configuration variables and sub-makefiles to react to them. – Kornel Oct 28 '13 at 00:11
  • 3
    No it doesn't. All variables that were passed to the top level ARE passed, as command-line variables, to sub-makes--as long as you don't mess around with MAKEFLAGS. Please show an example that does not work, because people have been successfully using this for 20+ years. – MadScientist Oct 28 '13 at 00:20
  • Oh, I see — flags are passed implicitly to sub-makefiles. I wrongly assumed the variable has to be used explicitly. – Kornel Oct 28 '13 at 20:59
  • 9
    I upvoted this answer, because it's right, but I'm going to add a comment because the answer wasn't clear to me initially. The short version is: Do not use $(MAKEFLAGS) in a rule; if you use $(MAKE) in a rule, make will ensure that $(MAKEFLAGS) gets picked up by the sub-make...magically...and everything Just Works. – Chris Cleeland Mar 07 '14 at 20:59
  • 1
    If you want to pass things explicitly you can also use `export`. – Craig Ringer Jun 26 '17 at 07:03
  • The "[MAKEFLAGS] it's not intended ... to be modified by a makefile" part needs to be *heavily* qualified, particularly given how unclear the Make manual is in this regard. See https://stackoverflow.com/a/31789730/1235165 – hmijail Dec 29 '22 at 04:26
  • I'm not sure what kind of "heavy qualification" you had in mind. I do say _about the only thing you can usefully do to it is add new flags_. – MadScientist Dec 29 '22 at 15:12
4

Years too late I got your answer if I got it right. You can construct $(MAKEARGS) manually yourself like:

MAKEARGS := $(strip \
  $(foreach v,$(.VARIABLES),\
    $(if $(filter command\ line,$(origin $(v))),\
      $(v)=$(value $(v)) ,)))

MAKEARGS := assign static

strip cleans leading and trailing whitespaces.

foreach v iterate over all variable names.

origin $(v) check if variable origin is "command line".

$(v)=$(value $(v)) output env assignment string.

Alternatively you can unpick the $(MAKEFLAGS) like:

MAKEARGS := $(wordlist 2,$(words $(MAKEFLAGS)),$(MAKEFLAGS))
MAKEFLAGS := $(firstword $(MAKEFLAGS))

Which can leave you with cleaner code for further recursions IMHO. I say this because I sometimes need to keep apart arguments and flags in certain cases. Especially as you get caught in debugging a recursion djungle. But for any specific case one should consult the manual about recursive options processing.

Changing the $(MAKEFLAGS) can lead to unwanted malfunction.

Another useful information for the willing user could be that the $(MAKEFLAGS) variable is basically the whole argument list passed to make, not only the flag characters. So $(info MAKEFLAGS = $(MAKEFLAGS)) can give you something like:

MAKEFLAGS = rRw -- VAR=val

Cheers

domson
  • 205
  • 2
  • 8
1

To check if -B is present in make flags i do :

BB_CLOBBER := $(shell echo $(MAKEFLAGS) | grep wB)
ifeq (,$(BB_CLOBBER))
  # also force clobber make if these files are missing
  BB_CLOBBER := $(shell (test -e $(bb_gen)/minimal/.config && test -e $(bb_gen)/full/.config) || echo "B")
endif

bb_prepare:
ifneq (,$(BB_CLOBBER))
    @rm -rf $(bb_gen)/full
    ...
Tanguy
  • 2,227
  • 1
  • 18
  • 8