2

In our makefile, we have one recipe that links together all our object and library files to make an executable (an .elf file). As a side effect, this step also produces a map-file and an Intel .hex file:

$(ELF_FILE) : <list of dependencies here>
    <linker command line>

Until now, since we never actually had a $(MAP_FILE) or a $(HEX_FILE) target, when ever another target depended on one of the $(ELF_FILE)'s side products, we simply declared it to be dependent on $(ELF_FILE), even if the recipe of that target didn't want to access the $(ELF_FILE) itself at all. For instance:

# Target that needs map-file, which is a side product of the $(ELF_FILE) target.
$(TARGET_THAT_NEEDS_MAP_FILE) : $(ELF_FILE)
    <build-recipe>

# Target that needs hex-file, which is also a side product of the $(ELF_FILE) target.
$(TARGET_THAT_NEEDS_HEX_FILE) : $(ELF_FILE)
    <build-recipe>

We have recently found out that a recipe can be used for more than one target, like so:

$(MAP_FILE) $(HEX_FILE) $(ELF_FILE) : <list of dependencies here>
    <linker command line>

With this new-found knowledge, we figured we could get rid of the above "hack" and just directly state each target's direct dependencies:

$(TARGET_THAT_NEEDS_MAP_FILE) : $(MAP_FILE)
    <build-recipe>

$(TARGET_THAT_NEEDS_HEX_FILE) : $(HEX_FILE)
    <build-recipe>

Having implemented these changes, we now observe an odd effect that makes us suspect that we've either misunderstood this multiple-targets-one-recipe feature of make, or we're not using it correctly. The odd effect is that the recipe that produces the .elf, .map and .hex files now appears to run twice. This doesn't seem to have caused any immediate problems, but it does seem to indicate that something is fishy here. So my question, can our new approach work at all, or should we stick to the hack I described above?

EDIT: We're running our make in a multi-threaded manner (i.e. with -j).

antred
  • 3,697
  • 2
  • 29
  • 37

1 Answers1

2

It might be that when make is trying to update a target (whether it is $(MAP_FILE), $(HEX_FILE) or $(ELF_FILE), it does not know that its recipe will also update another target, therefore it starts a recipe for that one too, even if it's the same.

Of course, that would only happen when using the -j option. (Did you had the possibility to try without ?)

To illustrate :

$(TARGET): $(ELF_FILE) $(MAP_FILE)
   <update target>

Here make will try to update $(ELF_FILE) and $(MAP_FILE) and fire the recipe twice. (That should also applies if the dependencies are on different target, as long as the targets are updated by a one execution of make and that there is no bottlenecks between them.

I'm not completely sure about that, though, make might be able to know that this is the same recipe.

======

This answer might be of use to you. Specifically :

However, if your output files and your input file share a common base, you CAN write a pattern rule like this:

%.foo %.bar %.baz : %.boz ; $(BUILDIT)

Strangely, for implicit rules with multiple targets GNU make assumes that a single invocation of the recipe WILL build all the targets, and it will behave exactly as you want.

MadScientist

It refers to that part of the make manual :

Pattern rules may have more than one target. Unlike normal rules, this does not act as many different rules with the same prerequisites and recipe. If a pattern rule has multiple targets, make knows that the rule’s recipe is responsible for making all of the targets. The recipe is executed only once to make all the targets. When searching for a pattern rule to match a target, the target patterns of a rule other than the one that matches the target in need of a rule are incidental: make worries only about giving a recipe and prerequisites to the file presently in question. However, when this file’s recipe is run, the other targets are marked as having been updated themselves.


EDIT:
Gnu Make has now gained a feature that would support this usecase (in version 4.3) : grouped explicit targets. It allows make to be aware that one recipe generate several targets, and it used like this (from the gnu make manual) :

foo bar biz &: baz boz
    echo $^ > foo
    echo $^ > bar
    echo $^ > biz

foo, bar, and biz are generated by this rule (note the use of &: instead of :.
Full documentation : https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html ("Rules with Grouped Targets")

VannTen
  • 441
  • 2
  • 12
  • 1
    Your comments at the beginning of this message are not correct. A rule like `foo: bar baz` will run its recipe one time after all the prerequisites are built. Each prerequisite will be built one time no matter how many times it appears as a prerequisite. The problem is that this: `foo fab : bar baz` actually defines _TWO_ rules. It's identical, from make's perspective, to writing `foo : bar baz` and `fab : bar baz` separately. Note this is ONLY true of explicit rules. Pattern rules, as you show in the second part of your message, are handled differently. – MadScientist May 22 '17 at 18:47
  • 1
    My formulation was not very clear : I meaned that with a rule a like `foo : bar baz` and another like `bar baz : fab`(so in fact two rules) with the second rule producing bar, and baz as a side effect, make might try to rebuilt baz without knowing that the bar recipe has already been fired, but not yet finished, (so that would only happen in the context of a parrallel build), which could explain the OP behaviour. Is that a possibility ? – VannTen May 22 '17 at 22:25
  • 1
    Yes, that is quite likely in fact. – MadScientist May 22 '17 at 23:19
  • Well, I guess that means we will have to revert to our hack. I have to say, for something that is basically the staple of software builds, GNU make's functionality is pretty disappointing. :-\ – antred May 23 '17 at 11:34