Back to All Articles Subscribe   

Expert Make Tip: Use .DELETE_ON_ERROR

Written by J. David Giese. First published June 2019.

Most makefiles should include the .DELETE_ON_ERROR special target.

Make executes each line of a recipe in a new shell and will quit if an invocation exits with a non-zero status. If the target file was altered prior to exiting, then the target’s last-modified time will indicate that it is up to date even though it is likely corrupt or incomplete. Thus, next time make runs it will not update the target file. This behavior is often confusing and may lead to malformed build artifacts. If the .DELETE_ON_ERROR special target is present in the makefile, then make will delete the target file if a recipe has an error.

Makefile Lingo Refresher

A makefile consists of one or more rules. Here is an example with one rule:

languages.csv: projects.json
	python extract_languages.py projects.json > languages.csv

The first line of the rule indicates that languages.csv, the target, depends on projects.json, its only prerequisite. The second line, which is the rule’s recipe, is a script that should create the target from its prerequisites.

Key makefile terminology

When make processes a rule, it compares the last-modification time of the target to the last-modification times of its prerequisites. If a prerequisite is newer than the target (or doesn’t exist), then make will run the recipe. If the command on a recipe line exits with a non-zero status, make will quit. If the .DELETE_ON_ERROR special target is included, make will delete the target before quitting.

Special Targets

Certain names that begin with a period have special meanings if they appear as targets. For example, if a rule exists with .PHONY as the target name, then the prerequisites of this rule will be considered phony targets and will always have their recipes run regardless of their last-modified times. The .PHONY special target is an example of a special target configuring how make treats its prerequisites. Other special targets act as global configuration. For example, the .DELETE_ON_ERROR and .ONE_SHELL special targets act globally.

If we added .DELETE_ON_ERROR to our example makefile it would look as follows:

.DELETE_ON_ERROR:

languages.csv: projects.json
	python extract_languages.py projects.json > languages.csv

Note there is a colon because .DELETE_ON_ERROR must be the target of a rule, even though it has no prerequisites and no recipe.

Problem I: Rerunning Make with Invalid Targets

Imagine that, while updating projects.json, you introduce a syntax error. You run our example makefile and extract_languages.py has a non-zero exit status, but not before the shell’s output redirection truncates language.csv and updates its last-modified time. Thus languages.csv will be empty but make will consider it up to date.

Fixing the syntax error in projects.json will increment its last-modified time, informing make that it needs to rebuild languages.csv. However, imagine that this rule was part of a complex makefile which was run in parallel. It is easy to imagine a developer, especially one not familiar with the project, running make again without fixing the error. If an empty languages.csv didn’t cause downstream problems, make would complete successfully, and the developer may not realize the build targets are invalid.

Using .DELETE_ON_ERROR prevents this from occurring.

Problem II: Defects in Missing Dependencies

Now imagine that there is a bug in extract_languages.py. Since extract_languages.py is not a prerequisite of languages.csv, even if you fix the bug, make will not recognize that languages.csv is out of date. Thus, after you fix the bug you will need to manually delete languages.csv to have make build it again.

The ideal solution is to include your scripts as prerequisites. Doing so protects you in this situation and also ensures that make will re-build your targets if your scripts are updated. However, maintaining script dependencies can be challenging (extract_languages.py may depend on many other python files).

Using .DELETE_ON_ERROR is a practical alternative that avoids the need to manually clean invalid targets, while also not forcing you to maintain your scripts as prerequisites.

Problem III: Flaky Recipes

Finally, imagine that extract_languages.py makes an HTTP request a web-server. If this server is unavailable, languages.csv will be invalid. After the web-server is back online, you would need to manually delete languages.csv before make would run the recipe again.

Using .DELETE_ON_ERROR ensures you don’t need to manually clean targets built from flaky recipes.

Does .DELETE_ON_ERROR Have Downsides?

The GNU Make Manual says this about deleting target files after a recipe error:

This is almost always what you want make to do, but it is not historical practice; so for compatibility, you must explicitly request it.

After considering the problems that .DELETE_ON_ERROR solves, it is reasonable to ask if there are situations when you would not want to include .DELETE_ON_ERROR in your makefiles.

The main reason to not use .DELETE_ON_ERROR is that it is a GNU Make extension. GNU Make is quite pervasive, but if you need to build on multiple platforms, you may not want to rely on it. See this excellent article for a tutorial about writing portable makefiles.

Another situation when you may not want to enable .DELETE_ON_ERROR is if you want to keep the target files even after there is an error (e.g., imagine a target takes weeks to compute). In this situation, it may be appropriate to use .DELETE_ON_ERROR but to list these “precious” targets as prerequisites of the .PRECIOUS special target, which will inform make not to delete them even if there is an error in the recipe.

Continue Learning

Make has been used extensively for forty years, and offers incremental builds, parallelization, and declarative syntax. At Innolitics, we use make to build regulatory documents, to transform the DICOM standard into a condensed JSON format (for our standard browser), and other tasks.

If you want to learn more about make, we recommend the GNU Make Manual. It is easy to read and well written. The pages that discuss special targets and errors in recipes are especially relevant to this article.

How could we improve the sample makefile? Hint: check out the automatic variables page.

John Graham-Cumming wrote a book about GNU Make; much of the book stemmed from his blog posts on GNU Make, which are still available and are useful.


Was this article interesting?

We publish technical articles and coding case studies about topics we run into in the field. Follow us on Twitter or subscribe to our email list:



Back to All Articles