mirror of
https://github.com/boostorg/build.git
synced 2026-02-02 20:52:13 +00:00
367 lines
16 KiB
HTML
367 lines
16 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
|
"http://www.w3.org/TR/html4/strict.dtd">
|
|
|
|
<html>
|
|
<head>
|
|
<meta name="generator" content=
|
|
"HTML Tidy for Linux/x86 (vers 1st April 2002), see www.w3.org">
|
|
<!--tidy options: -i -wrap 78 -->
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<link rel="stylesheet" type="text/css" href="../../boost.css">
|
|
|
|
<title>Boost.Build v2 architecture</title>
|
|
|
|
<style type="text/css">
|
|
hr { color: black }
|
|
p.revision { text-align: right; font-style: italic }
|
|
pre.code { margin-left: 2em }
|
|
pre.output { margin-left: 2em }
|
|
img.banner { border: 0; float: left }
|
|
h1 { text-align: right }
|
|
br.clear { clear: left }
|
|
div.alert { color: red }
|
|
table { align: center; border: thin; }
|
|
</style>
|
|
</head>
|
|
<!-- Things yet to document:
|
|
- build request, build request expansion and directly requested targets
|
|
- conditional properties
|
|
-->
|
|
|
|
<body>
|
|
<p><a href="../../../../index.htm"><img class="banner" height="86" width="277"
|
|
alt="C++ Boost" src="../c++boost.gif"></a></p>
|
|
|
|
<h1>Boost.Build v2 architecture<br class="clear">
|
|
</h1>
|
|
<hr>
|
|
|
|
<div class="alert">
|
|
<p>This document is work-in progress. Don't expect much from it
|
|
yet.</p>
|
|
</div>
|
|
|
|
<dl class="page-index">
|
|
<dt><a href="#dependencies">Dependency scanning</a></dt>
|
|
</dl>
|
|
<hr>
|
|
|
|
<h2><a name="targets">Targets</a></h2>
|
|
|
|
<p>There are two user-visible kinds of targets in Boost.Build. First are
|
|
"abstract" — they correspond to things declared by user, for
|
|
example, projects and executable files. The primary thing about abstract
|
|
target is that it's possible to request them to be build with a
|
|
particular values of some properties. Each combination of properties may
|
|
possible yield different set of real file, so abstract target do not have
|
|
a direct correspondence with files.</p>
|
|
|
|
<p>File targets, on the contary, are associated with concrete files.
|
|
Dependency graphs for abstract targets with specific properties are
|
|
constructed from file targets. User has no was to create file targets,
|
|
however it can specify rules that detect file type for sources, and also
|
|
rules for transforming between file targets of different types. That
|
|
information is used in constructing dependency graph, as desribed in the
|
|
<a href="#generators">next section</a>. <b>Note:</b>File targets are not
|
|
the same as targets in Jam sense; the latter are created from file
|
|
targets at the latest possible moment. <b>Note:</b>"File target" is a
|
|
proposed name for what we call virtual targets. It it more understandable
|
|
by users, but has one problem: virtual targets can potentially be
|
|
"phony", and not correspond to any file.</p>
|
|
|
|
<h2 id="dependency_scanning">Dependency scanning</h2>
|
|
|
|
<p>Dependency scanning is the process of finding implicit dependencies,
|
|
like "#include" statements in C++. The requirements for right dependency
|
|
scanning mechanism are:</p>
|
|
|
|
<ul>
|
|
<li>Support for different scanning algorithms. C++ and XML have quite
|
|
different syntax for includes and rules for looking up included
|
|
files.</li>
|
|
|
|
<li>Ability to scan the same file several times. For example, single
|
|
C++ file can be compiled with different include paths.</li>
|
|
|
|
<li>Proper detection of dependencies on generated files.</li>
|
|
|
|
<li>Proper detection of dependencies from generated file.</li>
|
|
</ul>
|
|
|
|
<h3>Support for different scanning algorithms</h3>
|
|
|
|
<p>Different scanning algorithm are encapsulated by objects called
|
|
"scanners". Please see the documentation for "scanner" module for more
|
|
details.</p>
|
|
|
|
<h3>Ability to scan the same file several times</h3>
|
|
|
|
<p>As said above, it's possible to compile a C++ file twice, with
|
|
different include paths. Therefore, include dependencies for those
|
|
compilations can be different. The problem is that bjam does not allow
|
|
several scans of the same target.</p>
|
|
|
|
<p>The solution in Boost.Build is straigtforward. When a virtual target
|
|
is converted to bjam target (via <tt>virtual-target.actualize</tt>
|
|
method), we specify the scanner object to be used. The actualize method
|
|
will create different bjam targets for different scanners.</p>
|
|
|
|
<p>All targets with specific scanner are made dependent on target without
|
|
scanner, which target is always created. This is done in case the target
|
|
is updated. The updating action will be associated with target without
|
|
scanner, but if sources for that action are touched, all targets —
|
|
with scanner and without should be considered outdated.</p>
|
|
|
|
<p>For example, assume that "a.cpp" is compiled by two compilers with
|
|
different include path. It's also copied into some install location. In
|
|
turn, it's produced from "a.verbatim". The dependency graph will look
|
|
like:</p>
|
|
<pre>
|
|
a.o (<toolset>gcc) <--(compile)-- a.cpp (scanner1) ----+
|
|
a.o (<toolset>msvc) <--(compile)-- a.cpp (scanner2) ----|
|
|
a.cpp (installed copy) <--(copy) ----------------------- a.cpp (no scanner)
|
|
^
|
|
|
|
|
a.verbose --------------------------------+
|
|
|
|
</pre>
|
|
|
|
<h3>Proper detection of dependencies on generated files.</h3>
|
|
|
|
<p>This requirement breaks down to the following ones.</p>
|
|
|
|
<ol>
|
|
<li>If when compiling "a.cpp" there's include of "a.h", the "dir"
|
|
directory is in include path, and a target called "a.h" will be
|
|
generated to "dir", then bjam should discover the include, and create
|
|
"a.h" before compiling "a.cpp".</li>
|
|
|
|
<li>Since almost always Boost.Build generates targets to a "bin"
|
|
directory, it should be supported as well. I.e. in the scanario above,
|
|
Jamfile in "dir" might create a main target, which generates "a.h". The
|
|
file will be generated to "dir/bin" directory, but we still have to
|
|
recornize the dependency.</li>
|
|
</ol>
|
|
|
|
<p>The first requirement means that when determining what "a.h" means,
|
|
when found in "a.cpp", we have to iterate over all directories in include
|
|
paths, checking for each one:</p>
|
|
|
|
<ol>
|
|
<li>If there's file "a.h" in that directory, or</li>
|
|
|
|
<li>If there's a target called "a.h", which will be generated to that
|
|
directory.</li>
|
|
</ol>
|
|
|
|
<p>Classic Jam has built-in facilities for point (1) above, but that's
|
|
not enough. It's hard to implement the right semantic without builtin
|
|
support. For example, we could try to check if there's targer called
|
|
"a.h" somewhere in dependency graph, and add a dependency to it. The
|
|
problem is that without search in include path, the semantic may be
|
|
incorrect. For example, one can have an action which generated some
|
|
"dummy" header, for system which don't have the native one. Naturally, we
|
|
don't want to depend on that generated header on platforms where native
|
|
one is included.</p>
|
|
|
|
<p>There are two design choices for builtin support. Suppose we have
|
|
files a.cpp and b.cpp, and each one includes header.h, generated by some
|
|
action. Dependency graph created by classic jam would look like:</p>
|
|
<pre>
|
|
a.cpp -----> <scanner1>header.h [search path: d1, d2, d3]
|
|
|
|
|
|
<d2>header.h --------> header.y
|
|
[generated in d2]
|
|
|
|
b.cpp -----> <scanner2>header.h [ search path: d1, d2, d4]
|
|
</pre>
|
|
In this case, Jam thinks all header.h target are not realated. The right
|
|
dependency graph might be:
|
|
<pre>
|
|
a.cpp ----
|
|
\
|
|
\
|
|
>----> <d2>header.h --------> header.y
|
|
/ [generated in d2]
|
|
/
|
|
b.cpp ----
|
|
</pre>
|
|
or
|
|
<pre>
|
|
a.cpp -----> <scanner1>header.h [search path: d1, d2, d3]
|
|
|
|
|
(includes)
|
|
V
|
|
<d2>header.h --------> header.y
|
|
[generated in d2]
|
|
^
|
|
(includes)
|
|
|
|
|
b.cpp -----> <scanner2>header.h [ search path: d1, d2, d4]
|
|
</pre>
|
|
The first alternative was used for some time. The problem however is:
|
|
what include paths should be used when scanning header.h? The second
|
|
alternative was suggested by Matt Armstrong. It has similiar effect: add
|
|
targets which depend on <scanner1>header.h will also depend on
|
|
<d2>header.h. But now we have two different target with two
|
|
different scanners, and those targets can be scanned independently. The
|
|
problem of first alternative is avoided, so the second alternative is
|
|
implemented now.
|
|
|
|
<p>The second sub-requirements is that targets generated to "bin"
|
|
directory are handled as well. Boost.Build implements semi-automatic
|
|
approach. When compiling C++ files the process is:</p>
|
|
|
|
<ol>
|
|
<li>The main target to which compiled file belongs is found.</li>
|
|
|
|
<li>All other main targets that the found one depends on are found.
|
|
Those include main target which are used as sources, or present as
|
|
values of "dependency" features.</li>
|
|
|
|
<li>All directories where files belonging to those main target will be
|
|
generated are added to the include path.</li>
|
|
</ol>
|
|
|
|
<p>After this is done, dependencies are found by the approach explained
|
|
previously.</p>
|
|
|
|
<p>Note that if a target uses generated headers from other main target,
|
|
that main target should be explicitly specified as dependency property.
|
|
It would be better to lift this requirement, but it seems not very
|
|
problematic in practice.</p>
|
|
|
|
<p>For target types other than C++, adding of include paths must be
|
|
implemented anew.</p>
|
|
|
|
<h3>Proper detection of dependencies from generated files</h3>
|
|
|
|
<p>Suppose file "a.cpp" includes "a.h" and both are generated by some
|
|
action. Note that classic jam has two stages. In first stage dependency
|
|
graph graph is build and actions which should be run are determined. In
|
|
second stage the actions are executed. Initially, neither file exists, so
|
|
the include is not found. As the result, jam might attempt to compile
|
|
a.cpp before creating a.h, and compilation will fail.</p>
|
|
|
|
<p>The solution in Boost.Jam is to perform additional dependency scans
|
|
after targets are updated. This break separation between build stages in
|
|
jam — which some people consider a good thing — but I'm not
|
|
aware of any better solution.</p>
|
|
|
|
<p>In order to understand the rest of this section, you better read some
|
|
details about jam dependency scanning, available <a href=
|
|
"http://public.perforce.com:8080/@md=d&cd=//public/jam/src/&ra=s&c=kVu@//2614?ac=10">
|
|
at this link</a>.</p>
|
|
|
|
<p>Whenever a target is updated, Boost.Jam rescans it for includes.
|
|
Consider this graph, created before any actions are run.</p>
|
|
<pre>
|
|
A -------> C ----> C.pro
|
|
/
|
|
B --/ C-includes ---> D
|
|
|
|
</pre>
|
|
Both A and B have dependency on C and C-includes (the latter dependency
|
|
is not shown). Say during building we've tried to create A, then tried to
|
|
create C and successfully created C.
|
|
|
|
<p>In that case, the set of includes in C might well have changed. We do
|
|
not bother to detect precisely which includes were added or removed.
|
|
Instead we create another internal node C-includes-2. Then we determine
|
|
what actions should be run to update the target. In fact this mean that
|
|
we perform logic of first stage while already executing stage.</p>
|
|
|
|
<p>After actions for C-includes-2 are determined, we add C-includes-2 to
|
|
the list of A's dependents, and stage 2 proceeds as usual. Unfortunately,
|
|
we can't do the same with target B, since when it's not visited, C target
|
|
does not know B depends on it. So, we add a flag to C which tells and it
|
|
was rescanned. When visiting B target, the flag is notices and
|
|
C-includes-2 will be added to the list of B's dependencies.</p>
|
|
|
|
<p>Note also that internal nodes are sometimes updated too. Consider this
|
|
dependency graph:</p>
|
|
<pre>
|
|
a.o ---> a.cpp
|
|
a.cpp-includes --> a.h (scanned)
|
|
a.h-includes ------> a.h (generated)
|
|
|
|
|
|
|
|
a.pro <-------------------------------------------+
|
|
</pre>
|
|
|
|
<p>Here, out handling of generated headers come into play. Say that a.h
|
|
exists but is out of date with respect to "a.pro", then "a.h (generated)"
|
|
and "a.h-includes" will be marking for updating, but "a.h (scanned)"
|
|
won't be marked. We have to rescan "a.h" file after it's created, but
|
|
since "a.h (generated)" has no scanner associated with it, it's only
|
|
possible to rescan "a.h" after "a.h-includes" target was updated.</p>
|
|
|
|
<p>Tbe above consideration lead to decision that we'll rescan a target
|
|
whenever it's updated, no matter if this target is internal or not.</p>
|
|
|
|
<div class="alert">
|
|
The remainder of this document is not indended to be read at all. This
|
|
will be rearranged in future.
|
|
</div>
|
|
|
|
<h4>File targets</h4>
|
|
As described above, file targets corresponds to files that Boost.Build
|
|
manages. User's may be concerned about file targets in three ways: when
|
|
declaring file target types, when declaring transformations between
|
|
types, and when determining where file target will be placed. File
|
|
targets can also be connected with actions, that determine how the target
|
|
is created. Both file targets and actions are implemented in the
|
|
<tt>virtual-target</tt> module.
|
|
|
|
<h5>Types</h5>
|
|
A file target can be given a file, which determines what transformations
|
|
can be applied to the file. The <tt>type.register</tt> rule declares new
|
|
types. File type can also be assigned a scanner, which is used to find
|
|
implicit dependencies. See <a href="#dependency_scanning">dependency
|
|
scanning</a> below.
|
|
|
|
<h4>Target paths</h4>
|
|
|
|
<p>To distinguish targets build with different properties, they are put
|
|
in different directories. Rules for determining target paths are given
|
|
below:</p>
|
|
|
|
<ol>
|
|
<li>All targets are placed under directory corresponding to the project
|
|
where they are defined.</li>
|
|
|
|
<li>Each non free, non incidental property cause an additional element
|
|
to be added to the target path. That element has the form
|
|
<tt><feature-name>-<feature-value></tt> for ordinary
|
|
features and <tt><feature-value></tt> for implicit ones. [Note
|
|
about composite features].</li>
|
|
|
|
<li>If the set of free, non incidental properties is different from the
|
|
set of free, non incidental properties for the project in which the
|
|
main target that uses the target is defined, a part of the form
|
|
<tt>main_target-<name></tt> is added to the target path.
|
|
<b>Note:</b>It would be nice to completely track free features also,
|
|
but this appears to be complex and not extremely needed.</li>
|
|
</ol>
|
|
|
|
<p>For example, we might have these paths:</p>
|
|
<pre>
|
|
debug/optimization-off
|
|
debug/main-target-a
|
|
|
|
</pre>
|
|
<hr>
|
|
|
|
<p class="revision">Last modified: June 30, 2003</p>
|
|
|
|
<p>© Copyright Vladimir Prus 2002-2003. Permission to copy, use,
|
|
modify, sell and distribute this document is granted provided this
|
|
copyright notice appears in all copies. This document is provided ``as
|
|
is'' without express or implied warranty, and with no claim as to its
|
|
suitability for any purpose.</p>
|
|
<!-- sf logo -->
|
|
</body>
|
|
</html>
|
|
|