2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-02 20:52:13 +00:00
Files
build/doc/architecture.html
Vladimir Prus fe2a006bcd Fix links
[SVN r22069]
2004-01-30 11:34:54 +00:00

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" &mdash; 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 &mdash;
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 (&lt;toolset&gt;gcc) &lt;--(compile)-- a.cpp (scanner1) ----+
a.o (&lt;toolset&gt;msvc) &lt;--(compile)-- a.cpp (scanner2) ----|
a.cpp (installed copy) &lt;--(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 -----&gt; &lt;scanner1&gt;header.h [search path: d1, d2, d3]
&lt;d2&gt;header.h --------&gt; header.y
[generated in d2]
b.cpp -----&gt; &lt;scanner2&gt;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 ----
\
\
&gt;----&gt; &lt;d2&gt;header.h --------&gt; header.y
/ [generated in d2]
/
b.cpp ----
</pre>
or
<pre>
a.cpp -----&gt; &lt;scanner1&gt;header.h [search path: d1, d2, d3]
|
(includes)
V
&lt;d2&gt;header.h --------&gt; header.y
[generated in d2]
^
(includes)
|
b.cpp -----&gt; &lt;scanner2&gt;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 &lt;scanner1&gt;header.h will also depend on
&lt;d2&gt;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 &mdash; which some people consider a good thing &mdash; 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&amp;cd=//public/jam/src/&amp;ra=s&amp;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 -------&gt; C ----&gt; C.pro
/
B --/ C-includes ---&gt; 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 ---&gt; a.cpp
a.cpp-includes --&gt; a.h (scanned)
a.h-includes ------&gt; a.h (generated)
|
|
a.pro &lt;-------------------------------------------+
</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>&lt;feature-name&gt;-&lt;feature-value&gt;</tt> for ordinary
features and <tt>&lt;feature-value&gt;</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-&lt;name&gt;</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>&copy; 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>