build2 | FAQ

What is build2?
Why are you doing this?
What is the end goal?
Who is behind this?
How are you going to make money from this?
What does 2 in build2 stand for?
Why not to use build2?
How does build2 compare to other build systems (CMake, Bazel, etc)?
How does build2 compare to other package managers (Conan, Vcpkg, etc)?
Why not just use a system package manager?
Why did you write build2 in C++ instead of another language (Rust, Python, etc)?
What are your plans for cppget.org?
Do you support cross-compilation?
Do you support binary packages?
Do you support VCS other than git?
Can I run my own private/public repositories?
Why did you call the build system driver b?
Why does Ninja start compiling faster than build2?

What is build2?

At the center of the project is the build2 toolchain, a hierarchy of tools consisting of the build system, the package manager (for package consumption), and the project manager (for project development).

Then there is cppget.org, a central repository/registry of open source packages. Each package in this repository builds with the build2 build system, can be consumed with the package manager, and can be conveniently released and published with the project manager. Once a package is submitted to cppget.org, it is automatically built and tested on a substantial number of commonly used platforms/compilers with the results available as part of the package page in the repository (pick any version of libsqlite3 for an example). To allow the same building and testing during development, there is ci.cppget.org which provides a push-style CI service (normally accessed via the project manager).

Besides these central elements, there is also a number of other tools and services. The following list summarizes all the notable parts:

build2
Build system.
bpkg
Package manager.
bdep
Project manager.
libbuild2-*
Collection of build system modules.
cppget.org
Package repository/registry.
queue.cppget.org
Package repository queue.
ci.cppget.org
CI service.
github.com/build2-packaging/
Packaging efforts of third-party projects.
*.stage.build2.org
Staging infrastructure.
brep
Package repository web interface (runs on cppget.org).
bbot
Build bot.
buildos
Custom (Debian-based) operating system for running build bots.

Why are you doing this?

To make something people want? No, scratch that: we are making what we need.

We have a bunch of cross-platform C++ tools and libraries that are becoming an ever increasing pain to develop, test, distribute, and support. A mailing list thread of 20 messages where we painstakingly discovered that the user accidentally built our library with libstdc++ and their application with libc++ which all resulted in mysterious crashes (exception handling was not working properly) – this is a waste of life.

What is the end goal?

The end goal is a universal build toolchain that can build a project of any complexity written in any language (or a combination of languages), including all its dependencies, from the ground up and without relying on any other build systems, package managers, etc.

Since all the modern software stacks in practical use today are based on C/C++, the medium-term goal is to rebuild the foundation of the C/C++ software ecosystem with a single, modern, general-purpose build system and make it available from a single package repository that can be accessed from all the major platforms (including Windows) and used with all the major compilers in a uniform manner and without requiring another "system" (Java, Python, Cygwin, etc).

Who is behind this?

The development of the build2 core and packaging of the foundational C/C++ libraries is funded by Code Synthesis, both in terms of the full-time engineering effort and build infrastructure. The bulk of the packages on cppget.org are available thanks to the packaging efforts of the growing build2 community.

How are you going to make money from this?

We are not sure. Maybe we won't. Honestly, if this succeeds, and we won't have to manually test and distribute our projects on the myriad of platform/compiler/IDE combinations, then it would have already been worth it.

What does 2 in build2 stand for?

It stands for "take two", after a previous, GNU make-based attempt called build.

Why not to use build2?

See also:

While we aim for a general-purpose build system and package manager, build2 is not everything to everyone. As a result, if you are looking for one or more of the following, build2 may not be a good fit:

May be an overkill for simple, single-platform projects.
build2's main use cases are complex, cross-platform C/C++ (or mixed language) projects. Complex single-platform projects (for example, that rely heavily on auto-generated source code) and simple cross-platform projects could also benefit. However, it may not be worth it for a simple single-platform project if you find the platform's native build system and package management adequate.
Not an IDE project generator (Visual Studio, Xcode, etc).
build2 is not a meta-build system and will unlikely ever support IDE project generation. Though an IDE could conceivably use build2 buildfiles as a source of project information.
No build-by-convention.
The build system expects an explicit description of the project. However, use of wildcard patterns can approximate build-by-convention (while supporting various layouts found in real-world C/C++ projects).
Build description is not a general-purpose language (JSON, YAML, etc).
We believe the importance and complexity of the problem warrants a purpose-built language.
You may not get far without reading the documentation.
The complexity of real-world C/C++ projects and variability in platforms that they must support requires an elaborate and flexible conceptual model of build (which is also substantially different compared to other build systems, such as CMake). While you should be able to make minor tweaks in existing buildfiles without much prior experience, to fully understand build2 and use it effectively you will need to read a substantial amount of documentation, at least eventually.
You may not be able to replicate existing source/output layout exactly.
Most C/C++ build systems are "mono-repo first" while build2 is decidedly "multi-repo first". While build2 offers quite a bit of flexibility when it comes to source and output layouts, there are a few fundamental expectations (such as that the output directory tree must be the same or outside of the source) which are probably not worth fighting.
No autoconf-style configuration probing.
build2 eschews error-prone autoconf-style probing in favor of expectation-based configuration where a project assumes a feature to be available if certain conditions are met, for example, a feature test macro is defined. This approach is supported by the autoconf build system module.

