Building External Libraries with Boost Build
I recently ran into the problem of having to use an external library not available for our distribution. Of course, it would be possible to build Debian or RPM packages, deploy them, and then link against those. However, building sanely-behaving library packages is tricky.
The easier way is to include the source distribution in a sub-folder
of the project and integrate it into our build system, Boost
Build. It seemed prohibitive to
create Boost Build rules to build the library from scratch (from *.cpp
to *.a
/*.so
)
because the library had fairly complex build requirements.
The way to go was to leverage the library's own build
system.
Easier said than done. I only arrived at a fully integrated solution after seeking help on the Boost Build Mailing List (thanks Steven, Vladimir). This is the core of it:
path-constant LIBSQUARE_DIR : . ;
make libsquare.a
: [ glob-tree *.c *.cpp *.h : generated-file.c gen-*.cpp ]
: @build-libsquare
;
actions build-libsquare
{
( cd $(LIBSQUARE_DIR) && make )
cp $(LIBSQUARE_DIR)/libsquare.a $(<)
}
alias libsquare
: libsquare.a # sources
: # requirements
: # default-build
: <include>$(LIBSQUARE_DIR) <dependency>libsquare.a # usage-requirements
;
The first line defines a filesystem constant for the library directory, so the build process becomes independent of the working directory.
The next few lines tell Boost Build about a library called libsquare.a
, which
is built by calling make
. The cp
command copies the library to the target
location expected by Boost Build ($(<)
). glob-tree
defines source files
that should trigger library rebuilds when changed, taking care to exclude files
generated by the build process itself.
The alias
command defines a Boost Build target to link against
the library. The real magic is the
<dependency>libsquare.a
directive: it is required because the library
build may produce header files used by client code. Thus,
build-libsquare
must run before compiling any dependent
C++ files. Adding libsquare.a
to an executable's dependency list
won't quite enforce this: it only builds libsquare.a
in time
for the linking step (*.o
→ executable
), but not for the compilation
(*.cpp
→ *.o
). In contrast, the <dependency>libsquare.a
directive
propagates to all dependent build steps, including the compilation, and induces
the required dependencies.
I created an example project on GitHub that
demonstrates this. It builds a simple executable relying on a library
built via make
. The process is fully integrated in the Boost Build framework
and triggered by a single call to bjam
(or bb2
).
If you need something along these lines give this solution a try!