Can --always-make not affect a sub-make?

Viewed 131

Using GNU Make 4.1

Summary

I am calling a sub-make b.mk from a makefile a.mk. b.mk is called to ensure that a sub-system is built. Sometimes I want to force a target of a.mk to be remade:

make -f a.mk <target> --always-make

When I do this, b.mk also considers all targets out of date, but I don't want it to.

Failed remedy

I have tried using make -f b.mk MAKEFLAGS=, as suggested in the manual 5.7.3 Communicating Options to a Sub-make, but with no luck.

Here is the gist of a.mk:

.PHONY: all
$(info ===> a.mk MAKEFLAGS: $(MAKEFLAGS))
all:
    $(MAKE) -f b.mk y MAKEFLAGS=

and b.mk:

$(info ===> b.mk MAKEFLAGS: $(MAKEFLAGS))
y: x
    cp $< $@

Now, even when b.mk would normally regard y as being up to date:

$ make -f b.mk y
===> b.mk MAKEFLAGS: 
make: 'y' is up to date.

... y is remade when a.mk is called with --always-make (-B):

$ make -f a.mk --always-make
===> a.mk MAKEFLAGS: B
make -f b.mk y MAKEFLAGS=
make[1]: Entering directory '/home/matt/junk/make-b'
===> b.mk MAKEFLAGS: 
cp x y
make[1]: Leaving directory '/home/matt/junk/make-b'

As you can see, the B flag appears in a.mk's MAKEFLAGS, but not in those of b.mk. However, y is remade by b.mk.

Questions

  • Why?
  • Is there a way around this?
  • With GNU make I would expect that there's a very good reason for this behaviour. What is the reason?

Update: 2020-08-05

Why (on earth) would I want to do this?

In the answer https://stackoverflow.com/a/63231100/685715, there was a request to see an example of wanting to force a particular target to be remade, but without wanting sub-makes forced too.

Rather than invent something, here is an extract from the actual makefile which led to my question:

WWW_SVG := score.svg

%Score.app/$(WWW_SVG): %Score.svg | %Score.app/
    cd $(MUSIC_SCORE_PLAYER) && $(MAKE) -f $(MUSIC_SCORE_PLAYER_MAKEFILE) $(MUSIC_SCORE_PLAYER_TGT) MAKEFLAGS=
    cp $< $(MUSIC_SCORE_PLAYER_DIR)$(WWW_SVG)
    node $(MUSIC_SCORE_PLAYER_SVG_CONVERTER) > $@

The purpose of the rule is to create one SVG from another. The new one mirrors the state of the SVG part of the DOM after a web application loads and modifies the original SVG.

  • The first line of the recipe uses a sub-make to ensure that the web application is up to date, which it may not be as I am currently developing it. The web app is a separate project to the one with the makefile from which the above snippet was extracted.
  • The second line copies the original SVG into the web app's deployment directory so that it can be loaded by the web app
  • The third line calls a node script to launch the web app, extract the SVG from its DOM, and write it to stdout. This is then redirected to update the SVG target.

While I am testing, I want to be able to force the re-making of targets which have prerequisites that match %FooScore/score.svg, but without rebuilding the web application, unless it is out of date.

Of course, I could move the line that calls the sub-make so that it would be invoked just once, not once for every target that matches the rule. But that is an optimization, not a solution.

2 Answers

The point of "always build" is that all the parts of the build are invoked. It wouldn't make sense for this to not be passed down to sub-makes. In fact many makefiles have almost nothing happening at the top level makefile: they just invoke a set of sub-makes. If "always build" were not passed down, it would serve little purpose.

Unfortunately your examples are all predicated on things working this way, and they don't. In order to suggest a solution we'd need to see a more realistic example. In particular, you say you want to force a specific target to be remade but in your examples above you don't show that. What is the target that is to be remade? How does it interact with the sub-make invocation of b.mk?

As a note I should say, you should never use make to invoke sub-makes. You should always use $(MAKE) (or, equivalently, ${MAKE}).

This is based on this answer and its comments.

Looking at a.mk from the original question, one line needs to be changed:

.PHONY: all
$(info ===> a.mk MAKEFLAGS: $(MAKEFLAGS))
all:
    MAKEFLAGS=  $(MAKE) -f b.mk y    # This line has changed

It now uses MAKEFLAGS as an envirnoment variable instead of a command line parameter. See the comments in this answer for why this makes a difference.

We now see that -B (--always-make) is not passed to the sub-make and that, unlike when MAKEFLAGS is passed as a command line varibale, the sub-make does not "always-make":

$ make -B -f a.mk 
===> a.mk MAKEFLAGS: B
MAKEFLAGS= make -f b.mk y
make[1]: Entering directory '/home/matt/junk/make-b'
===> b.mk MAKEFLAGS: w
make[1]: 'y' is up to date.
make[1]: Leaving directory '/home/matt/junk/make-b'

The other difference here, compared to the output shown in the original question, is that the -w (--print-directory) flag is passed to the sub-make, which is fine.

Related