Libraries and Dependent Targets

| Home | Libraries | People | FAQ | More

## Libraries and Dependent Targets

TODO: need to make this section consistent with "examples-v2/libraries".

Targets that are "needed" by other targets are called dependencies of those other targets. The targets that need the other targets are called dependent targets.

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

    lib bar : bar.cpp ;

Then, to use this library in `src/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`. When linking the app executable, the appropriate version of `bar` will be built and linked in. What do we mean by "appropriate"? For example, suppose we build "app" with:

    bjam app optimization=full cxxflags=-w-8080

Which properties must be used to build `foo`? The answer is that some properties 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 <cxxflags> feature is not propagated: its value will be added as-is to compiler flags for "a.cpp", but won't affect "foo". There is still a couple of problems. First, the library probably has some headers which must be used when compiling "app.cpp". We could use requirements on "app" to add those includes, but then this work will be repeated for all programs which use "foo". A better solution is to modify util/foo/Jamfilie in this way:

    project
        : usage-requirements <include>.
        ;

    lib foo : foo.cpp ;

Usage requirements are requirements which are applied to dependents. In this case, <include> will be applied to all targets which use "foo" — i.e. targets which have "foo" either in sources or in dependency properties. You'd need to specify usage requirements only once, and programs which use "foo" don't have to care about include paths any longer. Or course, the path will be interpreted relatively to "util/foo" and will be adjusted according to the bjams invocation directory. For example, if building from project root, the final compiler's command line will contain `-Ilib/foo`.

The second problem is that we hardcode the path to library's Jamfile. Imagine it's hardcoded in 20 different places and we change the directory layout. The solution is to use project ids — symbolic names, not tied to directory layout. First, we assign a project id to Jamfile in util/foo:

    project foo
        : usage-requirements <include>.
        ;

Second, we use the project id to refer to the library in src/Jamfile:

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

The "/foo//bar" syntax is used to refer to target "foo" in project with global id "/foo" (the slash is used to specify global id). This way, users of "foo" do not depend on its location, only on id, which is supposedly stable. The only thing left, it to make sure that src/Jamfile knows the project id that it uses. We add to top/Jamfile the following line:

    use-project /foo : util/foo ;

Now, all projects can refer to "foo" using the symbolic name. If the library is moved somewhere, only a single line in the top-level Jamfile should be changed.