How does build2 compare to other build systems (CMake, Bazel, etc)?

See also:

Modern build systems vary greatly not only in the individual features they support but in the overall approach they take. The following key points should give you a sense of where build2 fits in this space:

And the following list highlights other major features of the build2 build system:

How does build2 compare to other package managers (Conan, Vcpkg, etc)?

See also:

Similar to build systems, there is great variability in this area and the following list highlights the key features of the build2 package manager:

But the most significant difference, at least when it comes to C/C++, is the mandatory use of the build2 build system by all packages (using system-installed packages is the only escape hatch). This has a number of benefits:

More robust builds.
One build system means fewer things to go wrong. Also, when things do go wrong, you don't need to be an expert in multiple build systems.
Support for new platforms and compilers.
When build2 adds support for a new platform or compiler, you are not dependent on other build systems to provide the same support. So far this already happened twice with the addition of support for Clang targeting MSVC and Emscripten.
Faster builds.
With build2, your project and all its dependencies are built as part of the same build system invocation. This has a number of build performance implications. As an example, let's consider a hypothetical project that uses libcurl and libboost-regex, with libcurl in turn depending on libssl from OpenSSL and libboost-regex – on a few libraries from ICU.

Outside of build2 these four libraries use four different build systems, which means we would have to build them one-by-one in the dependency order (we could build OpenSSL and ICU at the same time but then we would need to split available hardware threads between the two jobs). In contrast, when using build2 packages of these libraries, everything is built at the same time: ICU and OpenSSL builds will proceed in parallel and as soon as one of them is ready, its dependent (Curl or Boost) can start building (it's actually even better than this: Curl source code can start compiling before the OpenSSL library is linked and the same with Boost and ICU).

But that's not the end of it: with build2 we will only build what's ultimately needed by our project. For example, ICU actually consists of three libraries but libboost-regex only depends on two. The same story with Boost: we only need libboost-regex (and all its dependencies) while Boost provides over a hundred libraries. This also continues with library variants of our dependencies: should we build static, shared, or both variants of each library? With build2 we automatically build only what's needed.

To put some specific numbers on all of this, a debug version of this build takes about 25 seconds on i9-12900K.

Why not just use a system package manager?

See also:

In short, there is nothing wrong with that, they work well (unless you are on Windows), and we use them ourselves every day. And build2 (both the build system and package manager) work well with system-installed packages.

To elaborate, system package managers (such as rpm, dpkg, FreeBSD pkg, etc) serve a different purpose compared to bpkg and bdep: they are optimized for the end-user who wants to easily install and use a program, not the developer who wants flexibility and power in juggling their library/tool dependencies. Yes, most system package managers support installing development files for a library but you can normally only have one version at a time. While during development we often want multiple versions (so we can make sure our code works with all of them) that are built in multiple configurations (different compilers, 32/64-bit, debug/release, and don't forget cross-compilation). And this is what build2 is optimized for.

Still, it often makes sense to use a system-installed library or tool instead of building it from source. Take libsqlite3 as an example: it is stable, available and works the same way pretty much everywhere – it probably doesn't make much sense to build it from source for every configuration.

The build2 build system will happily use a system-installed library; there is even pkg-config integration to extract additional information such as the location of headers, additional libraries to link, etc. Also, the package manager has a notion of system-installed packages (that sys: prefix) which allows you to instruct bpkg (and bdep) to assume the package is available from the system rather than building it from source. There is even integration with some system package managers meaning that bpkg will query the system package manager for the installed version of a sys: package and will attempt to install it if not already installed but available from the system package repository.

Why did you write build2 in C++ instead of another language (Rust, Python, etc)?

We needed a cross-platform (in particular, must work on Windows), efficient (nobody likes to wait for their builds), and stable/proven language that is available on all the major platforms without installing another "system" (Python, Java, Cygwin, etc). Put another way, a C/C++ toolchain is the base of every piece of software on every operating system in use today.

There is also this perspective: developing a build system and/or package manager is a multi-year, full-time project (build2 is going for almost 10 years now). If you are writing it in, say, Python, then this means you are a full-time Python developer trying to design and implement a major piece of C/C++ infrastructure for a language you don't actually use yourself for any serious development.

What are your plans for cppget.org?

We hope it will become The C++ Package Repository, the place where people publish theirs and look for other's C++ libraries and tools. If/when this happens, it will probably be a good idea to turn the control over to someone impartial, like Standard C++ Foundation (to this effect, we are running build2.org and cppget.org on completely separate infrastructures from the start).

Do you support cross-compilation?

Yes, cross-compilation was supported from day one with native compilation being just a special case (target is the same as host). Fun fact: in the early days, build2 could cross-compile to Windows before it could build natively.

Do you support binary packages?

The "binary package" term can mean many things but in this context it usually means one of two things: "binary distribution package" and "binary pre-built package".

A binary distribution package is a package for a Linux distribution such as Debian (.deb) or Fedora (.rpm), or another operating system, such as Mac OS (.pkg). The key property here is that the corresponding source package is built and then installed and what's packaged is the result of the installation.

In contrast, a binary pre-built package is essentially an archive of the build directory of the source package before the installation. It is meant to be dropped into your project's build directory with the result looking as if the package was built from source.

Now to answer the question for each meaning of binary package:

There is binary distribution package generation support for Debian (or alike, such as Ubuntu), Fedora (or alike, such as RHEL), and for other operating systems as installation archives.

At this stage there is no support for binary pre-built package but maybe in the future.

The problem with supporting binary pre-built packages is you have to make sure the binary package built on someone else's machine matches your environment precisely (if you think this is a Windows-only problem, check the recent GCC dual ABI mess). If we start cutting corners, we will end up in tears (and long mailing list threads). One interesting approach would be to dump the entire build system state into, say, an SQLite database and then ship that with the binary package. On the other end we can then load this database and verify that all the prerequisites (like system headers, compiler versions, etc) match.

Do you support VCS other than git?

Not at this stage but maybe in the future.

While the build system and the package manager are perfectly usable without a VCS, most of the project manager's functionality assumes the presence of a VCS (you can still publish and CI without it) and currently the only supported option is git. However, we are open to supporting other options if there is interest and willingness to do the work.

Can I run my own private/public repositories?

Yes, the entire toolchain, including the server-side web interface, is open source, licensed under the MIT License (see brep/INSTALL for details). We are also working on a VM with everything pre-installed to ease the setup process. Get in touch if you would be interested in trying it out.

Why did you call the build system driver b?

We found typing something like make -j8 too tedious so we've decided to be bold and call it just b (the -j8 part is unnecessary since the hardware concurrency is detected automatically).

In retrospect, this was probably a bad idea since, somewhat surprisingly, it seems to upset a few people. Yes, in the UNIX world, single letter commands are said to be reserved for user aliases (though in my GNU/Linux /usr/bin/ I have w, X, and, of course, [ ). Note, however, that we often have to run b on machines (like build bots) where one is unlikely to bother with aliases. And it would be nice to have the same short name available everywhere.

As another indication of this probably not being a big deal, the Fedora project accepted the /usr/bin/b executable in the build2 package without any fuss.

In any case, it is probably too late to change now (maybe in version 2). Also, if you really must, you can always add an executable prefix or suffix when installing build2 (see the install script help for details).

Why does Ninja start compiling faster than build2?

TL;DR: While there may be an initial delay before the first compilation command line is printed, useful work is being done and the overall build might even be slightly (because of file caching) or even significantly (because of more precise change detection) faster.

Ninja and build2 use different C/C++ compilation models when it comes to handling header dependencies (sets of headers each translation unit depends on that are used to decide what needs to be rebuilt).

Ninja extracts header dependencies as part of the compilation step itself. This allows it to issue the first compilation command pretty much immediately but requires all the headers to already exist and be up-to-date. Which means no auto-generated headers, at least not as part of the overall build (maybe as a pre-build step).

In contrast, build2 extracts header dependencies before starting the compilation. While it means there is a delay before you see the first compilation command, this approach has a number of advantages: It supports auto-generated source code as part of the build itself which can become essential for complex code generators like ORMs that may need to extract their own dependencies. This setup also opens the door for supporting C++ modules which in many ways are like auto-generated headers: the build system has to update all the binary module interfaces before it can start compiling a translation unit that imports them.

And this is not all: there are even more advantages but to fully appreciate them we need to understand how build2 sets this compilation pipeline up. At the compiler level, header dependency extraction is essentially a preprocessor run: it has to process #include directives recursively (and keeping track of which ones are #ifdef'ed out, etc) and produce the list of headers that have been included. So instead of extracting only the header list, build2 instructs the compiler to also save the (partially) preprocessed output. Later, if the translation unit needs to be rebuilt, the compiler is given this (partially) preprocessed file rather than the original.

So build2 does do some extra work compared to Ninja but the results of this work are not wasted. While one would still expect Ninja to be faster overall (one compiler invocation rather than two, no extra filesystem access to save the preprocessed file, etc), the reality is surprising: in certain cases the build2 approach results in a slightly faster overall build. The reason is probably the time-localized and cached access to all the headers; with build2 all this happens at the beginning of the build while with Ninja the access is spread over the entire build. See Separate Preprocess and Compile Performance for details.

Going back to other advantages, now that we have a (partially) preprocessed translation unit we can do a few more interesting things: we can perform more precise change detection by ignoring changes that do not affect the resulting token stream (or its debug information) such as changes to comments, macros that are not used, etc. In build2 this precise change detection is combined with the module dependency (import) extraction.

Finally, the (partially) preprocessed unit is a perfect candidate for shipping to a remote machine for distributed compilation. Since all the #include directives have been expanded we don't need to worry about the remote machine having exactly the same set of headers in exactly the same locations.

Note also that build2 itself is multi-threaded: it not only runs compilation jobs in parallel but also parallelizes internal tasks, such as parsing of the dependency output, calculation of the translation unit checksums, etc.