![]() |
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 target types”. 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 propogation 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, their documentation explicitly says so.
The actual requirements for a target are obtained by refining requirements of the project where a 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.
Name of main target can contain alphanumeral characters, dash, undescore and dot. 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 double slash. There's no special syntax to distinguish directory name from project name—the part before 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 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 library) defined by Jamfile one level higher. Finally, the third target uses some C++ Boost library, referring to it by 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 is 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 ;
More powerfull variant of conditional requirements is indirect conditional requiremens. 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.
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 in unless the user explicitly requests a single-threaded version. The difference between requirements and default-build is that requirements cannot be overriden 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
bjam 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 ;