How do you abort on shell command errors in makefiles?

I'm trying to write a makefile to package up some PHP scripts. I would also like it to check for syntax errors (using the built in php lint tool) before building the final zip file, to prevent any accidental errors slipping in.

So far I have

all: dist clean: rm -f output.zip dist: clean for i in `find . -name "*.php"`; do php -l $$i; done zip -r output.zip src -x "*/.*" "*/tests*"

This works, but if there is a PHP error I would like it to break and not continue building the zip file (for obvious reasons). Currently they may just get lost in the pages of output from successful lint runs and the zip file.

Am I going about this the right way? Should I be using a make loop rather than a shell loop? How to you get make to abort on shell command errors?


You can do something like this:

for i in `find . -name "*.php"`; do \ php -l $$i; \ if [ $$? -ne 0 ] ; then exit 42 ; fi \ done


The shell for loop in your dist recipe is hiding the exit status of php -l from make, so it doesn't know to exit. Mat's answer shows how to fix that by having the shell command exit on error.

The shell loop could be avoided entirely like this:

all: dist clean: rm -f output.zip LINT_TARGETS := $(addprefix lint-,$(shell find . -name "*.php")) .PHONY: $(LINT_TARGETS) $(LINT_TARGETS):lint-%:% php -l $< dist: clean $(LINT_TARGETS) zip -r output.zip src -x "*/.*" "*/tests*"

Now there's a phony target with a static pattern rule to run each of the php -l commands. If any of them fails, the dist recipe will not run. This is arguably harder to read than a shell loop, but it means that a) the error message from make itself will finger the problematic file for you and b) with make -j you can have multithreaded lint checks :-)


  • Error building gcc 4.8.3 from source: libstdc++.so.6: version `CXXABI_1.3.8' not found (require
  • xp_regread() returned error 5, 'Access is denied.'
  • Is there a less verbose alternative to Perl's Data::Dumper?
  • Modifying files nested in tar archive
  • twisted.internet.error.ConnectError when run scrapy spider
  • Is there a equivalent to JSON.Net in Java? [duplicate]
  • end daemon processes with multiprocessing module
  • Git describe fails to return most recent annotated tag
  • Bash if statement with multiple conditions
  • Invalid object name 'dbo.Item'
  • nonblocking BIO_do_connect blocked when there is no internet connected
  • Jenkins: FATAL: Could not initialize class hudson.util.ProcessTree$UnixReflection
  • How to view images from protected folder with php?
  • Query to find the duplicates between the name and number in table
  • Excel's Macro-Recorder usage
  • What and where is mdimport
  • zope_i18n_compile_mo_files doesn't work on a Zeo configuration
  • How do I exclude a dependency in provided scope when running in Maven test scope?
  • How do I get HTML corresponding to current DOM tree?
  • Display images in Django
  • PHP buffered output depending on server setting?
  • print() is showing quotation marks in results
  • Why does access(2) check for real and not effective UID?
  • Is it possible to access block's scope in method?
  • Meteor helpers not available in Angular template
  • Asynchronous UI Testing in Xcode With Swift
  • Resize panoramic image to fixed size
  • How to recover from a Spring Social ExpiredAuthorizationException
  • angularjs unit test when to use $rootScope.$new()
  • ILMerge & Keep Assembly Name
  • Deserializing XML into class C#
  • Importing jscolor library in angular 2
  • Large data - storage and query
  • Function pointer “assignment from incompatible pointer type” only when using vararg ellipsis
  • WOWZA + RTMP + HTML5 Playback?
  • How can I remove ASP.NET Designer.cs files?
  • python draw pie shapes with colour filled
  • Is there any way to bind data to data.frame by some index?
  • How can i traverse a binary tree from right to left in java?
  • How to Embed XSL into XML