views:

421

answers:

4

I have a Makefile that starts by running a tool before applying the build rules (which this tool writes for me). If this tool, which is a python script, exits with a non-null status code, I want GNU Make to stop right there and not go on with building the program.

Currently, I do something like this (top level, i.e. column 1):

$(info Generating build rules...)
$(shell python collect_sources.py)
include BuildRules.mk

But this does not stop make if collect_sources.py exits with a status code of 1. This also captures the standard output of collect_sources.py but does not print it out, so I have the feeling I'm looking in the wrong direction.

If at all possible, the solution should even work when a simple MS-DOS shell is the standard system shell.

Any suggestion?

+1  A: 

There might be a better way, but I tried the following and it works:

$(if $(shell if your_command; then echo ok; fi), , $(error your_command failed))

Here I did assume that your_command does not give any output, but it shouldn't be hard to work around such a situation.

Edit: To make it work with the default Windows shell (and probably any decent shell) you could write your_command && echo ok instead of the if within the shell function. I do not think this is possible for (older) DOS shells. For these you probably want to adapt your_command or write a wrapper script to print something on error (or success).

mweerden
It assumes a unix shell, though.
JesperE
+1  A: 

You should use a regular target to create BuildRules.mk:

BuildRules.mk: collect_sources.py
        python $< >$@

include BuildRules.mk

This is the standard trick to use when automatically generating dependencies.

JesperE
Jesper, thanks for your input, but I don't see how your rule can fix my problem. I need collect_sources.py to run everytime, so that I can update the list of source files and rules, etc. Or am I missing something?
Carl Seleborg
Add a ".PHONY: BuildRules.mk" at the top, then.
JesperE
Perhaps I'm missing something obvious here, but I can't seem to get this to work correctly. BuildRules.mk is remade every time (using .PHONY), but the include happens before this is done. This effectively means you are always using the previous BuildRules.mk.
mweerden
Hm. The .PHONY is preventing Make from reloading the generated file, I missed that. BuildRules.mk needs a proper dependency on something.
JesperE
How about using $(wildcard ...) to let BuildRules.mk depend on all files which might affect the build rules?
JesperE
A: 

Make sure you're not invoking make/gmake with the -k option.

JohnMcG
+1  A: 

Ok, here's my own solution, which is unfortunately not based on the status code of the collect_sources.py script, but which Works For Me (TM) and lets me see any output that the script produces:

SHELL_OUTPUT := $(shell python collect_sources.py 2>&1)
ifeq ($(filter error: [Errno %],$(SHELL_OUTPUT)),)
  $(info $(SHELL_OUTPUT))
else
  $(error $(SHELL_OUTPUT))
endif

The script is written so that any error produces an output beginning with "collect_sources: error:". Additionally, if python cannot find or execute the given script, it outputs an error message containing the message "[Errno 2]" or similar. So this little piece of code just captures the output (redirecting stderr to stdout) and searches for error messages. If none is found, it simply uses $(info) to print the output, otherwise it uses $(error), which effectively makes Make stop.

Note that the indentation in the ifeq ... endif is done with spaces. If tabs are used, Make thinks you're trying to invoke a command and complains about it.

Carl Seleborg