build2 0.6.0 Release Notes

These notes provide a more detailed discussion of major new features in the build2 release 0.6.0 , including motivation for implementing them and their usage examples. For the complete list of changes, see the Release Announcement or the NEWS files in the individual packages.

Build System

The bulk of the development in this release cycle has again gone into the build system. The two features that undoubtedly stand out are support for C++ Modules TS and precise C/C++ source change detection.

The build2 build system now includes support for building C++ Modules with the three main compilers: GCC (c++-modules branch), Clang (5.0 and up), and VC (15u3 and up). Supporting modules properly, that, is as part of the main build cycle rather than an ad hoc pre-build step, is, to put it mildly, challenging. What helped us a lot was already having proper support for auto-generated headers since modules are pretty similar. And we still had to redesign the underlying compilation pipeline a lot.

But all the hard work (we even submitted compiler patches) paid off: building modularized projects with build2 is as easy as listing modules along with other sources. For example (the mxx target type denotes a module interface unit):

exe{hello}: cxx{driver} {mxx cxx}{hello}

There is even support for installing modularized libraries with the module information stored in pkg-config .pc files (other build systems are invited to use the same approach for compatibility).

We are not going into more detail here since there is a whole section dedicated to C++ Modules Support in the build system manual. Because there is very little information on how to actually use modules, we've also included a practical Modules Introduction. Finally, there are now fully modularized variants of the "Hello, World" packages from the Introduction (look for mhello , libmhello , etc).

Some build systems calculate source file checksums in an attempt to minimize recompilation (for example, when switching branches). With the redesigned compilation pipeline we could go a step further: build2 now calculates a checksum of the preprocessed token stream (i.e., what the compiler proper receives from the preprocessor). This allows us to detect quite a few more ignorable changes, such as comments, whitespaces, unused macros, etc.

To give you a concrete example, in our projects we automatically (thanks to the version module) increment in-development versions on each commit. We also generate (again, automatically) version headers that include this information as well as the version compatibility checks. These headers are included by every translation unit in a project. You can probably see where this is going: every commit triggers a version change and thus the regeneration of the version header. And that, in turn, triggers the recompilation of the entire project. While conceptually sound (we, for example, no longer have any kind of version clashes in our CI setup), these recompilations were absolutely killing us.

Note that the version header does change: there is a new version number embedded into it on every commit. So a simple checksum of the file won't help. However, the checksum of the preprocessed token stream works wonderfully since the majority of translation units never actually expand the version macro (they include it for the compatibility checks). There is normally one translation unit with main() that handles the --version option which does get recompiled. But everything else is skipped.

And if you are worrying about the cost of tokenizing the preprocessed output and calculating this checksum, don't be: on modern hardware full preprocessing takes only a few percentages of the entire compilation. And so avoiding this entire compilation is a net win. See Separate Preprocess and Compile Performance for details.