We're having some discussion lately about the way we handle .d files for dependencies in our make-based build process. The issue has been raised that sometimes the .d files can become corrupted when builds are interrupted.
We're using the .DELETE_ON_ERROR target to ensure that if a build is interrupted or fails, the object files that it was in the process of generating are deleted. However we're also using GCC to generate .d files at compile time which would need to be deleted as well. There doesn't appear to be a straightforward way to tell make about this.
So the question is, is there a way we can coax make to delete both our object and our dependency files in the case of an error? Is there some way we can set up the rules so that it knows that both the .d and .o files are generated at the same time and need to be deleted if there's an error?
Alternately, is there something else we can do to fix the problem of corrupt .d files? One suggestion along these lines is to generate the .d files with a temporary name and have a separate post-compile step per file that copies it to the correct name.
Generally speaking, GNU make doesn't support targets with multiple outputs. However, there is an exception to that rule: pattern rules. If you can structure your makefile such that it uses pattern rules to generate the object files, you may be able to achieve your goals. For example:
.DELETE_ON_ERROR:
all: foo.o
%.o %.d: %.c
@touch $*.d
@touch $*.o
@exit 1
You'll see that with this makefile when the "error" is detected in the rule, both the .d and the .o file are deleted. The advantage to this approach is that it more accurately expresses the dependency graph by describing how the .d file is to be generated and what rule will produce it.
Alternatively, a common paradigm in this case is just as you suggested: have GCC generate the .d file into a temporary file name and only move it into place after the GCC command has completed successfully. Usually this is accomplished with a trick of the shell:
all: foo.o
%.o: %.c
gcc -o $@ -MMD -MF $(basename $@).d.tmp -c $< \
&& mv $(basename $@).d.tmp $(basename $@).d
Here the "magic" trick is the use of the GCC flags -MMD
, which generates the dependency file as a side-effect of compilation, and -MF
, which lets you specify the output name for the dependency file; and the use of the shell cmd1 && cmd2
syntax, which causes the shell to only execute cmd2
if cmd1
exits successfully.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With