1

Given a Makefile that's often times run with the -j flag for parallel builds. I want it to terminate with a result message. I would like this message to say if the build failed, and if it failed, what the error was. It doesn't have to say anything if the build succeeded (although it could) but it must warn the user when a target failed to build and why.

This behavior is already there during sequential builds, but not during parallel builds. Parallel builds interweaves the output and an error message is often overlooked because output from other targets might push the failed target's error off screen. A careless developer might see no errors on his/her screen and assume the build succeeded.

It's quite an intuitive feature and I've searched for an answer, but it doesn't seem like there's any straight forward solutions. Any ideas?

bli00
  • 2,215
  • 2
  • 19
  • 46
  • You might want to look at ["Output During Parallel Execution"](https://www.gnu.org/software/make/manual/make.html#Parallel-Output). – G.M. Sep 20 '19 at 20:08
  • There is an open feature request (from 2013) related to this: [bug #39146: Indicate error upon termination in case of parallel jobs](https://savannah.gnu.org/bugs/?func=detailitem&item_id=39146). – Scott McPeak Jun 02 '22 at 06:44

2 Answers2

1

You basically run

make -j 8 2> >(tee /tmp/error.log)
test $? -ne 0 && echo "build errors:"
cat /tmp/error.log

and you get all of stderr after the build finishes.

root
  • 5,528
  • 1
  • 7
  • 15
  • This is actually a bit cleaner than my solution, as it pipes all stderr to to output, as opposed to looking for a particular string. Most compilers/linkers output errors and warnings to stderr, so this will give you a simple way of finding everything that fails, without making any assumptions on the format. The above syntax is bash specific, and does not work in bourn shell (`sh`) which make often uses. If you wanted to do this in a makefile you could switch the default shell (Or see [this answer](https://stackoverflow.com/a/692407/8710344) to do it in `sh`) – HardcoreHenry Sep 23 '19 at 13:47
  • I guess this solves my problem in a creatively way but it's not exactly what I was looking for. One could always manually check for the return code in `$?` to see if there's an error and scroll up in the terminal until you find the error, but not every developer will remember to do that. I could put this in a script and use the script as the entry point but that's not a canonical Makefile solution and adds the overhead of a having script. – bli00 Sep 23 '19 at 19:12
0

-- EDIT --

Updating to use tee, to output on stdout and into file:


Make returns non-zero if one of its recipe's fails so you could do something like this from the command line (assuming bash shell):

make 2>&1 | tee build.log
[ ${PIPESTATUS}[0] -eq 0 ] || ( echo "MAKE FAILED!"; grep --color build.log "Error:" )

The ${PIPESTATUS}[0] gives you the exit code of the first command (make 2>&1) as opposed to the exit status of the entire command (which would the exit status of tee if the make failed). It is bash specific, so it won't work in zsh for example.

Alternatively you could add the same logic as the top level target of a recursive make.

ifndef IN_RECURSION
export IN_RECURSION:=1

$(info At top level -- defining default target)

_default: 
    @echo "doing recursive call of make"
    @$(MAKE) $(MAKECMDGOALS) IN_RECURSION=1 2>&1 | tee build.log; \
     [ ${PIPESTATUS}[0] -eq 0 ] || ( echo "MAKE FAILED!"; grep --color "Error:" build.log )

.PHONY: _default

endif

all: 
   ....

Note that in this case the \ used to catinate the two recipe lines is crucial, as the second command must run in the same shell instance as the first.

HardcoreHenry
  • 5,909
  • 2
  • 19
  • 44
  • I added the snippet to my Makefile and it doesn't seem to work. Nothing was executed. Perhaps my understanding of GNU Make is too limited, but I was under the impression that `.PHONY` is called during ran and it executes `_default`, then `_default` calls `make` with the same parameters given, and if something fails, a error is printed from `build.log`, correct? So far it doesn't seem like `_default` is executed at all. I added a dummy echo before `@$(MAKE)` and nothing was printed. – bli00 Sep 20 '19 at 22:16
  • .PHONY simply tells make that _default does not represent a file name -- see [here](https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html). It's not an actual target. Thus, the code snippet defines target `_default`. If this is the first target in the makefile, then `make` should invoke it... Try adding `$(info got here)` right above the target to see if it's included. Also ensure that it is the first target. Also, the example pipes output to build.log as opposed to showing it on the screen. I'll modify the example to use `tee`... – HardcoreHenry Sep 23 '19 at 13:00