Using Boost.Test with Boost.Build
In my earlier post C++ Unit Testing With Boost.Test I used make to build my sample code — largely because that is what I am more familiar with. If you’re using Boost for testing, though, you should also consider using it for building. From what I’ve seen you get a lot of functionality for free with Boost.Build if you’re willing to climb the learning curve. In order to help, I’ve put together a simple tutorial that combines Boost.Test and Boost.Build.
Prerequisites
In this tutorial I’m assuming you have Boost installed already. If not, you can refer to my earlier post or the Boost Getting Started Guide.
Installing Boost.Build
If, like me, you installed Boost by using the package manager on your Linux box, you may still not have Boost.Build installed. On Debian-based systems, Boost.Build requires two extra packages:
$ sudo apt-get install bjam boost-build
The bjam package installs Boost’s variant of the jam build tool, whereas the boost-build package installs a set of bjam configurations that form the actual Boost.Build system.
If you’re not lucky enough to have a boost-build package or equivalent, you can get a pre-built bjam binary and sources for Boost.Build, see the official documentation for details.
Once you have everything set up, you should be able to run bjam -version and see similar to the following output:
$ bjam --version Boost.Build V2 (Milestone 12) Boost.Jam 03.1.16
If you don’t see details of the Boost.Build version then it is likely you have only installed bjam and not the full Boost.Build system.
Sample Projects
To demonstrate Boost.Build’s support for multiple projects in a single tree, I split my sample code into two pieces: a simple library, and the test code itself. The library consists of a single Number class, which is an entirely contrived wrapper around an int. The test code exercises this library, and thus needs to link against it.
Boost.Build isn’t particularly fussy about how you lay out your projects, so I went for a simple structure:
$ ls -R .: Jamroot number test ./number: Jamfile Number.cpp Number.hpp ./test: Jamfile NumberTest.cpp
The Jamroot and Jamfiles are build files used by Boost.Build. They are in the same format — the difference in name is used to indicate the top level of the project. Boost.Build subprojects inherit configuration from parent projects by searching up the directory tree for a Jamfile, and will stop when a Jamroot is reached.
Top Level
The top level Jamroot file is incredibly simple in this case:
use-project /libs/number : number ;
In fact this line isn’t even strictly necessary, but it is good practice. It assigns the symbolic name “/libs/number” to the project in the “number” subdirectory. It’s overkill for such a simple example, but this abstraction means our test project will have no dependency on the exact location of the number library. If we refactored and moved the library into a subdirectory called “math”, then we would only need to update the Jamroot.
Number Library
As mentioned above, the number library is a contrived wrapper around an int that I created simply for illustration. The interface for this library is defined in Number.hpp:
#ifndef MY_LIBRARY_H
#define MY_LIBRARY_H
#include <iostream>
class Number
{
public:
Number(int value);
bool operator==(const Number& other) const;
Number add(const Number& other) const;
Number subtract(const Number& other) const;
int getValue() const;
private:
int value;
};
std::ostream& operator<<(std::ostream& output, const Number& n);
#endif
Of greater interest is the Jamfile used to build the library:
project : usage-requirements <include>. ; lib number : Number.cpp ;
Note that the single “lib” line is all that is required to build the library. The lib rule is one of the core rules provided by Boost.Build, and follows its common syntax:
rule rule-name (
main-target-name :
sources + :
requirements * :
default-build * :
usage-requirements * )
So in this case we are instructing Boost.Build to create a library named “number” from the sources “Number.cpp”.
The project declaration, which adds usage-requirements, is a convenience for consumers of this library. This tells the build system that any project that uses the number library should have this directory “.” added to its include path. This makes it easy for those projects to include Number.hpp.
We can build the library by running bjam in the number directory:
$ bjam ...found 12 targets... ...updating 5 targets... MkDir1 ../number/bin MkDir1 ../number/bin/gcc-4.4.1 MkDir1 ../number/bin/gcc-4.4.1/debug gcc.compile.c++ ../number/bin/gcc-4.4.1/debug/Number.o gcc.link.dll ../number/bin/gcc-4.4.1/debug/libnumber.so ...updated 5 targets...
Note that by default Boost.Build produces a dynamic library, and outputs the built artifacts into configuration-specific subdirectories.
Test Project
Finally, our test project consists of a single source file, NumberTest.cpp, with a single test suite:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Number
#include <boost/test/unit_test.hpp>
#include <Number.hpp>
BOOST_AUTO_TEST_SUITE(NumberSuite)
BOOST_AUTO_TEST_CASE(checkPass)
{
BOOST_CHECK_EQUAL(Number(2).add(2), Number(4));
}
BOOST_AUTO_TEST_CASE(checkFailure)
{
BOOST_CHECK_EQUAL(Number(2).add(2), Number(5));
}
BOOST_AUTO_TEST_SUITE_END()
Note the definition of BOOST_TEST_DYN_LINK: this is essential to link against the Boost.Test dynamic library. Other than that the code is fairly self explanatory.
Again, the Jamfile is what we are really interested in here:
using testing ; lib boost_unit_test_framework ; run NumberTest.cpp /libs/number//number boost_unit_test_framework ;
Starting from the top, the “using testing” line includes Boost.Build’s support for Boost.Test. This support includes rules for building and running tests; for example it defines the “run” rule which is used later in the file.
The “lib” line declares a pre-built library (note that it has no sources) named “boost_unit_test_framework”. We use this later for linking against the Boost.Test dynamic library.
Finally, the “run” rule is used to define how to build an run a Boost.Test executable. The syntax for this rule is:
rule run (
sources + :
args * :
input-files * :
requirements * :
target-name ? :
default-build * )
In our sources we include both the source file and the two libraries that we require. Note that we refer to the number project using the symbolic name declared in our Jamroot.
To build and run the tests, we simply execute bjam in the test directory:
$ bjam ...found 29 targets... ...updating 8 targets... MkDir1 bin MkDir1 bin/NumberTest.test MkDir1 bin/NumberTest.test/gcc-4.4.1 MkDir1 bin/NumberTest.test/gcc-4.4.1/debug gcc.compile.c++ bin/NumberTest.test/gcc-4.4.1/debug/NumberTest.o gcc.link bin/NumberTest.test/gcc-4.4.1/debug/NumberTest testing.capture-output bin/NumberTest.test/gcc-4.4.1/debug/NumberTest.run ====== BEGIN OUTPUT ====== Running 2 test cases... NumberTest.cpp(18): error in "checkFailure": check Number(2).add(2) == Number(5) failed [4 != 5] *** 1 failure detected in test suite "Number" EXIT STATUS: 201 ====== END OUTPUT ====== <snipped diagnostics> ...failed testing.capture-output bin/NumberTest.test/gcc-4.4.1/debug/NumberTest.run... ...failed updating 1 target... ...skipped 1 target... ...updated 6 targets...
Note that the build fails as I have deliberately created a failing test case. The full output is somewhat longer due to the diagnostics given.
Wrap Up
That’s it! The impressive part is how simple it is to build two projects with an interdependency and run a test suite. In total the three build files include just six lines! And I haven’t even explored the fact that Boost.Build allows you to easily build across multiple platforms using various toolchains and configurations.
The hardest part is working through enough of the documentation to find out the few lines you need — hopefully this tutorial goes some way to removing that barrier.
This entry was posted on Friday, December 4th, 2009 at 4:23 am and is filed under Agile, Build, C++, Technology, Testing. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

March 5th, 2010 at 9:06 am
Thanks! Just the kind of clear and practical example I was looking for.