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
.
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
dependents. 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.
If you want all applications in some project to link to a certain
library, you can avoid having to specify 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 ;