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



Often, we need to control the options passed the invoked tools. This is done with features. Consider an example:

# Declare a new free feature
import feature : feature ;
feature verbatim-options : : free ;

# Cause the value of the 'verbatim-options' feature to be
# available as 'OPTIONS' variable inside verbatim.inline-file
import toolset : flags ;
flags verbatim.inline-file OPTIONS <verbatim-options> ;

# Use the "OPTIONS" variable
actions inline-file
    "./" $(OPTIONS) $(<) $(>)

We first define a new feature. Then, the flags invocation says that whenever verbatin.inline-file action is run, the value of the verbatim-options feature will be added to the OPTIONS variable, and can be used inside the action body. You'd need to consult online help (--help) to find all the features of the toolset.flags rule.

Although you can define any set of features and interpret their values in any way, Boost.Build suggests the following coding standard for designing features.

Most features should have a fixed set of values that is portable (tool neutral) across the class of tools they are designed to work with. The user does not have to adjust the values for a exact tool. For example, <optimization>speed has the same meaning for all C++ compilers and the user does not have to worry about the exact options passed to the compiler's command line.

Besides such portable features there are special 'raw' features that allow the user to pass any value to the command line parameters for a particular tool, if so desired. For example, the <cxxflags> feature allows you to pass any command line options to a C++ compiler. The <include> feature allows you to pass any string preceded by -I and the interpretation is tool-specific. (See the section called “ Can I get capture external program output using a Boost.Jam variable? ” for an example of very smart usage of that feature). Of course one should always strive to use portable features, but these are still be provided as a backdoor just to make sure Boost.Build does not take away any control from the user.

Using portable features is a good idea because:

  • When a portable feature is given a fixed set of values, you can build your project with two different settings of the feature and Boost.Build will automatically use two different directories for generated files. Boost.Build does not try to separate targets built with different raw options.

  • Unlike with “raw” features, you don't need to use specific command-line flags in your Jamfile, and it will be more likely to work with other tools.

Steps for adding a feauture

Adding a feature requires three steps:

  1. Declaring a feature. For that, the "feature.feature" rule is used. You have to decide on the set of feature attributes:

    • if you want a feature value set for one target to automaticaly propagate to its dependant targets then make it “propagated”.

    • if a feature does not have a fixed list of values, it must be “free.” For example, the include feature is a free feature.

    • if a feature is used to refer to a path relative to the Jamfile, it must be a “path” feature. Such features will also get their values automatically converted to Boost Build's internal path representation. For example, include is a path feature.

    • if feature is used to refer to some target, it must be a “dependency” feature.

  2. Representing the feature value in a target-specific variable. Build actions are command templates modified by Boost.Jam variable expansions. The toolset.flags rule sets a target-specific variable to the value of a feature.

  3. Using the variable. The variable set in step 2 can be used in a build action to form command parameters or files.

Another example

Here's another example. Let's see how we can make a feature that refers to a target. For example, when linking dynamic libraries on Windows, one sometimes needs to specify a "DEF file", telling what functions should be exported. It would be nice to use this file like this:

        lib a : a.cpp : <def-file>a.def ;

Actually, this feature is already supported, but anyway...

  1. Since the feature refers to a target, it must be "dependency".

    feature def-file : : free dependency ;

  2. One of the toolsets that cares about DEF files is msvc. The following line should be added to it.

    flags DEF_FILE <def-file> ;

  3. Since the DEF_FILE variable is not used by the action, we need to modify it to be:

    actions link bind DEF_FILE
        $(.LD) .... /DEF:$(DEF_FILE) ....

    Note the bind DEF_FILE part. It tells bjam to translate the internal target name in DEF_FILE to a corresponding filename in the link action. Without it the expansion of $(DEF_FILE) would be a strange symbol that is not likely to make sense for the linker.

    We are almost done, but we should stop for a small workaround. Add the following code to msvc.jam

    rule link
        DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;

    This is needed to accomodate some bug in bjam, which hopefully will be fixed one day.

Variants and composite features.

Sometimes you want to create a shortcut for some set of features. For example, release is a value of <variant> and is a shortcut for a set of features.

It is possible to define your own build variants. For example:

variant crazy : <optimization>speed <inlining>off
                <debug-symbols>on <profiling>on ;

will define a new variant with the specified set of properties. You can also extend an existing variant:

variant super_release : release : <define>USE_ASM ;

In this case, super_release will expand to all properties specified by release, and the additional one you've specified.

You are not restricted to using the variant feature only. Here's example that defines a brand new feature:

feature parallelism : mpi fake none : composite link-incompatible ;
feature.compose <parallelism>mpi : <library>/mpi//mpi/<parallelism>none ;
feature.compose <parallelism>fake : <library>/mpi//fake/<parallelism>none ;

This will allow you to specify the value of feature parallelism, which will expand to link to the necessary library.