1.10. Building a Complex application Using Boost.Build

Problem

You wish to use Boost.Build to build an executable that depends on several static and dynamic libraries.

Solution

Follow these steps:

  1. For each library on which the executable depends—unless it is distributed as a prebuilt binary—create a Jamfile as described in Recipe 1.8 and Recipe 1.9.

  2. Create a Jamroot file in the directory where you want the executable to be created.

  3. In the file Jamroot, invoke the exe rule to declare an executable target. Specify your .cpp files and the library targets on which the executable depends as sources. Also, add properties of the form <include>path as sources, if necessary, to tell the compiler where to search for library headers.

  4. In the file Jamroot, invoke the install rule, specifying the properties <install-dependencies>on, <install-type>EXE, and <install-type>SHARED_LIB as requirements.

  5. Run bjam from the directory containing Jamroot as described in Recipe 1.7.

For example, to build an executable from the source files listed in Example 1-3, create a file named Jamroot in the directory hellobeatles as shown in Example 1-13.

Example 1-13. A Jamfile to build the executable hellobeatles.exe or hellobeatles

# Jamfile for project hellobeatles

exe hellobeatles
    : # sources
      ../johnpaul//libjohnpaul
      ../georgeringo//libgeorgeringo
      hellobeatles.cpp
    ;

install dist 
    : # sources
      hellobeatles
    : # requirements
      <install-dependencies>on
      <install-type>EXE
      <install-type>SHARED_LIB
      <location>.
    ;

Now enter:

> bjam hellobeatles

from the directory hellobeatles. This first builds the two projects on which the target hellobeatles depends, and then builds the target hellobeatles. Finally, enter:

> bjam dist

This copies the executable hellobeatles and the dynamic library georgeringo to the directory containing hellobeatles.cpp.

As discussed in Recipe 1.5, before you can run hellobeatles, you may need to place a copy of your toolset’s dynamic runtime library in a location where it can be found by the operating system.

Discussion

Library targets

The library targets on which a target depends are specified as sources using the notation path//target-name. In Recipe 1.8 and Recipe 1.9, I showed how to declare a target for a library to be built from source code by Boost.Build. If a library is available as a prebuilt binary, however, you can declare a target for it as follows:

lib target-name
    : 
    : <file>file-name
    ;

As explained in Recipe 1.7, most main targets correspond not to a single file but to collections of related files, such as the debug and release build of an executable. To declare a target for a prebuilt library that has several variants, you can use the following notation:

lib target-name
    : 
    : <file>file-name requirements
    ;

lib target-name
    : 
    : <file>other-file-name other-requirements
    ;

For example, debug and release variants of a prebuilt library might be declared as follows:

lib cryptolib
    : 
    : <file> ../libraries/cryptolib/cryptolib_debug.lib 
      <variant>debug
    ;

lib cryptolib
    : 
    : <file> ../libraries/cryptolib/cryptolib.lib
      <variant>release
    ;

If a prebuilt library is located in one the directories that is searched automatically by the linker, as described in Recipe 1.5, you can declare a target for it as follows:

lib target-name
    : 
    : <name>library-name 
    ;

Here, library-name is the name that should be passed to the linker, which may differ from the actual file name, as discussed in Recipe 1.5. To tell the linker to look in a particular directory, you can write

lib target-name
    : 
    : <name>library-name
      <search>library-path
    ;

Installation

A complex application may need to be installed together with a number of additional executables and dynamic libraries on which it depends. Rather than specifying all these files individually, you can use the install-dependencies features, which allows you to specify only the top-level executable target and the type of dependencies that should be installed. In Example 1-13, the requirement <install-dependencies>on turns on the install-dependencies feature, and the requirements <install-type>EXE and <install-type>SHARED_LIB tells Boost.Build to install all dependencies that are executables or shared libraries. Other possible values of the install-type feature include LIB and IMPORT_LIB.

Project organization

All three Jamfiles involved in building the executable hellobeatles are named Jamroot. This is fine in such a simple example, but in general it’s a good idea to organize a collection of Jamfiles hierarchically, with a single top-level Jamfile defining the project root. Arranging projects in this manner allows you to take advantage of some of Boost.Build’s more sophisticated features, such as allowing properties to be inherited by child projects. One way to accomplish this in the present case is to change the names of the Jamfiles in the directories johnpaul, georgeringo, and hellobeatles from Jamroot to Jamfile, and add to a Jamroot file in the parent directory with the following content:

# jamfile for example application

build-project hellobeatles  ;

The rule build-project simply tells bjam to build a given project, which can be specified either by pathname or by a symbolic identifier. If you change to the directory containing Jamroot and run bjam, the three child projects will be built.

Get C++ Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.