Generate Checksums using Make and Automatic Variables
I've been migrating the build system of a project recently from a bunch of scripts to Make. Oddly, I find my self refactoring build systems a lot in my career.
In this build, instead of a single checksum file being generated per release, there's one checksum file for each file. There's multiple ways to do this in Make and I found the following approach to be the most elegant.
dist/myartifact:
echo 'artifact contents' > $@
%.sha256: %
cd $(@D); shasum -a 256 $(<F) > $(@F); shasum -a 256 -c $(@F)
make dist/myartifact.sha256 # build myartifact implicitly
# or
make dist/myartifact dist/myartifact.sha256 # explicitly build both
As well as targets defined in our Makefile, we can also use this checksum rule to generate checksums for any existing file.
This approach is using two features of GNU Make. "Static Pattern Rules" and "Automatic Variables", both are documented in GNU Make's Manual.
Static Pattern Rules
Static Pattern Rules lets us define a prerequisite based on the target's name using a %
. So for using our rule, make dist/myartifact.sha256
, %
becomes dist/myartifact
.
Automatic Variables
Automatic Variables are derived from our rule's target and prerequisites. In this case we're using the following:
$@
- Path of our target. "dist/myartifact.sha256"$(@F)
- Filename of our target. "myartifact.sha256"$(@D)
- Parent directory of our target. "dist"$(<F)
- Filename of the first prerequisite. "myartifact"
Conclusion
The use of @
and <
is a bit cryptic but other than that it's nice to have these variables around. It provides flexibility for pattern rules, and it reduces a lot of repetition for regular rules. Though, depending on your build, repetition might be preferable in most cases.
Using Make for this also provides the additional benefit of recalculating checksums only when files change (based on modification time).
Thanks for reading.