mirror of
https://github.com/boostorg/build.git
synced 2026-02-15 00:52:16 +00:00
864 lines
32 KiB
Plaintext
864 lines
32 KiB
Plaintext
[[bbv2.extender]]
|
|
= Extender Manual
|
|
|
|
[[bbv2.extender.intro]]
|
|
== Introduction
|
|
|
|
This section explains how to extend Boost.Build to accommodate your local
|
|
requirements -- primarily to add support for non-standard tools you
|
|
have. Before we start, be sure you have read and understood the concept
|
|
of metatarget, <<bbv2.overview.concepts>>, which is critical to
|
|
understanding the remaining material.
|
|
|
|
The current version of Boost.Build has three levels of targets, listed
|
|
below.
|
|
|
|
metatarget::
|
|
Object that is created from declarations in Jamfiles. May be called
|
|
with a set of properties to produce concrete targets.
|
|
concrete target::
|
|
Object that corresponds to a file or an action.
|
|
jam target::
|
|
Low-level concrete target that is specific to Boost.Jam build engine.
|
|
Essentially a string -- most often a name of file.
|
|
|
|
In most cases, you will only have to deal with concrete targets and the
|
|
process that creates concrete targets from metatargets. Extending
|
|
metatarget level is rarely required. The jam targets are typically only
|
|
used inside the command line patterns.
|
|
|
|
WARNING: All of the Boost.Jam target-related builtin functions, like
|
|
`DEPENDS` or `ALWAYS` operate on jam targets. Applying them to metatargets or
|
|
concrete targets has no effect.
|
|
|
|
[[bbv2.extender.overview.metatargets]]
|
|
=== Metatargets
|
|
|
|
Metatarget is an object that records information specified in Jamfile,
|
|
such as metatarget kind, name, sources and properties, and can be called
|
|
with specific properties to generate concrete targets. At the code level
|
|
it is represented by an instance of class derived from
|
|
link:#bbv2.reference.class.abstract-target[abstract-target].
|
|
footnote:[This name is historic, and will be eventually changed to
|
|
`metatarget`]
|
|
|
|
The link:#bbv2.reference.class.abstract-target.generate[generate] method
|
|
takes the build properties (as an instance of the
|
|
link:#bbv2.reference.class.property-set[property-set] class) and returns
|
|
a list containing:
|
|
|
|
* As front element -- Usage-requirements from this invocation (an
|
|
instance of link:#bbv2.reference.class.property-set[property-set])
|
|
|
|
* As subsequent elements -- created concrete targets ( instances of the
|
|
`virtual-target` class.)
|
|
|
|
It's possible to lookup a metatarget by target-id using the
|
|
`targets.resolve-reference` function, and the
|
|
`targets.generate-from-reference` function can both lookup and generate
|
|
a metatarget.
|
|
|
|
The link:#bbv2.reference.class.abstract-target[abstract-target] class
|
|
has three immediate derived classes:
|
|
|
|
* link:#bbv2.reference.class.project-target[project-target] that
|
|
corresponds to a project and is not intended for further subclassing.
|
|
The link:#bbv2.reference.class.project-target.generate[generate] method
|
|
of this class builds all targets in the project that are not marked as
|
|
explicit.
|
|
|
|
* link:#bbv2.reference.class.main-target[main-target] corresponds to a
|
|
target in a project and contains one or more target alternatives. This
|
|
class also should not be subclassed. The
|
|
link:#bbv2.reference.class.main-target.generate[generate] method of this
|
|
class selects an alternative to build, and calls the
|
|
link:#bbv2.reference.class.basic-target.generate[generate] method of
|
|
that alternative.
|
|
|
|
* link:#bbv2.reference.class.basic-target[basic-target] corresponds to a
|
|
specific target alternative. This is base class, with a number of
|
|
derived classes. The
|
|
link:#bbv2.reference.class.basic-target.generate[generate] method
|
|
processes the target requirements and requested build properties to
|
|
determine final properties for the target, builds all sources, and
|
|
finally calls the abstract
|
|
link:#bbv2.reference.class.basic-target.construct[construct] method with
|
|
the list of source virtual targets, and the final properties.
|
|
|
|
The instances of the
|
|
link:#bbv2.reference.class.project-target[project-target] and
|
|
link:#bbv2.reference.class.main-target[main-target] classes are created
|
|
implicitly -- when loading a new Jamfiles, or when a new target
|
|
alternative with as-yet unknown name is created. The instances of the
|
|
classes derived from
|
|
link:#bbv2.reference.class.basic-target[basic-target] are typically
|
|
created when Jamfile calls a metatarget rule, such as such as `exe`.
|
|
|
|
It it permissible to create a custom class derived from
|
|
link:#bbv2.reference.class.basic-target[basic-target] and create new
|
|
metatarget rule that creates instance of such target. However, in the
|
|
majority of cases, a specific subclass of
|
|
link:#bbv2.reference.class.basic-target[basic-target] --
|
|
link:#bbv2.reference.class.typed-target[typed-target] is used. That
|
|
class is associated with a type and relays to generators to construct
|
|
concrete targets of that type. This process will be explained below.
|
|
When a new type is declared, a new metatarget rule is automatically
|
|
defined. That rule creates new instance of type-target, associated with
|
|
that type.
|
|
|
|
[[bbv2.extender.overview.targets]]
|
|
=== Concrete targets
|
|
|
|
Concrete targets are represented by instance of classes derived from
|
|
`virtual-target`. The most commonly used subclass is `file-target`. A
|
|
file target is associated with an action that creates it -- an
|
|
instance of the `action` class. The action, in turn, hold a list of
|
|
source targets. It also holds the
|
|
link:#bbv2.reference.class.property-set[property-set] instance with the
|
|
build properties that should be used for the action.
|
|
|
|
Here's an example of creating a target from another target, `source`
|
|
|
|
[source,jam]
|
|
----
|
|
local a = [ new action $(source) : common.copy : $(property-set) ] ;
|
|
local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ;
|
|
----
|
|
|
|
The first line creates an instance of the `action` class. The first
|
|
parameter is the list of sources. The second parameter is the name a
|
|
jam-level link:#bbv2.overview.jam_language.actions[action]. The third
|
|
parameter is the property-set applying to this action. The second line
|
|
creates a target. We specify a name, a type and a project. We also pass
|
|
the action object created earlier. If the action creates several
|
|
targets, we can repeat the second line several times.
|
|
|
|
In some cases, code that creates concrete targets may be invoked more
|
|
than once with the same properties. Returning two different instances of
|
|
`file-target` that correspond to the same file clearly will result in
|
|
problems. Therefore, whenever returning targets you should pass them via
|
|
the `virtual-target.register` function, besides allowing Boost.Build to
|
|
track which virtual targets got created for each metatarget, this will
|
|
also replace targets with previously created identical ones, as
|
|
necessary.footnote:[This create-then-register pattern is caused by
|
|
limitations of the Boost.Jam language. Python port is likely to never
|
|
create duplicate targets.] Here are a couple of examples:
|
|
|
|
[source,jam]
|
|
----
|
|
return [ virtual-target.register $(t) ] ;
|
|
return [ sequence.transform virtual-target.register : $(targets) ] ;
|
|
----
|
|
|
|
[[bbv2.extender.overview.generators]]
|
|
=== Generators
|
|
|
|
In theory, every kind of metatarget in Boost.Build (like `exe`, `lib` or
|
|
`obj`) could be implemented by writing a new metatarget class that,
|
|
independently of the other code, figures what files to produce and what
|
|
commands to use. However, that would be rather inflexible. For example,
|
|
adding support for a new compiler would require editing several
|
|
metatargets.
|
|
|
|
In practice, most files have specific types, and most tools consume and
|
|
produce files of specific type. To take advantage of this fact,
|
|
Boost.Build defines concept of target type and generators generators,
|
|
and has special metatarget class
|
|
link:#bbv2.reference.class.typed-target[typed-target]. Target type is
|
|
merely an identifier. It is associated with a set of file extensions
|
|
that correspond to that type. Generator is an abstraction of a tool. It
|
|
advertises the types it produces and, if called with a set of input
|
|
target, tries to construct output targets of the advertised types.
|
|
Finally, link:#bbv2.reference.class.typed-target[typed-target] is
|
|
associated with specific target type, and relays the generator (or
|
|
generators) for that type.
|
|
|
|
A generator is an instance of a class derived from `generator`. The
|
|
`generator` class itself is suitable for common cases. You can define
|
|
derived classes for custom scenarios.
|
|
|
|
[[bbv2.extender.example]]
|
|
== Example: 1-to-1 generator
|
|
|
|
Say you're writing an application that generates {CPP} code. If you ever
|
|
did this, you know that it's not nice. Embedding large portions of {CPP}
|
|
code in string literals is very awkward. A much better solution is:
|
|
|
|
1. Write the template of the code to be generated, leaving placeholders
|
|
at the points that will change
|
|
2. Access the template in your application and replace placeholders
|
|
with appropriate text.
|
|
3. Write the result.
|
|
|
|
It's quite easy to achieve. You write special verbatim files that are
|
|
just {CPP}, except that the very first line of the file contains the name
|
|
of a variable that should be generated. A simple tool is created that
|
|
takes a verbatim file and creates a cpp file with a single `char*`
|
|
variable whose name is taken from the first line of the verbatim file
|
|
and whose value is the file's properly quoted content.
|
|
|
|
Let's see what Boost.Build can do.
|
|
|
|
First off, Boost.Build has no idea about "verbatim files". So, you must
|
|
register a new target type. The following code does it:
|
|
|
|
[source,jam]
|
|
----
|
|
import type ;
|
|
type.register VERBATIM : verbatim ;
|
|
----
|
|
|
|
The first parameter to
|
|
link:#bbv2.reference.modules.type.register[type.register] gives the name
|
|
of the declared type. By convention, it's uppercase. The second
|
|
parameter is the suffix for files of this type. So, if Boost.Build sees
|
|
`code.verbatim` in a list of sources, it knows that it's of type
|
|
`VERBATIM`.
|
|
|
|
Next, you tell Boost.Build that the verbatim files can be transformed
|
|
into {CPP} files in one build step. A generator is a template for a build
|
|
step that transforms targets of one type (or set of types) into another.
|
|
Our generator will be called `verbatim.inline-file`; it transforms
|
|
`VERBATIM` files into `CPP` files:
|
|
|
|
[source,jam]
|
|
----
|
|
import generators ;
|
|
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
|
|
----
|
|
|
|
Lastly, you have to inform Boost.Build about the shell commands used to
|
|
make that transformation. That's done with an `actions` declaration.
|
|
|
|
[source,jam]
|
|
----
|
|
actions inline-file
|
|
{
|
|
"./inline-file.py" $(<) $(>)
|
|
}
|
|
----
|
|
|
|
Now, we're ready to tie it all together. Put all the code above in file
|
|
`verbatim.jam`, add `import verbatim ;` to `Jamroot.jam`, and it's
|
|
possible to write the following in your Jamfile:
|
|
|
|
[source,jam]
|
|
----
|
|
exe codegen : codegen.cpp class_template.verbatim usage.verbatim ;
|
|
----
|
|
|
|
The listed verbatim files will be automatically converted into {CPP}
|
|
source files, compiled and then linked to the `codegen` executable.
|
|
|
|
In subsequent sections, we will extend this example, and review all the
|
|
mechanisms in detail. The complete code is available in the
|
|
`example/customization` directory.
|
|
|
|
[[bbv2.extending.targets]]
|
|
== Target types
|
|
|
|
The first thing we did in the link:#bbv2.extender.intro[introduction]
|
|
was declaring a new target type:
|
|
|
|
[source,jam]
|
|
----
|
|
import type ;
|
|
type.register VERBATIM : verbatim ;
|
|
----
|
|
|
|
The type is the most important property of a target. Boost.Build can
|
|
automatically generate necessary build actions only because you specify
|
|
the desired type (using the different main target rules), and because
|
|
Boost.Build can guess the type of sources from their extensions.
|
|
|
|
The first two parameters for the `type.register` rule are the name of
|
|
new type and the list of extensions associated with it. A file with an
|
|
extension from the list will have the given target type. In the case
|
|
where a target of the declared type is generated from other sources, the
|
|
first specified extension will be used.
|
|
|
|
Sometimes you want to change the suffix used for generated targets
|
|
depending on build properties, such as toolset. For example, some
|
|
compiler uses extension `elf` for executable files. You can use the
|
|
`type.set-generated-target-suffix` rule:
|
|
|
|
[source,jam]
|
|
----
|
|
type.set-generated-target-suffix EXE : <toolset>elf : elf ;
|
|
----
|
|
|
|
A new target type can be inherited from an existing one.
|
|
|
|
[source,jam]
|
|
----
|
|
type.register PLUGIN : : SHARED_LIB ;
|
|
----
|
|
|
|
The above code defines a new type derived from `SHARED_LIB`. Initially,
|
|
the new type inherits all the properties of the base type - in
|
|
particular generators and suffix. Typically, you'll change the new type
|
|
in some way. For example, using `type.set-generated-target-suffix` you
|
|
can set the suffix for the new type. Or you can write a special
|
|
generator for the new type. For example, it can generate additional
|
|
meta-information for the plugin. In either way, the `PLUGIN` type can be
|
|
used whenever `SHARED_LIB` can. For example, you can directly link
|
|
plugins to an application.
|
|
|
|
A type can be defined as "main", in which case Boost.Build will
|
|
automatically declare a main target rule for building targets of that
|
|
type. More details can be found
|
|
link:#bbv2.extending.rules.main-type[later].
|
|
|
|
[[bbv2.extending.scanners]]
|
|
== Scanners
|
|
|
|
Sometimes, a file can refer to other files via some include system. To
|
|
make Boost.Build track dependencies between included files, you need to
|
|
provide a scanner. The primary limitation is that only one scanner can
|
|
be assigned to a target type.
|
|
|
|
First, we need to declare a new class for the scanner:
|
|
|
|
[source,jam]
|
|
----
|
|
class verbatim-scanner : common-scanner
|
|
{
|
|
rule pattern ( )
|
|
{
|
|
return "//###include[ ]*\"([^\"]*)\"" ;
|
|
}
|
|
}
|
|
----
|
|
|
|
All the complex logic is in the `common-scanner` class, and you only
|
|
need to override the method that returns the regular expression to be
|
|
used for scanning. The parentheses in the regular expression indicate
|
|
which part of the string is the name of the included file. Only the
|
|
first parenthesized group in the regular expression will be recognized;
|
|
if you can't express everything you want that way, you can return
|
|
multiple regular expressions, each of which contains a parenthesized
|
|
group to be matched.
|
|
|
|
After that, we need to register our scanner class:
|
|
|
|
[source,jam]
|
|
----
|
|
scanner.register verbatim-scanner : include ;
|
|
----
|
|
|
|
The value of the second parameter, in this case `include`, specifies the
|
|
properties that contain the list of paths that should be searched for
|
|
the included files.
|
|
|
|
Finally, we assign the new scanner to the `VERBATIM` target type:
|
|
|
|
[source,jam]
|
|
----
|
|
type.set-scanner VERBATIM : verbatim-scanner ;
|
|
----
|
|
|
|
That's enough for scanning include dependencies.
|
|
|
|
[[bbv2.extending.tools]]
|
|
== Tools and generators
|
|
|
|
This section will describe how Boost.Build can be extended to support
|
|
new tools.
|
|
|
|
For each additional tool, a Boost.Build object called generator must be
|
|
created. That object has specific types of targets that it accepts and
|
|
produces. Using that information, Boost.Build is able to automatically
|
|
invoke the generator. For example, if you declare a generator that takes
|
|
a target of the type `D` and produces a target of the type `OBJ`, when
|
|
placing a file with extension `.d` in a list of sources will cause
|
|
Boost.Build to invoke your generator, and then to link the resulting
|
|
object file into an application. (Of course, this requires that you
|
|
specify that the `.d` extension corresponds to the `D` type.)
|
|
|
|
Each generator should be an instance of a class derived from the
|
|
`generator` class. In the simplest case, you don't need to create a
|
|
derived class, but simply create an instance of the `generator` class.
|
|
Let's review the example we've seen in the
|
|
link:#bbv2.extender.intro[introduction].
|
|
|
|
[source,jam]
|
|
----
|
|
import generators ;
|
|
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
|
|
actions inline-file
|
|
{
|
|
"./inline-file.py" $(<) $(>)
|
|
}
|
|
----
|
|
|
|
We declare a standard generator, specifying its id, the source type and
|
|
the target type. When invoked, the generator will create a target of
|
|
type `CPP` with a source target of type `VERBATIM` as the only source.
|
|
But what command will be used to actually generate the file? In
|
|
Boost.Build, actions are specified using named "actions" blocks and the
|
|
name of the action block should be specified when creating targets. By
|
|
convention, generators use the same name of the action block as their
|
|
own id. So, in above example, the "inline-file" actions block will be
|
|
used to convert the source into the target.
|
|
|
|
There are two primary kinds of generators: standard and composing, which
|
|
are registered with the `generators.register-standard` and the
|
|
`generators.register-composing` rules, respectively. For example:
|
|
|
|
[source,jam]
|
|
----
|
|
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
|
|
generators.register-composing mex.mex : CPP LIB : MEX ;
|
|
----
|
|
|
|
The first (standard) generator takes a _single_ source of type
|
|
`VERBATIM` and produces a result. The second (composing) generator takes
|
|
any number of sources, which can have either the `CPP` or the `LIB`
|
|
type. Composing generators are typically used for generating top-level
|
|
target type. For example, the first generator invoked when building an
|
|
`exe` target is a composing generator corresponding to the proper
|
|
linker.
|
|
|
|
You should also know about two specific functions for registering
|
|
generators: `generators.register-c-compiler` and
|
|
`generators.register-linker`. The first sets up header dependency
|
|
scanning for C files, and the seconds handles various complexities like
|
|
searched libraries. For that reason, you should always use those
|
|
functions when adding support for compilers and linkers.
|
|
|
|
(Need a note about UNIX)
|
|
|
|
*Custom generator classes*
|
|
|
|
The standard generators allows you to specify source and target types,
|
|
an action, and a set of flags. If you need anything more complex, you
|
|
need to create a new generator class with your own logic. Then, you have
|
|
to create an instance of that class and register it. Here's an example
|
|
how you can create your own generator class:
|
|
|
|
[source,jam]
|
|
----
|
|
class custom-generator : generator
|
|
{
|
|
rule __init__ ( * : * )
|
|
{
|
|
generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
|
}
|
|
|
|
}
|
|
|
|
generators.register
|
|
[ new custom-generator verbatim.inline-file : VERBATIM : CPP ] ;
|
|
----
|
|
|
|
This generator will work exactly like the `verbatim.inline-file`
|
|
generator we've defined above, but it's possible to customize the
|
|
behavior by overriding methods of the `generator` class.
|
|
|
|
There are two methods of interest. The `run` method is responsible for
|
|
the overall process - it takes a number of source targets, converts them
|
|
to the right types, and creates the result. The `generated-targets`
|
|
method is called when all sources are converted to the right types to
|
|
actually create the result.
|
|
|
|
The `generated-targets` method can be overridden when you want to add
|
|
additional properties to the generated targets or use additional
|
|
sources. For a real-life example, suppose you have a program analysis
|
|
tool that should be given a name of executable and the list of all
|
|
sources. Naturally, you don't want to list all source files manually.
|
|
Here's how the `generated-targets` method can find the list of sources
|
|
automatically:
|
|
|
|
[source,jam]
|
|
----
|
|
class itrace-generator : generator {
|
|
...
|
|
rule generated-targets ( sources + : property-set : project name ? )
|
|
{
|
|
local leaves ;
|
|
local temp = [ virtual-target.traverse $(sources[1]) : : include-sources ] ;
|
|
for local t in $(temp)
|
|
{
|
|
if ! [ $(t).action ]
|
|
{
|
|
leaves += $(t) ;
|
|
}
|
|
}
|
|
return [ generator.generated-targets $(sources) $(leafs)
|
|
: $(property-set) : $(project) $(name) ] ;
|
|
}
|
|
}
|
|
generators.register [ new itrace-generator nm.itrace : EXE : ITRACE ] ;
|
|
----
|
|
|
|
The `generated-targets` method will be called with a single source
|
|
target of type `EXE`. The call to `virtual-target.traverse` will return
|
|
all targets the executable depends on, and we further find files that
|
|
are not produced from anything. The found targets are added to the
|
|
sources.
|
|
|
|
The `run` method can be overridden to completely customize the way the
|
|
generator works. In particular, the conversion of sources to the desired
|
|
types can be completely customized. Here's another real example. Tests
|
|
for the Boost Python library usually consist of two parts: a Python
|
|
program and a {CPP} file. The {CPP} file is compiled to Python extension
|
|
that is loaded by the Python program. But in the likely case that both
|
|
files have the same name, the created Python extension must be renamed.
|
|
Otherwise, the Python program will import itself, not the extension.
|
|
Here's how it can be done:
|
|
|
|
[source,jam]
|
|
----
|
|
rule run ( project name ? : property-set : sources * )
|
|
{
|
|
local python ;
|
|
for local s in $(sources)
|
|
{
|
|
if [ $(s).type ] = PY
|
|
{
|
|
python = $(s) ;
|
|
}
|
|
}
|
|
|
|
local libs ;
|
|
for local s in $(sources)
|
|
{
|
|
if [ type.is-derived [ $(s).type ] LIB ]
|
|
{
|
|
libs += $(s) ;
|
|
}
|
|
}
|
|
|
|
local new-sources ;
|
|
for local s in $(sources)
|
|
{
|
|
if [ type.is-derived [ $(s).type ] CPP ]
|
|
{
|
|
local name = [ $(s).name ] ; # get the target's basename
|
|
if $(name) = [ $(python).name ]
|
|
{
|
|
name = $(name)_ext ; # rename the target
|
|
}
|
|
new-sources += [ generators.construct $(project) $(name) :
|
|
PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ;
|
|
}
|
|
}
|
|
|
|
result = [ construct-result $(python) $(new-sources) : $(project) $(name)
|
|
: $(property-set) ] ;
|
|
}
|
|
----
|
|
|
|
First, we separate all source into python files, libraries and {CPP}
|
|
sources. For each {CPP} source we create a separate Python extension by
|
|
calling `generators.construct` and passing the {CPP} source and the
|
|
libraries. At this point, we also change the extension's name, if
|
|
necessary.
|
|
|
|
[[bbv2.extending.features]]
|
|
== Features
|
|
|
|
Often, we need to control the options passed the invoked tools. This is
|
|
done with features. Consider an example:
|
|
|
|
[source,jam]
|
|
----
|
|
# 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
|
|
{
|
|
"./inline-file.py" $(OPTIONS) $(<) $(>)
|
|
}
|
|
----
|
|
|
|
We first define a new feature. Then, the `flags` invocation says that
|
|
whenever verbatim.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 {CPP} 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 {CPP} compiler. The
|
|
`<include>` feature allows you to pass any string preceded by `-I` and
|
|
the interpretation is tool-specific. (See <<bbv2.faq.external>>
|
|
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 feature*
|
|
|
|
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
|
|
link:#bbv2.reference.features.attributes[feature attributes]:
|
|
* if you want a feature value set for one target to automatically
|
|
propagate to its dependent 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:
|
|
|
|
[source,jam]
|
|
----
|
|
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 msvc.link DEF_FILE <def-file> ;
|
|
----
|
|
|
|
3. Since the DEF_FILE variable is not used by the msvc.link 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 Boost.Build 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, except for adding the following code to `msvc.jam`:
|
|
+
|
|
----
|
|
rule link
|
|
{
|
|
DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
|
|
}
|
|
----
|
|
+
|
|
This is a workaround for a bug in Boost.Build engine, which will
|
|
hopefully 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:
|
|
|
|
[source,jam]
|
|
----
|
|
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:
|
|
|
|
[source,jam]
|
|
----
|
|
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:
|
|
|
|
[source,jam]
|
|
----
|
|
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.
|
|
|
|
[[bbv2.extending.rules]]
|
|
== Main target rules
|
|
|
|
A main target rule (e.g “link:#bbv2.tasks.programs[exe]” Or
|
|
“link:#bbv2.tasks.libraries[lib]”) creates a top-level target. It's
|
|
quite likely that you'll want to declare your own and there are two ways
|
|
to do that.
|
|
|
|
[[bbv2.extending.rules.main-type]]The first way applies when your target rule
|
|
should just produce a target
|
|
of specific type. In that case, a rule is already defined for you! When
|
|
you define a new type, Boost.Build automatically defines a corresponding
|
|
rule. The name of the rule is obtained from the name of the type, by
|
|
down-casing all letters and replacing underscores with dashes. For
|
|
example, if you create a module `obfuscate.jam` containing:
|
|
|
|
[source,jam]
|
|
----
|
|
import type ;
|
|
type.register OBFUSCATED_CPP : ocpp ;
|
|
|
|
import generators ;
|
|
generators.register-standard obfuscate.file : CPP : OBFUSCATED_CPP ;
|
|
----
|
|
|
|
and import that module, you'll be able to use the rule "obfuscated-cpp"
|
|
in Jamfiles, which will convert source to the OBFUSCATED_CPP type.
|
|
|
|
The second way is to write a wrapper rule that calls any of the existing
|
|
rules. For example, suppose you have only one library per directory and
|
|
want all cpp files in the directory to be compiled into that library.
|
|
You can achieve this effect using:
|
|
|
|
[source,jam]
|
|
----
|
|
lib codegen : [ glob *.cpp ] ;
|
|
----
|
|
|
|
If you want to make it even simpler, you could add the following
|
|
definition to the `Jamroot.jam` file:
|
|
|
|
[source,jam]
|
|
----
|
|
rule glib ( name : extra-sources * : requirements * )
|
|
{
|
|
lib $(name) : [ glob *.cpp ] $(extra-sources) : $(requirements) ;
|
|
}
|
|
----
|
|
|
|
allowing you to reduce the Jamfile to just
|
|
|
|
[source,jam]
|
|
----
|
|
glib codegen ;
|
|
----
|
|
|
|
Note that because you can associate a custom generator with a target
|
|
type, the logic of building can be rather complicated. For example, the
|
|
`boostbook` module declares a target type `BOOSTBOOK_MAIN` and a custom
|
|
generator for that type. You can use that as example if your main target
|
|
rule is non-trivial.
|
|
|
|
[[bbv2.extending.toolset_modules]]
|
|
== Toolset modules
|
|
|
|
If your extensions will be used only on one project, they can be placed
|
|
in a separate `.jam` file and imported by your `Jamroot.jam`. If the
|
|
extensions will be used on many projects, users will thank you for a
|
|
finishing touch.
|
|
|
|
The `using` rule provides a standard mechanism for loading and
|
|
configuring extensions. To make it work, your module should provide an
|
|
`init` rule. The rule will be called with the same parameters that were
|
|
passed to the `using` rule. The set of allowed parameters is determined
|
|
by you. For example, you can allow the user to specify paths, tool
|
|
versions, and other options.
|
|
|
|
Here are some guidelines that help to make Boost.Build more consistent:
|
|
|
|
* The `init` rule should never fail. Even if the user provided an
|
|
incorrect path, you should emit a warning and go on. Configuration may
|
|
be shared between different machines, and wrong values on one machine
|
|
can be OK on another.
|
|
|
|
* Prefer specifying the command to be executed to specifying the tool's
|
|
installation path. First of all, this gives more control: it's possible
|
|
to specify
|
|
+
|
|
----
|
|
/usr/bin/g++-snapshot
|
|
time g++
|
|
----
|
|
+
|
|
as the command. Second, while some tools have a logical "installation
|
|
root", it's better if the user doesn't have to remember whether a
|
|
specific tool requires a full command or a path.
|
|
|
|
* Check for multiple initialization. A user can try to initialize the
|
|
module several times. You need to check for this and decide what to do.
|
|
Typically, unless you support several versions of a tool, duplicate
|
|
initialization is a user error. If the tool's version can be specified
|
|
during initialization, make sure the version is either always specified,
|
|
or never specified (in which case the tool is initialized only once). For
|
|
example, if you allow:
|
|
+
|
|
----
|
|
using yfc ;
|
|
using yfc : 3.3 ;
|
|
using yfc : 3.4 ;
|
|
----
|
|
+
|
|
Then it's not clear if the first initialization corresponds to version
|
|
3.3 of the tool, version 3.4 of the tool, or some other version. This
|
|
can lead to building twice with the same version.
|
|
|
|
* If possible, `init` must be callable with no parameters. In which
|
|
case, it should try to autodetect all the necessary information, for
|
|
example, by looking for a tool in PATH or in common installation
|
|
locations. Often this is possible and allows the user to simply write:
|
|
+
|
|
----
|
|
using yfc ;
|
|
----
|
|
|
|
* Consider using facilities in the `tools/common` module. You can take a
|
|
look at how `tools/gcc.jam` uses that module in the `init` rule.
|