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”
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
.
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 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>NETWORK_LIB_SHARED
<variant>release:<define>EXTRA_FAST
;
In the example above, whenever network
is
built with <link>shared
,
<define>NETWORK_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.
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.
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 ;