JamPlus manual
Building with Checksums

Overview

Stock Jam is able to determine builds very efficiently by using just the timestamps from files. Upon first access of a file in a given directory, Jam retrieves the timestamp for that file and all other files in the directory. When the timestamp of the source file is newer than that of its built destination target, such as when updating a .cpp file and consequently making the executable now be older, Jam will build the target. If no change is made to a file but the timestamp is updated, a build is correctly (or incorrectly, depending on your view) initiated.

JamPlus adds optional support for using the checksum of a file to determine the need to build. It does so by tracking the timestamp of a file and its associated checksum inside of a file called .jamchecksums. Having checksum support enabled slows down the initial execution of Jam as checksums are gathered, but on subsequent executions where source files aren't changing often, the checksums are looked up from the .jamchecksums database instead of being calculated each time.

Please note that the checksum support is derived from the same system that drives the File Cache, so many of the same concepts apply.

Usage

Given that checksums are optional and disabled by default, support can be enabled by setting JAM_CHECKSUMS to 1 at the beginning of any Jamfiles.

JAM_CHECKSUMS = 1 ;

Without further configuration, the Jam checksums support should work out-of-box. Sometimes, though, additional hints may be needed to compensate for correct but undesirable behaviors.

As with the File Cache facility, checksums require deep calculations through the dependency graph in order to guarantee a correct build. This is a good thing in most circumstances, but there can be some build 'anomalies' that may make it necessary for you to adjust how deep the calculations go or what is considered for the buildmd5sum calculation.

For example, consider a build where the destination target depends on a tool executable. If the source target file changes, the destination target rebuilds. If the tool executable changes, the destination target also rebuilds.

  • destination.file
    • depends on tool.exe
      • depends on main.cpp
    • depends on source.file

For the following discussion, assume the tool's author merely adds comments to the main.cpp file that makes up the tool executable's source code with absolute no functionality change.

Using GCC on Linux, it is very likely this scenario will generate the exact same executable. However, destination.file will still rebuild. That is because the buildmd5sum is calculated against tool.exe (which didn't change) and main.cpp (which did change). The extra comment in main.cpp will force Jam to believe the dependencies were updated, and destination.file will build.

Fortunately, there is an easy workaround to this in the form of a 'hint' rule. We merely have to apply the rule rule ForceContentsOnly targets ; to tool.exe, and the buildmd5sum traversal will stop there, never considering main.cpp. This is such a big deal that the rules rule C.Application TARGET : SOURCES [ : OPTIONS ] and rule C.Library TARGET : SOURCES [ : OPTIONS ] do it by default.

Unfortunately, using Microsoft's Visual C++, any simple comment change in main.cpp will result in an ever changing tool.exe. When relinking an executable with Microsoft's Visual C++, it is impossible to get a reproducible build; the executable will be different every single time. This means that some documentation-happy programmer may cause massive rebuilds of content for every single bit of commentary he/she adds.

There are a few ways to work around this, but we'll only cover the one specifically used as a hint to the checksum system here.

By using rule IgnoreContents targets ; on tool.exe, the buildmd5sum traversal will just simply not sum in the contents of tool.exe. The destination.file target's dependency on tool.exe will act only as a dependency that causes tool.exe to build. For destination.file itself, no build will occur when tool.exe changes.

That is bad, of course, and the advised workaround is to use rule UseCommandLine TARGETS : COMMANDLINE; in addition to rule IgnoreContents targets ;. When tool.exe receives a change that actually matters, bump a version string supplied to UseCommandLine like this:

IgnoreContents tool.exe ;
UseCommandLine destination.file : v2 ; # This was previously v1.

Using UseCommandLine is often a better method anyway than a direct dependency on the executable. While the direct dependency may be 'most' correct, it may also be overzealous especially in a situation where tool.exe handles more than one type of content, and the change to tool.exe was made for a different file type altogether. In that case, different UseCommandLine statements can be used to better direct what should be built.

Note that using IgnoreContents in this manner is pretty much analogous to using rule Needs targets1 : targets2 [ : target3 ... targets9 ] ;.