4

Given a Makefile:

all: build/a build/b build/c    # need to change this to    all: build/*

build/a:
   ...

build/b:
   ...

build/c:
   ...

I would like to change the all target to automatically build all targets that match build/*. This question seems to be very similar, except that it prints the result rather than acts on it. Also, I am not sure if that answer will work on both Linux & Mac.

Yuri Astrakhan
  • 8,808
  • 6
  • 63
  • 97
  • Does `make $(grep -o ^build/[^:]* Makefile)` do what you want? – Matt Mar 26 '19 at 04:53
  • that would feel very hacky and could break at any moment, including a potential infinite loop... i would hope for a more "proper" solution :) – Yuri Astrakhan Mar 26 '19 at 07:57
  • Not sure how that could lead into the infinite loop (cf. Make is safe against circular dependencies by design), but, as you can see from that question cited, any solution would consist of some sort of regex match/replace thing. So it cannot be perfect, unless you rewrite the Makefile itself. – Matt Mar 26 '19 at 09:19
  • Can you rely on GNUmake or does it need to be a portable solution? – Vroomfondel Mar 26 '19 at 09:51
  • sadly yes, it should be portable :( – Yuri Astrakhan Mar 27 '19 at 14:39
  • Note that macOS uses GNU `make` too, but, unfortunately, an older version (v3.81 as of macOS 10.14.4 vs. v4.1 on Ubuntu 18.04, for instance). – mklement0 Mar 29 '19 at 09:19

1 Answers1

1

Make has no such functionality built-in. And, in fact, keeping the list of the targets up-to-date manually is much cleaner and easier than any alternative solution. Personally, I'd probably start with something like this:

# second expansion is needed to get the value of
# $(targets) after the whole file was preprocessed
.SECONDEXPANSION:
all: $$(targets)

targets += build/a
build/a:
    ...
targets += build/b
build/b:
    ...
targets += build/c
build/c:
    ...

I don't believe that the requirement to add a single line per target (targets+=xxx) could be so annoying for you.

However, this is how we can "preprocess" Makefile ourselves:

# assume all targets are explicitly defined in Makefile
targets != grep -o '^ *build/\w* *:' Makefile | sed 's/^ *//;s/ *:$$//'
all: $(targets)

build/a:
    ...

build/b:
    ...

build/c:
    ...

Of course, this would fail if targets are in the included files, or contain substitutions, etc. However, it works for "simple cases".

Matt
  • 13,674
  • 1
  • 18
  • 27
  • Could you include how the `make`'s output can be converted into a list of targets inside the makefile itself? e.g. I saw somewhere that i can do `all: $(targets)` -- how would i create that $targets ? Thx! – Yuri Astrakhan Mar 27 '19 at 14:38
  • that's exactly what I need - purely inside the Makefile, but I tried doing it and got lost in the $$s :) My goal is just to modify the above Makefile so that running `make all` would really build all targets, even if someone else edits it and adds a few more `build/...` entries. It would be great if you can modify your answer a bit to solve this issue in the most cross-platform way (if possible). Thank you!! – Yuri Astrakhan Mar 27 '19 at 22:59
  • my hope is for the $targets to automatically get the list of makefile targets matching a pattern, not to manually add them by hand. The example in my question -- I want the first line to be `all: $(targets)`, and nothing else. And the $targets should somehow be assigned a value without me listing "build/prog1 build/prog2 ...", rather than to duplicate the names of the ones listed below. I want to avoid a situation when someone adds a new target, and forgets to update the list in the "all:". I also don't want any CLI params - just a simple `make all`. – Yuri Astrakhan Mar 28 '19 at 21:18
  • 1
    Matt could you update the main answer with that, so that it would fit the question better? I don't want to accept the answer that users will need through comments to actually get the result. Thanks for a good suggestion. Also, could you split `all: ;@$(MAKE) $$(grep -o ^build/[^:]* $(lastword $(MAKEFILE_LIST)))` into two lines -- one that generates a $(target) var, and the one that uses it? Thanks! – Yuri Astrakhan Mar 29 '19 at 17:39