Generating a Single-Include C++ Header File Using Buck

4,336 reads

TL;DR

Use Buck to generate a single-include header file for your library. This provides your users with an option for rapid development, which can be optimized later. Take a look at a working example on GitHub.

Introduction

A common pattern in C++ libraries is to provide a header file that includes all of the other header files in that library.

For example, suppose we have a library with these headers:

To use the library, we need to include each header file that we require:

Alternatively, the library designer can provide a single-include header that does all of this for us:

mathutils.hpp

And all the user has to do is include that header:

Caveats

This approach is certainly more convenient, but there are also some downsides.

First, single-include headers might increase compilation times. If we are only using definitions from add.hpp , then including everything will require the parser and preprocessor to do more work than is really necessary.

Second, it obscures that headers that you actually depend upon. This can cause confusion when examining your include-graph to better understand your project, for example.

So those are the two big downsides, but if you are still iterating on your code, using a single-include is probably a good idea. Why optimize your includes before you are sure which headers you will need?

Implementation

OK, so as a library designer, it’s probably a good idea to provide a single-include header, but how can we generate one?

Buck build can help us here. In case you are not familiar with it, Buck is a high-performance build system from Facebook. The advantages of using Buck build over other solutions are:

Builds are reproducible

Build artefacts can be cached and shared

Buck is cross-platform

It works on hashes, not time-stamps, which prevents unnecessary rebuilds

And more…

Buck build scripts are written in Python, so we can take advantage of Python’s list and dictionary functions to generate the header. Let’s get started…

If you just want to go straight to a working example, then have a look at the GitHub repo.

First, we need to grab all of the header files in the library. Buck provides a useful utility function for this called subdir_glob .

Assuming this file layout…

and this script…

… the output will be:

Once we have the header mapping, we need to transform it into a C++ header. This is quite easy in Python:

Python’s join function is weird!

Then, we need create a genrule that takes the single_header string and writes it to a file:

The final step is to include the output of the genrule as a header in our cxx_library . Fortunately, Buck makes this easy by allowing us to address a rule’s output by its name:

However, we also want to include the non-generated headers; we need to merge these two dictionaries. Python does not provide a + operator for dictionaries, but we can write a small function that does the work:

So then our mathutils target will be:

If you build the library, then you will see the generated header in the build folder:

The best thing about this solution is that it is automatic. If you add an .hpp file to the include folder, then Buck will detect this automatically, and regenerate the header file.

Since You’re Here…

We created Buckaroo to make it easier to integrate C++ libraries. If you would like try it out, the best place to start is the documentation. You can browse the existing packages on Buckaroo.pm or request more over on the wishlist.

Tags