Delete targets with recipes failed in Makefile

Viewed 1390

I tried to use .DELETE_ON_ERROR target in makefile in order to delete both $(OBJ) and executable files if the recipe fails, but it doesn't work. If I put an error inside any object file than while compiling the pattern rule an error occurs and it stops. The old object file is still on its place but I expect .DELETE_ON_ERROR to remove it.

Can anyone test the code? Can -include $(DEP) or flag -DDBG influence? The goal is to delete both the .o file that failed and the executable.

OUTPUT = executable
CPP := $(shell find $(SRC) -type f -name "*.cpp")
OBJ := $(CPP:.cpp=.o)
DEP := $(OBJ:.o=.d)

CXX := g++
CXXFLAGS =-MMD -MP -DDBG
INCLUDES = -I.

.DELETE_ON_ERROR :

$(OUTPUT): $(OBJ)
    $(CXX) $^ -o $@

%.o: %.cpp
    $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@

-include $(DEP)

.PHONY : clean
clean:
    rm -rf $(OBJ) $(DEP) 

EDIT: According to the Ondrej K. solution to fix this problem you need to add @touch command before compilator in order to make the object files changed (the docs read "delete the target of a rule if it has changed".). So, the code should look like this:

%.o: %.cpp
    @touch $@
    $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
2 Answers

Not sure what failure you're seeing, but I am afraid there really isn't a good way for you to do that. .o files and executable ($(OUTPUT)) are separate rules. If the latter fails, former is already out of consideration. See the documentation:

.DELETE_ON_ERROR: If .DELETE_ON_ERROR is mentioned as a target anywhere in the makefile, then make will delete the target of a rule if it has changed and its recipe exits with a nonzero exit status, just as it does when it receives a signal. See Errors in Recipes.

In other words, if your the target producing a binary object failed after .o target itself got updated, make would prune the changed file. But if your executable did not link, it won't go back and delete object files.

Not sure it'd be nice, but if you really needed to, you could probably achieve this by refactoring your makefile to basically have direct exec + objs from source prerequisites rule with a single recipe. Obvious downside, such rule would mean single .c file change causing all files being recompiled (basically negating substantial benefit of using make).


EDIT: I'll expand on the comment a bit to clarify. What you seem to want is: in case there is a broken .c file and compilation fails, remove the old .o file. That is quite clearly not how .DELETE_ON_ERROR works though. If the .o file already got updated, and then the rule failed, it would remove it ("delete the target of a rule if it has changed"), but in case of a mentioned syntactical problem, the compiler would fail before it would produced an .o file.

So, if for instance you updated your (pattern) rule for compilation so that it first touches (effectively updates timestamp) on the .o file and then tries to compile. After the compiler call and rule failed make would consider the target of the failed root to have been updated and remove it. Alternatively you could also change to rule to first try to rm the expected '.o' file in which case you actually wouldn't need to use .DELETE_ON_ERROR (and if there is no change in the relevant sources, the rule does not get used, so it's actually not as terrible as it sounds). Either way is not exactly very clean, but leads towards the behavior I understand you're describing.

It is possible that the Compiler crashes while writing the Output file. In this case, there is a corrupt output file that is newer than its sources. Make will stop due to the error, but on next run, it won't recompile the output file as it is newer than ist sources - and the make will fail again and again in the build step. With the .DELETE_ON_ERROR rule, make will delete the Output file if the compiler (or whatever build step failed) exits with an error after touching (and corrupting) the Output file, so it will be recompiled on next run. (if the Compiler failed without touching the old output file, it will always be recompiled on next run anyway)

Related