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

Declaring Targets

A Main target is a user-defined named entity that can be built, for example an executable file. Declaring a main target is usually done using one of the main target rules described in the section called “Builtin rules”. The user can also declare custom main target rules as shown in the section called “Main target rules”.

Most main target rules in Boost.Build have the same common signature:

rule rule-name (
     main-target-name :
     sources + :
     requirements * :
     default-build * :
     usage-requirements * )
  • main-target-name is the name used to request the target on command line and to use it from other main targets. A main target name may contain alphanumeric characters, dashes (‘-’), and underscores (‘_’).
  • sources is the list of source files and other main targets that must be combined.
  • requirements is the list of properties that must always be present when this main target is built.
  • default-build is the list of properties that will be used unless some other value of the same feature is already specified, e.g. on the command line or by propagation from a dependent target.
  • usage-requirements is the list of properties that will be propagated to all main targets that use this one, i.e. to all its dependents.

Some main target rules have a different list of parameters as explicitly stated in their documentation.

The actual requirements for a target are obtained by refining the requirements of the project where the target is declared with the explicitly specified requirements. The same is true for usage-requirements. More details can be found in the section called “Property refinement”

Name

The name of main target has two purposes. First, it's used to refer to this target from other targets and from command line. Second, it's used to compute the names of the generated files. Typically, filenames are obtained from main target name by appending system-dependent suffixes and prefixes.

The name of a main target can contain alphanumeric characters, dashes, undescores and dots. The entire name is significant when resolving references from other targets. For determining filenames, only the part before the first dot is taken. For example:

obj test.release : test.cpp : <variant>release ;
obj test.debug : test.cpp : <variant>debug ;

will generate two files named test.obj (in two different directories), not two files named test.release.obj and test.debug.obj.

Sources

The list of sources specifies what should be processed to get the resulting targets. Most of the time, it's just a list of files. Sometimes, you'll want to automatically construct the list of source files rather than having to spell it out manually, in which case you can use the glob rule. Here are two examples:

exe a : a.cpp ;           # a.cpp is the only source file
exe b : [ glob *.cpp ] ;  # all .cpp files in this directory are sources

Unless you specify a file with an absolute path, the name is considered relative to the source directory — which is typically the directory where the Jamfile is located, but can be changed as described in the section called “Projects”.

The list of sources can also refer to other main targets. Targets in the same project can be referred to by name, while targets in other projects must be qualified with a directory or a symbolic project name. The directory/project name is separated from the target name by a double forward slash. There is no special syntax to distinguish the directory name from the project name—the part before the double slash is first looked up as project name, and then as directory name. For example:

lib helper : helper.cpp ;
exe a : a.cpp helper ;
# Since all project ids start with slash, ".." is a directory name.
exe b : b.cpp ..//utils ;
exe c : c.cpp /boost/program_options//program_options ;

The first exe uses the library defined in the same project. The second one uses some target (most likely a library) defined by a Jamfile one level higher. Finally, the third target uses a C++ Boost library, referring to it using its absolute symbolic name. More information about target references can be found in the section called “Dependent Targets” and the section called “Target identifiers and references”.

Requirements

Requirements are the properties that should always be present when building a target. Typically, they are includes and defines:

exe hello : hello.cpp : <include>/opt/boost <define>MY_DEBUG ;

There are a number of other features, listed in the section called “Builtin features”. For example if a library can only be built statically, or a file can't be compiled with optimization due to a compiler bug, one can use

lib util : util.cpp : <link>static ;
obj main : main.cpp : <optimization>off ;

Sometimes, particular relationships need to be maintained among a target's build properties. This can be achieved with conditional requirements. For example, you might want to set specific #defines when a library is built as shared, or when a target's release variant is built in release mode.

lib network : network.cpp
    : <link>shared:<define>NEWORK_LIB_SHARED
     <variant>release:<define>EXTRA_FAST
    ;

In the example above, whenever network is built with <link>shared, <define>NEWORK_LIB_SHARED will be in its properties, too.

You can use several properties in the condition, for example:

lib network : network.cpp
    : <toolset>gcc,<optimization>speed:<define>USE_INLINE_ASSEMBLER
    ;

A more powerful variant of conditional requirements is indirect conditional requirements. You can provide a rule that will be called with the current build properties and can compute additional properties to be added. For example:

lib network : network.cpp
    : <conditional>@my-rule
    ;
rule my-rule ( properties * )
{
    local result ;
    if <toolset>gcc <optimization>speed in $(properties)
    {
        result += <define>USE_INLINE_ASSEMBLER ;
    }
    return $(result) ;
}

This example is equivalent to the previous one, but for complex cases, indirect conditional requirements can be easier to write and understand.

Requirements explicitly specified for a target are usually combined with the requirements specified for the containing project. You can cause a target to completely ignore a specific project requirement using the syntax by adding a minus sign before the property, for example:

exe main : main.cpp : -<define>UNNECESSARY_DEFINE ;

This syntax is the only way to ignore free properties, such as defines, from a parent. It can be also useful for ordinary properties. Consider this example:

project test : requirements <threading>multi ;
exe test1 : test1.cpp ;
exe test2 : test2.cpp : <threading>single ;
exe test3 : test3.cpp : -<threading>multi ;

Here, test1 inherits the project requirements and will always be built in multi-threaded mode. The test2 target overrides the project's requirements and will always be built in single-threaded mode. In contrast, the test3 target removes a property from the project requirements and will be built either in single-threaded or multi-threaded mode depending on which variant is requested by the user.

Note that the removal of requirements is completely textual: you need to specify exactly the same property to remove it.

Default Build

The default-build parameter is a set of properties to be used if the build request does not otherwise specify a value for features in the set. For example:

exe hello : hello.cpp : : <threading>multi ;

would build a multi-threaded target unless the user explicitly requests a single-threaded version. The difference between the requirements and the default-build is that the requirements cannot be overridden in any way.

Additional Information

The ways a target is built can be so different that describing them using conditional requirements would be hard. For example, imagine that a library actually uses different source files depending on the toolset used to build it. We can express this situation using target alternatives:

lib demangler : dummy_demangler.cpp ;                # alternative 1
lib demangler : demangler_gcc.cpp : <toolset>gcc ;   # alternative 2
lib demangler : demangler_msvc.cpp : <toolset>msvc ; # alternative 3

In the example above, when built with gcc or msvc, demangler will use a source file specific to the toolset. Otherwise, it will use a generic source file, dummy_demangler.cpp.

It is possible to declare a target inline, i.e. the "sources" parameter may include calls to other main rules. For example:

exe hello : hello.cpp
    [ obj helpers : helpers.cpp : <optimization>off ] ;

Will cause "helpers.cpp" to be always compiled without optimization. When referring to an inline main target, its declared name must be prefixed by its parent target's name and two dots. In the example above, to build only helpers, one should run b2 hello..helpers.

When no target is requested on the command line, all targets in the current project will be built. If a target should be built only by explicit request, this can be expressed by the explicit rule:

explicit install_programs ;

PrevUpHomeNext