Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Dependent Targets

When building a target X that depends on first building another target Y (such as a library that must be linked with X), Y is called a dependency of X and X is termed a dependent of Y.

To get a feeling of target dependencies, let's continue the above example and see how top/app/Jamfile can use libraries from top/util/foo. If top/util/foo/Jamfile contains

lib bar : bar.cpp ;

then to use this library in top/app/Jamfile, we can write:

exe app : app.cpp ../util/foo//bar ;

While app.cpp refers to a regular source file, ../util/foo//bar is a reference to another target: a library bar declared in the Jamfile at ../util/foo.

[Tip] Tip

Some other build system have special syntax for listing dependent libraries, for example LIBS variable. In Boost.Build, you just add the library to the list of sources.

Suppose we build app with:

b2 app optimization=full define=USE_ASM
    

Which properties will be used to build foo? The answer is that some features are propagated—Boost.Build attempts to use dependencies with the same value of propagated features. The <optimization> feature is propagated, so both app and foo will be compiled with full optimization. But <define> is not propagated: its value will be added as-is to the compiler flags for a.cpp, but won't affect foo.

Let's improve this project further. The library probably has some headers that must be used when compiling app.cpp. We could manually add the necessary #include paths to app's requirements as values of the <include> feature, but then this work will be repeated for all programs that use foo. A better solution is to modify util/foo/Jamfile in this way:

project
    : usage-requirements <include>.
    ;

lib foo : foo.cpp ;

Usage requirements are applied not to the target being declared but to its dependants. In this case, <include>. will be applied to all targets that directly depend on foo.

Another improvement is using symbolic identifiers to refer to the library, as opposed to Jamfile location. In a large project, a library can be used by many targets, and if they all use Jamfile location, a change in directory organization entails much work. The solution is to use project ids—symbolic names not tied to directory layout. First, we need to assign a project id by adding this code to Jamroot:

use-project /library-example/foo : util/foo ;

Second, we modify app/Jamfile to use the project id:

exe app : app.cpp /library-example/foo//bar ;

The /library-example/foo//bar syntax is used to refer to the target bar in the project with id /library-example/foo. We've achieved our goal—if the library is moved to a different directory, only Jamroot must be modified. Note that project ids are global—two Jamfiles are not allowed to assign the same project id to different directories.

[Tip] Tip

If you want all applications in some project to link to a certain library, you can avoid having to specify it directly the sources of every target by using the <library> property. For example, if /boost/filesystem//fs should be linked to all applications in your project, you can add <library>/boost/filesystem//fs to the project's requirements, like this:

project
   : requirements <library>/boost/filesystem//fs
   ;
PrevUpHomeNext