$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r55201 - in trunk/tools/build/v2: . build kernel tools tools/types util
From: ghost_at_[hidden]
Date: 2009-07-30 14:43:15
Author: vladimir_prus
Date: 2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
New Revision: 55201
URL: http://svn.boost.org/trac/boost/changeset/55201
Log:
Add experimental Python port
Added:
   trunk/tools/build/v2/build/__init__.py   (contents, props changed)
   trunk/tools/build/v2/build/alias.py   (contents, props changed)
   trunk/tools/build/v2/build/build_request.py   (contents, props changed)
   trunk/tools/build/v2/build/engine.py   (contents, props changed)
   trunk/tools/build/v2/build/errors.py   (contents, props changed)
   trunk/tools/build/v2/build/feature.py   (contents, props changed)
   trunk/tools/build/v2/build/generators.py   (contents, props changed)
   trunk/tools/build/v2/build/project.ann.py   (contents, props changed)
   trunk/tools/build/v2/build/project.py   (contents, props changed)
   trunk/tools/build/v2/build/property.py   (contents, props changed)
   trunk/tools/build/v2/build/property_set.py   (contents, props changed)
   trunk/tools/build/v2/build/scanner.py   (contents, props changed)
   trunk/tools/build/v2/build/targets.py   (contents, props changed)
   trunk/tools/build/v2/build/toolset.py   (contents, props changed)
   trunk/tools/build/v2/build/type.py   (contents, props changed)
   trunk/tools/build/v2/build/virtual_target.py   (contents, props changed)
   trunk/tools/build/v2/build_system.py   (contents, props changed)
   trunk/tools/build/v2/exceptions.py   (contents, props changed)
   trunk/tools/build/v2/manager.py   (contents, props changed)
   trunk/tools/build/v2/tools/__init__.py   (contents, props changed)
   trunk/tools/build/v2/tools/builtin.py   (contents, props changed)
   trunk/tools/build/v2/tools/common.py   (contents, props changed)
   trunk/tools/build/v2/tools/darwin.py   (contents, props changed)
   trunk/tools/build/v2/tools/gcc.py   (contents, props changed)
   trunk/tools/build/v2/tools/make.py   (contents, props changed)
   trunk/tools/build/v2/tools/pch.py   (contents, props changed)
   trunk/tools/build/v2/tools/rc.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/__init__.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/asm.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/cpp.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/exe.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/html.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/lib.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/obj.py   (contents, props changed)
   trunk/tools/build/v2/tools/types/rsp.py   (contents, props changed)
   trunk/tools/build/v2/tools/unix.py   (contents, props changed)
   trunk/tools/build/v2/util/__init__.py   (contents, props changed)
   trunk/tools/build/v2/util/logger.py   (contents, props changed)
   trunk/tools/build/v2/util/order.py   (contents, props changed)
   trunk/tools/build/v2/util/path.py   (contents, props changed)
   trunk/tools/build/v2/util/regex.py   (contents, props changed)
   trunk/tools/build/v2/util/sequence.py   (contents, props changed)
   trunk/tools/build/v2/util/set.py   (contents, props changed)
   trunk/tools/build/v2/util/utility.py   (contents, props changed)
Text files modified: 
   trunk/tools/build/v2/kernel/bootstrap.jam |   113 +++++++++++++++++++++++++++++++++++++-- 
   1 files changed, 107 insertions(+), 6 deletions(-)
Added: trunk/tools/build/v2/build/__init__.py
==============================================================================
Added: trunk/tools/build/v2/build/alias.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/alias.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,62 @@
+# Copyright 2003, 2004, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+# Status: ported (danielw)
+# Base revision: 40480
+
+#  This module defines the 'alias' rule and associated class.
+#
+#  Alias is just a main target which returns its source targets without any 
+#  processing. For example::
+#
+#    alias bin : hello test_hello ;
+#    alias lib : helpers xml_parser ;
+#
+#  Another important use of 'alias' is to conveniently group source files::
+#
+#    alias platform-src : win.cpp : <os>NT ;
+#    alias platform-src : linux.cpp : <os>LINUX ;
+#    exe main : main.cpp platform-src ;
+# 
+#  Lastly, it's possible to create local alias for some target, with different
+#  properties::
+#
+#    alias big_lib : : @/external_project/big_lib/<link>static ;
+#
+
+import targets
+import property_set
+from b2.manager import get_manager
+
+class AliasTarget(targets.BasicTarget):
+
+    def __init__(self, *args):
+        targets.BasicTarget.__init__(self, *args)
+
+    def construct(self, name, source_targets, properties):
+        return [property_set.empty(), source_targets]
+
+    def compute_usage_requirements(self, subvariant):
+        base = targets.BasicTarget.compute_usage_requirements(self, subvariant)
+        # Add source's usage requirement. If we don't do this, "alias" does not
+        # look like 100% alias.
+        return base.add(subvariant.sources_usage_requirements())
+
+def alias(name, sources, requirements=None, default_build=None, usage_requirements=None):
+    project = get_manager().projects().current()
+    targets = get_manager().targets()
+
+    if default_build:
+        default_build = default_build[0]
+
+    targets.main_target_alternative(AliasTarget(
+        name[0], project,
+        targets.main_target_sources(sources, name),
+        targets.main_target_requirements(requirements or [], project),
+        targets.main_target_default_build(default_build, project),
+        targets.main_target_usage_requirements(usage_requirements or [], project)))
+
+# Declares the 'alias' target. It will build sources, and return them unaltered.
+get_manager().projects().add_rule("alias", alias)
+
Added: trunk/tools/build/v2/build/build_request.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/build_request.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,211 @@
+# Status: being ported by Vladimir Prus
+# TODO: need to re-compare with mainline of .jam
+# Base revision: 40480
+#
+#  (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+import feature
+from b2.util import set
+from b2.util.utility import *
+
+def expand_no_defaults (property_sets):
+    """ Expand the given build request by combining all property_sets which don't
+        specify conflicting non-free features.
+    """
+    # First make all features and subfeatures explicit
+    expanded_property_sets = [ __apply_to_property_set (feature.expand_subfeatures, x) for x in property_sets ]
+    
+    # Now combine all of the expanded property_sets
+    product = __x_product (expanded_property_sets)
+    
+    return product
+
+def __apply_to_property_set (f, property_set):
+    """ Transform property_set by applying f to each component property.
+    """
+    properties = feature.split (property_set)
+    return '/'.join (f (properties))
+
+
+
+def __x_product (property_sets):
+    """ Return the cross-product of all elements of property_sets, less any
+        that would contain conflicting values for single-valued features.
+    """
+    x_product_seen = []
+    x_product_used = []
+    feature_space = []
+    return __x_product_aux (property_sets, x_product_seen, x_product_used, feature_space)
+    
+def __x_product_aux (property_sets, x_product_seen, x_product_used, feature_space):
+    """ Implementation of __x_product.
+    """
+    result = []
+    
+    if property_sets:
+        p = feature.split (property_sets [0])
+    else:
+        p = []
+        
+    f = set.difference (get_grist (p), feature.free_features ())
+    
+    seen = []
+    # No conflict with things used at a higher level?
+    if not set.intersection (f, x_product_used):
+        # don't mix in any conflicting features
+        local_x_product_used = x_product_used + f
+        local_x_product_seen = []
+        
+        if len (property_sets) > 1:
+            rest = __x_product_aux (property_sets [1:], local_x_product_seen, local_x_product_used, feature_space)
+            result = [ property_sets [0] + '/' + x for x in rest]
+        
+        if not result and property_sets:
+            result = [property_sets [0]]
+        
+        # If we didn't encounter a conflicting feature lower down,
+        # don't recurse again.
+        if not set.intersection (f, local_x_product_seen):
+            property_sets = []
+        
+        seen = local_x_product_seen
+    
+    if len (property_sets) > 1:
+        result.extend (__x_product_aux (property_sets [1:], x_product_seen, x_product_used, feature_space))
+    x_product_seen += f + seen
+    
+    # Note that we've seen these features so that higher levels will
+    # recurse again without them set.
+
+    return result
+
+def looks_like_implicit_value(v):
+    """Returns true if 'v' is either implicit value, or
+    the part before the first '-' symbol is implicit value."""
+    if feature.is_implicit_value(v):
+        return 1
+    else:
+        split = v.split("-")
+        if feature.is_implicit_value(split[0]):
+            return 1
+
+    return 0
+
+def from_command_line(command_line):
+    """Takes the command line tokens (such as taken from ARGV rule)
+    and constructs build request from it. Returns a list of two
+    lists. First is the set of targets specified in the command line,
+    and second is the set of requested build properties."""
+
+    targets = []
+    properties = []
+
+    for e in command_line:
+        if e[0] != "-":
+            # Build request spec either has "=" in it, or completely
+            # consists of implicit feature values.
+            if e.find("=") != -1 or looks_like_implicit_value(e.split("/")[0]):                
+                properties += convert_command_line_element(e)
+            else:
+                targets.append(e)
+
+    return [targets, properties]
+ 
+# Converts one element of command line build request specification into
+# internal form.
+def convert_command_line_element(e):
+
+    result = None
+    parts = e.split("/")
+    for p in parts:
+        m = p.split("=")
+        if len(m) > 1:
+            feature = m[0]
+            values = m[1].split(",")
+            lresult = [("<%s>%s" % (feature, v)) for v in values]
+        else:
+            lresult = p.split(",")
+            
+        if p.find('-') == -1:
+            # FIXME: first port property.validate
+            # property.validate cannot handle subfeatures,
+            # so we avoid the check here.
+            #for p in lresult:
+            #    property.validate(p)
+            pass
+
+        if not result:
+            result = lresult
+        else:
+            result = [e1 + "/" + e2 for e1 in result for e2 in lresult]
+
+    return result
+
+### 
+### rule __test__ ( )
+### {
+###     import assert feature ;
+###     
+###     feature.prepare-test build-request-test-temp ;
+###     
+###     import build-request ;
+###     import build-request : expand_no_defaults : build-request.expand_no_defaults ;
+###     import errors : try catch ;
+###     import feature : feature subfeature ;
+### 
+###     feature toolset : gcc msvc borland : implicit ;
+###     subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4
+###       3.0 3.0.1 3.0.2 : optional ;
+### 
+###     feature variant : debug release : implicit composite ;
+###     feature inlining : on off ;
+###     feature "include" : : free ;
+### 
+###     feature stdlib : native stlport : implicit ;
+### 
+###     feature runtime-link : dynamic static : symmetric ;
+### 
+### 
+###     local r ;
+### 
+###     r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ;              
+###     assert.equal [ $(r).get-at 1 ] : ;
+###     assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ;
+### 
+###     try ;
+###     {
+### 
+###         build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ;
+###     }
+###     catch \"static\" is not a value of an implicit feature ;
+### 
+### 
+###     r = [ build-request.from-command-line bjam -d2 --debug debug target runtime-link=dynamic ] ;
+###     assert.equal [ $(r).get-at 1 ] : target ;
+###     assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ;
+### 
+###     r = [ build-request.from-command-line bjam debug runtime-link=dynamic,static ] ;
+###     assert.equal [ $(r).get-at 1 ] : ;
+###     assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic <runtime-link>static ;
+### 
+###     r = [ build-request.from-command-line bjam debug gcc/runtime-link=dynamic,static ] ;
+###     assert.equal [ $(r).get-at 1 ] : ;
+###     assert.equal [ $(r).get-at 2 ] : debug gcc/<runtime-link>dynamic 
+###                  gcc/<runtime-link>static ;
+### 
+###     r = [ build-request.from-command-line bjam msvc gcc,borland/runtime-link=static ] ;
+###     assert.equal [ $(r).get-at 1 ] : ;
+###     assert.equal [ $(r).get-at 2 ] : msvc gcc/<runtime-link>static 
+###                     borland/<runtime-link>static ;
+### 
+###     r = [ build-request.from-command-line bjam gcc-3.0 ] ;
+###     assert.equal [ $(r).get-at 1 ] : ;
+###     assert.equal [ $(r).get-at 2 ] : gcc-3.0 ;
+### 
+###     feature.finish-test build-request-test-temp ;
+### }
+### 
+### 
Added: trunk/tools/build/v2/build/engine.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/engine.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,159 @@
+# Copyright Pedro Ferreira 2005.
+# Copyright Vladimir Prus 2007.
+# Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+bjam_interface = __import__('bjam')
+
+import operator
+
+class BjamAction:
+    """Class representing bjam action defined from Python."""
+    
+    def __init__(self, action_name, function):
+        self.action_name = action_name
+        self.function = function
+            
+    def __call__(self, targets, sources, property_set):
+        if self.function:
+            self.function(targets, sources, property_set)
+        # Bjam actions defined from Python have only the command
+        # to execute, and no associated jam procedural code. So
+        # passing 'property_set' to it is not necessary.
+        bjam_interface.call("set-update-action", self.action_name,
+                            targets, sources, [])
+
+class BjamNativeAction:
+    """Class representing bjam action fully defined by Jam code."""
+    
+    def __init__(self, action_name):
+        self.action_name = action_name
+        
+    def __call__(self, targets, sources, property_set):
+        if property_set:
+            bjam_interface.call("set-update-action", self.action_name,
+                                targets, sources, property_set.raw())
+        else:
+            bjam_interface.call("set-update-action", self.action_name,
+                                targets, sources, [])
+        
+action_modifiers = {"updated": 0x01,
+                    "together": 0x02,
+                    "ignore": 0x04,
+                    "quietly": 0x08,
+                    "piecemeal": 0x10,
+                    "existing": 0x20}
+
+class Engine:
+    """ The abstract interface to a build engine.
+
+    For now, the naming of targets, and special handling of some
+    target variables like SEARCH and LOCATE make this class coupled
+    to bjam engine.
+    """
+    def __init__ (self):
+        self.actions = {}
+
+    def add_dependency (self, targets, sources):
+        """Adds a dependency from 'targets' to 'sources'
+
+        Both 'targets' and 'sources' can be either list
+        of target names, or a single target name.
+        """
+        if isinstance (targets, str):
+            targets = [targets]
+        if isinstance (sources, str):
+            sources = [sources]
+
+        for target in targets:
+            for source in sources:
+                self.do_add_dependency (target, source)
+    
+    def set_target_variable (self, targets, variable, value, append=0):
+        """ Sets a target variable.
+
+        The 'variable' will be available to bjam when it decides
+        where to generate targets, and will also be available to
+        updating rule for that 'taret'.
+        """
+        if isinstance (targets, str): 
+            targets = [targets]
+
+        for target in targets:
+            self.do_set_target_variable (target, variable, value, append)
+
+    def set_update_action (self, action_name, targets, sources, properties):
+        """ Binds a target to the corresponding update action.
+            If target needs to be updated, the action registered
+            with action_name will be used.
+            The 'action_name' must be previously registered by
+            either 'register_action' or 'register_bjam_action'
+            method.
+        """
+        if isinstance (targets, str): 
+            targets = [targets]
+        self.do_set_update_action (action_name, targets, sources, properties)
+
+    def register_action (self, action_name, command, bound_list = [], flags = [],
+                         function = None):
+        """Creates a new build engine action.
+
+        Creates on bjam side an action named 'action_name', with
+        'command' as the command to be executed, 'bound_variables'
+        naming the list of variables bound when the command is executed
+        and specified flag.
+        If 'function' is not None, it should be a callable taking three
+        parameters:
+            - targets
+            - sources
+            - instance of the property_set class
+        This function will be called by set_update_action, and can
+        set additional target variables.
+        """
+        if self.actions.has_key(action_name):
+            raise "Bjam action %s is already defined" % action_name
+
+        assert(isinstance(flags, list))
+
+        bjam_flags = reduce(operator.or_,
+                            (action_modifiers[flag] for flag in flags), 0)
+
+        bjam_interface.define_action(action_name, command, bound_list, bjam_flags)
+
+        self.actions[action_name] = BjamAction(action_name, function)
+
+    def register_bjam_action (self, action_name):
+        """Informs self that 'action_name' is declared in bjam.
+
+        From this point, 'action_name' is a valid argument to the
+        set_update_action method. The action_name should be callable
+        in the global module of bjam.
+        """
+
+        # We allow duplicate calls to this rule for the same
+        # action name.  This way, jamfile rules that take action names
+        # can just register them without specially checking if
+        # action is already registered.
+        if not self.actions.has_key(action_name):
+            self.actions[action_name] = BjamNativeAction(action_name)
+    
+    # Overridables
+
+
+    def do_set_update_action (self, action_name, targets, sources, property_set):
+        action = self.actions.get(action_name)
+        if not action:
+            raise "No action %s was registered" % action_name
+        action(targets, sources, property_set)
+
+    def do_set_target_variable (self, target, variable, value, append):
+        if append:
+            bjam_interface.call("set-target-variable", target, variable, value, "true")
+        else:
+            bjam_interface.call("set-target-variable", target, variable, value)
+        
+    def do_add_dependency (self, target, source):
+        bjam_interface.call("DEPENDS", target, source)
+         
+        
Added: trunk/tools/build/v2/build/errors.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/errors.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,122 @@
+# Status: being written afresh by Vladimir Prus
+
+# Copyright 2007 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+# This file is supposed to implement error reporting for Boost.Build.
+# Experience with jam version has shown that printing full backtrace
+# on each error is buffling. Further, for errors printed after parsing --
+# during target building, the stacktrace does not even mention what
+# target is being built.
+
+# This module implements explicit contexts -- where other code can
+# communicate which projects/targets are being built, and error
+# messages will show those contexts. For programming errors,
+# Python assertions are to be used.
+
+import bjam
+import traceback
+import sys
+
+def format(message, prefix=""):
+    parts = message.split("\n")
+    return "\n".join(prefix+p for p in parts)
+    
+
+class Context:
+
+    def __init__(self, message, nested=None):
+        self.message_ = message
+        self.nested_ = nested
+
+    def report(self, indent=""):
+        print indent + "    -", self.message_
+        if self.nested_:
+            print indent + "        declared at:"
+            for n in self.nested_:
+                n.report(indent + "    ")
+
+class JamfileContext:
+
+    def __init__(self):
+        raw = bjam.backtrace()
+        self.raw_ = raw
+
+    def report(self, indent=""):
+        for r in self.raw_:
+            print indent + "    - %s:%s" % (r[0], r[1])
+
+class ExceptionWithUserContext(Exception):
+
+    def __init__(self, message, context,
+                 original_exception=None, original_tb=None, stack=None):
+        Exception.__init__(self, message)
+        self.context_ = context
+        self.original_exception_ = original_exception
+        self.original_tb_ = original_tb
+        self.stack_ = stack
+
+    def report(self):
+        print "error:", self.message
+        if self.original_exception_:
+            print format(self.original_exception_.message, "    ")
+        print
+        print "    error context (most recent first):"
+        for c in self.context_[::-1]:
+            c.report()
+        print
+        if "--stacktrace" in bjam.variable("ARGV"):
+            if self.original_tb_:
+                traceback.print_tb(self.original_tb_)
+            elif self.stack_:
+                for l in traceback.format_list(self.stack_):
+                    print l,                
+        else:
+            print "    use the '--stacktrace' option to get Python stacktrace"
+        print
+
+def user_error_checkpoint(callable):
+    def wrapper(self, *args):
+        errors = self.manager().errors()
+        try:
+            return callable(self, *args)
+        except ExceptionWithUserContext, e:
+            raise
+        except Exception, e:
+            errors.handle_stray_exception(e)
+        finally:
+            errors.pop_user_context()
+            
+    return wrapper
+                            
+class Errors:
+
+    def __init__(self):
+        self.contexts_ = []
+
+    def push_user_context(self, message, nested=None):
+        self.contexts_.append(Context(message, nested))
+
+    def pop_user_context(self):
+        del self.contexts_[-1]
+
+    def push_jamfile_context(self):
+        self.contexts_.append(JamfileContext())
+
+    def pop_jamfile_context(self):
+        del self.contexts_[-1]
+
+    def capture_user_context(self):
+        return self.contexts_[:]
+
+    def handle_stray_exception(self, e):
+        raise ExceptionWithUserContext("unexpected exception", self.contexts_[:],
+                                       e, sys.exc_info()[2])    
+    def __call__(self, message):
+        raise ExceptionWithUserContext(message, self.contexts_[:], 
+                                       stack=traceback.extract_stack())
+
+        
+
+    
Added: trunk/tools/build/v2/build/feature.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/feature.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,891 @@
+# Status: mostly ported.
+# TODO: carry over tests.
+# Base revision: 40480
+#
+# Copyright 2001, 2002, 2003 Dave Abrahams 
+# Copyright 2002, 2006 Rene Rivera 
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+# TODO: stop using grists to identify the name of features?
+#       create a class for Features and Properties?
+#       represent conditions using object trees, composite pattern?
+
+import re
+
+from b2.util import set, utility
+from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, to_seq
+from b2.exceptions import *
+
+__re_split_subfeatures = re.compile ('<(.*):(.*)>')
+__re_no_hyphen = re.compile ('^([^:]+)$')
+__re_slash_or_backslash = re.compile (r'[\\/]')
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __all_attributes, __all_features, __implicit_features, __composite_properties
+    global __features_with_attributes, __subfeature_value_to_name, __all_top_features, __free_features
+    global __all_subfeatures
+        
+    # The list with all attribute names.
+    __all_attributes = [ 'implicit',
+                        'executed',
+                        'composite',
+                        'optional',
+                        'symmetric',
+                        'free',
+                        'incidental',
+                        'path',
+                        'dependency',
+                        'propagated',
+                        'link-incompatible',
+                        'subfeature',
+                        'order-sensitive'
+                       ]
+    
+    # A map containing all features. The key is the gristed feature name. The value is a map with:
+    #    'values': [],
+    #    'attributes': [],
+    #    'subfeatures': [],
+    #    'default': None
+    __all_features = {}
+    
+    # All non-subfeatures.
+    __all_top_features = []
+    
+    # Maps valus to the corresponding implicit feature
+    __implicit_features = {}
+    
+    # A map containing all composite properties. The key is the name of the property. The value is a map with:
+    #    'components': []
+    __composite_properties = {}
+    
+    __features_with_attributes = {}
+    for attribute in __all_attributes:
+        __features_with_attributes [attribute] = []
+    
+    # Maps a value to the corresponding subfeature name.
+    __subfeature_value_to_name = {}
+    
+    # All free features
+    __free_features = []
+
+    __all_subfeatures = []
+
+reset ()
+
+def enumerate ():
+    """ Returns an iterator to the features map.
+    """
+    return __all_features.iteritems ()
+
+# FIXME: prepare-test/finish-test?
+
+def feature (name, values, attributes = []):
+    """ Declares a new feature with the given name, values, and attributes.
+        name: the feature name
+        values: a sequence of the allowable values - may be extended later with feature.extend
+        attributes: a sequence of the feature's attributes (e.g. implicit, free, propagated, ...)
+    """
+    name = add_grist (name)
+
+    __validate_feature_attributes (name, attributes)
+
+    feature = { 
+        'values': [],
+        'attributes': attributes,
+        'subfeatures': [],
+        'default': None
+        }
+    __all_features [name] = feature
+    
+    feature ['attributes'] = attributes
+    
+    for attribute in attributes:
+        __features_with_attributes [attribute].append (name)
+        
+    if 'subfeature' in attributes:
+        __all_subfeatures.append(name)
+    else:
+        __all_top_features.append(name)
+
+    extend (name, values)
+
+    # FIXME: why his is needed.
+    if 'free' in attributes:
+        __free_features.append (name)
+
+def set_default (feature, value):
+    """ Sets the default value of the given feature, overriding any previous default.
+        feature: the name of the feature
+        value: the default value to assign
+    """
+
+    if isinstance(feature, list):
+        feature = feature[0]
+
+    feature = add_grist (feature)
+    f = __all_features [feature]
+
+    if isinstance(value, list):
+        value = value[0]
+
+    values = f['values']
+    if not value in values:
+        raise InvalidValue ("The specified default value, '%s' is invalid.\n" % value + "allowed values are: %s" % values)
+
+    f ['default'] = value
+
+def defaults (features):
+    """ Returns the default property values for the given features.
+    """
+    result = []
+    for f in features:
+        attributes = __all_features [f]['attributes']
+        if not 'free' in attributes and not 'optional' in attributes:
+            defaults = __all_features [f]['default']
+            if defaults:
+                result.append (replace_grist (defaults, f))
+
+    return result
+
+def valid (names):
+    """ Returns true iff all elements of names are valid features.
+    """
+    def valid_one (name): return __all_features.has_key (name)
+        
+    if isinstance (names, str):
+        return valid_one (names)
+    else:
+        return [ valid_one (name) for name in names ]
+
+def attributes (feature):
+    """ Returns the attributes of the given feature.
+    """
+    return __all_features [feature]['attributes']
+        
+def values (feature):
+    """ Return the values of the given feature.
+    """
+    validate_feature (feature)
+    return __all_features [feature]['values']
+
+def is_implicit_value (value_string):
+    """ Returns true iff 'value_string' is a value_string
+    of an implicit feature.
+    """
+    v = value_string.split('-')
+    
+    if not __implicit_features.has_key(v[0]):
+        return False
+
+    feature = __implicit_features[v[0]]
+    
+    for subvalue in (v[1:]):
+        if not __find_implied_subfeature(feature, subvalue, v[0]):
+            return False
+            
+    return True
+
+def implied_feature (implicit_value):
+    """ Returns the implicit feature associated with the given implicit value.
+    """
+    components = implicit_value.split('-')
+    
+    if not __implicit_features.has_key(components[0]):
+        raise InvalidValue ("'%s' is not a value of an implicit feature" % implicit_value)
+        
+    return __implicit_features[components[0]]
+
+def __find_implied_subfeature (feature, subvalue, value_string):
+    feature = add_grist (feature)
+    if value_string == None: value_string = ''
+
+    if not __subfeature_value_to_name.has_key (feature) \
+        or not __subfeature_value_to_name [feature].has_key (value_string) \
+        or not __subfeature_value_to_name [feature][value_string].has_key (subvalue):
+        return None
+        
+    return __subfeature_value_to_name[feature][value_string][subvalue]
+
+# Given a feature and a value of one of its subfeatures, find the name
+# of the subfeature. If value-string is supplied, looks for implied
+# subfeatures that are specific to that value of feature
+#  feature             # The main feature name
+#  subvalue            # The value of one of its subfeatures
+#  value-string        # The value of the main feature
+
+def implied_subfeature (feature, subvalue, value_string):
+    result = __find_implied_subfeature (feature, subvalue, value_string)
+    if not result:
+        raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string))
+
+    return result
+
+def validate_feature (name):
+    """ Checks if all name is a valid feature. Otherwise, raises an exception.
+    """
+    x = valid (name)
+    if not x:
+        raise InvalidFeature ("'%s' is not a valid feature name" % name)
+
+def valid (names):
+    """ Returns true iff all elements of names are valid features.
+    """
+    def valid_one (name): return __all_features.has_key (name)
+        
+    if isinstance (names, str):
+        return valid_one (names)
+    else:
+        return [ valid_one (name) for name in names ]
+
+def __expand_subfeatures_aux (feature, value, dont_validate = False):
+    """ Helper for expand_subfeatures.
+        Given a feature and value, or just a value corresponding to an
+        implicit feature, returns a property set consisting of all component
+        subfeatures and their values. For example:
+        
+          expand_subfeatures <toolset>gcc-2.95.2-linux-x86
+              -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
+          equivalent to:
+              expand_subfeatures gcc-2.95.2-linux-x86
+
+        feature:        The name of the feature, or empty if value corresponds to an implicit property
+        value:          The value of the feature.
+        dont_validate:  If True, no validation of value string will be done.
+    """
+    if not feature:
+        feature = implied_feature(value)
+    else:
+        validate_feature(feature)
+
+    if not dont_validate:
+        validate_value_string(feature, value)
+    
+    components = value.split ("-")
+    
+    # get the top-level feature's value
+    value = replace_grist(components[0], '')
+
+    result = [ replace_grist(components[0], feature) ]
+    
+    subvalues = components[1:]
+
+    while len(subvalues) > 0:
+        subvalue = subvalues [0]    # pop the head off of subvalues
+        subvalues = subvalues [1:]
+        
+        subfeature = __find_implied_subfeature (feature, subvalue, value)
+        
+        # If no subfeature was found, reconstitute the value string and use that
+        if not subfeature:
+            result = '-'.join(components)
+            result = replace_grist (result, feature)
+            return [result]
+            
+        f = ungrist (feature)
+        # FIXME: why grist includes '<>'?
+        result.append (replace_grist (subvalue, '<' + f + '-' + subfeature + '>'))
+    
+    return result
+
+def expand_subfeatures (properties, dont_validate = False):
+    """
+    Make all elements of properties corresponding to implicit features
+    explicit, and express all subfeature values as separate properties
+    in their own right. For example, the property
+    
+       gcc-2.95.2-linux-x86
+    
+    might expand to
+    
+      <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
+
+    properties:     A sequence with elements of the form
+                    <feature>value-string or just value-string in the
+                    case of implicit features.
+  : dont_validate:  If True, no validation of value string will be done.
+    """
+    result = []
+    for p in properties:
+        p_grist = get_grist (p)
+        # Don't expand subfeatures in subfeatures
+        if ':' in p_grist:
+            result.append (p)
+        else:
+            result.extend (__expand_subfeatures_aux (p_grist, replace_grist (p, ''), dont_validate))
+
+    return result
+
+
+
+# rule extend was defined as below:
+    # Can be called three ways:
+    #
+    #    1. extend feature : values *
+    #    2. extend <feature> subfeature : values *
+    #    3. extend <feature>value-string subfeature : values *
+    #
+    # * Form 1 adds the given values to the given feature
+    # * Forms 2 and 3 add subfeature values to the given feature
+    # * Form 3 adds the subfeature values as specific to the given
+    #   property value-string.
+    #
+    #rule extend ( feature-or-property subfeature ? : values * )
+#
+# Now, the specific rule must be called, depending on the desired operation:
+#   extend_feature
+#   extend_subfeature
+
+def extend (name, values):
+    """ Adds the given values to the given feature.
+    """
+    name = add_grist (name)
+    __validate_feature (name)
+    feature = __all_features [name]
+    
+    if 'implicit' in feature ['attributes']:
+        for v in values:
+            if __implicit_features.has_key (v):
+                raise BaseException ("'%s' is already associated with the feature '%s'" % (v, __implicit_features [v]))
+
+            __implicit_features[v] = name
+
+    if len (feature ['values']) == 0 and len (values) > 0:
+        # This is the first value specified for this feature,
+        # take it as default value
+        feature ['default'] = values[0]
+
+    feature['values'].extend (values)
+
+def validate_value_string (feature, value_string):
+    """ Checks that value-string is a valid value-string for the given feature.
+    """
+    f = __all_features [feature]
+    if 'free' in f ['attributes'] or value_string in f ['values']:
+        return
+
+    values = [value_string]
+
+    if f['subfeatures']:
+        values = value_string.split('-')
+
+    # An empty value is allowed for optional features
+    if not values[0] in f['values'] and \
+           (values[0] or not 'optional' in f['attributes']):
+        raise InvalidValue ("'%s' is not a known value of feature '%s'\nlegal values: '%s'" % (values [0], feature, f ['values']))
+
+    for v in values [1:]:
+        # this will validate any subfeature values in value-string
+        implied_subfeature(feature, v, values[0])
+
+
+""" Extends the given subfeature with the subvalues.  If the optional
+    value-string is provided, the subvalues are only valid for the given
+    value of the feature. Thus, you could say that
+    <target-platform>mingw is specifc to <toolset>gcc-2.95.2 as follows:
+    
+          extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ;
+
+    feature:        The feature whose subfeature is being extended.
+    
+    value-string:   If supplied, specifies a specific value of the
+                    main feature for which the new subfeature values
+                    are valid.
+    
+    subfeature:     The name of the subfeature.
+    
+    subvalues:      The additional values of the subfeature being defined.
+"""
+def extend_subfeature (feature, value_string, subfeature, subvalues):
+    feature = add_grist (feature)
+    validate_feature (feature)
+
+    if value_string:
+        validate_value_string (feature, value_string)
+
+    subfeature_name = __get_subfeature_name (subfeature, value_string)
+    
+    f = ungrist (feature)
+    extend (f + '-' + subfeature_name, subvalues) ;
+    
+    __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues)
+
+def subfeature (feature_name, value_string, subfeature, subvalues, attributes = []):
+    """ Declares a subfeature.
+        feature_name:   Root feature that is not a subfeature.
+        value_string:   An optional value-string specifying which feature or
+                        subfeature values this subfeature is specific to,
+                        if any.                
+        subfeature:     The name of the subfeature being declared.
+        subvalues:      The allowed values of this subfeature.
+        attributes:     The attributes of the subfeature.
+    """
+    feature_name = add_grist (feature_name)
+    validate_feature (feature_name)
+    
+    # Add grist to the subfeature name if a value-string was supplied
+    subfeature_name = __get_subfeature_name (subfeature, value_string)
+    
+    if subfeature_name in __all_features [feature_name]['subfeatures']:
+        message = "'%s' already declared as a subfeature of '%s'" % (subfeature, feature_name)
+        message += " specific to '%s'" % value_string
+        raise BaseException (message)
+
+    __all_features [feature_name]['subfeatures'].append (subfeature_name)
+
+    # First declare the subfeature as a feature in its own right
+    f = ungrist (feature_name)
+    feature (f + '-' + subfeature_name, subvalues, attributes + ['subfeature'])
+    
+    # Now make sure the subfeature values are known.
+    extend_subfeature (feature_name, value_string, subfeature, subvalues)
+
+def compose (composite_property, component_properties):
+    """ Sets the components of the given composite property.
+    """
+    component_properties = to_seq (component_properties)
+
+    feature = get_grist (composite_property)
+    if not 'composite' in attributes (feature):
+        raise BaseException ("'%s' is not a composite feature" % feature)
+
+    if __composite_properties.has_key (composite_property):
+        raise BaseException ('components of "%s" already set: %s' % (composite_property, str (__composite_properties [composite_property]['components'])))
+
+    if composite_property in component_properties:
+        raise BaseException ('composite property "%s" cannot have itself as a component' % composite_property)
+
+    entry = { 'components': component_properties }
+    __composite_properties [composite_property] = entry
+
+
+def expand_composite (property):
+    result = [ property ]
+    if __composite_properties.has_key (property):
+        for p in __composite_properties [property]['components']:
+            result.extend (expand_composite (p))
+    return result
+
+
+def get_values (feature, properties):
+    """ Returns all values of the given feature specified by the given property set.
+    """
+    result = []
+    for p in properties:
+        if get_grist (p) == feature:
+            result.append (replace_grist (p, ''))
+    
+    return result
+
+def free_features ():
+    """ Returns all free features.
+    """
+    return __free_features
+
+def expand_composites (properties):
+    """ Expand all composite properties in the set so that all components
+        are explicitly expressed.
+    """
+    explicit_features = get_grist (properties)
+
+    result = []
+
+    # now expand composite features
+    for p in properties:
+        expanded = expand_composite (p)
+
+        for x in expanded:
+            if not x in result:
+                f = get_grist (x)
+
+                if f in __free_features:
+                    result.append (x)
+                elif not x in properties:  # x is the result of expansion
+                    if not f in explicit_features:  # not explicitly-specified
+                        if f in get_grist (result):
+                            raise FeatureConflict ("expansions of composite features result in "
+                            "conflicting values for '%s'\nvalues: '%s'\none contributing composite property was '%s'" % (f, 
+                            get_values (f, result) + [replace_grist (x, '')], p))
+                        else:
+                            result.append (x)
+                elif f in get_grist (result):
+                    raise FeatureConflict ("explicitly-specified values of non-free feature '%s' conflict\n"
+                    "existing values: '%s'\nvalue from expanding '%s': '%s'" % (f, 
+                    get_values (f, properties), p, replace_grist (x, '')))
+                else:
+                    result.append (x)
+
+    return result
+
+def is_subfeature_of (parent_property, f):
+    """ Return true iff f is an ordinary subfeature of the parent_property's
+        feature, or if f is a subfeature of the parent_property's feature
+        specific to the parent_property's value.
+    """
+    if not valid (f) or not 'subfeature' in __all_features [f]['attributes']:
+        return False
+
+    specific_subfeature = __re_split_subfeatures.match (f)
+
+    if specific_subfeature:
+        # The feature has the form
+        # <topfeature-topvalue:subfeature>,
+        # e.g. <toolset-msvc:version>
+        feature_value = split_top_feature(specific_subfeature.group(1))
+        if replace_grist (feature_value [1], '<' + feature_value [0] + '>') == parent_property:
+            return True
+    else:
+        # The feature has the form <topfeature-subfeature>,
+        # e.g. <toolset-version>
+        top_sub = split_top_feature (ungrist (f))
+
+        if top_sub [1] and add_grist (top_sub [0]) == get_grist (parent_property):
+            return True
+
+    return False
+
+def __is_subproperty_of (parent_property, p):
+    """ As is_subfeature_of, for subproperties.
+    """
+    return is_subfeature_of (parent_property, get_grist (p))
+
+    
+# Returns true iff the subvalue is valid for the feature.  When the
+# optional value-string is provided, returns true iff the subvalues
+# are valid for the given value of the feature.
+def is_subvalue(feature, value_string, subfeature, subvalue):
+
+    if not value_string:
+        value_string = ''
+
+    if not __subfeature_value_to_name.has_key(feature):
+        return False
+        
+    if not __subfeature_value_to_name[feature].has_key(value_string):
+        return False
+        
+    if not __subfeature_value_to_name[feature][value_string].has_key(subvalue):
+        return False
+
+    if __subfeature_value_to_name[feature][value_string][subvalue]\
+           != subfeature:
+        return False
+
+    return True
+
+
+
+
+def implied_subfeature (feature, subvalue, value_string):
+    result = __find_implied_subfeature (feature, subvalue, value_string)
+    if not result:
+        raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string))
+
+    return result
+
+
+def expand (properties):
+    """ Given a property set which may consist of composite and implicit
+        properties and combined subfeature values, returns an expanded,
+        normalized property set with all implicit features expressed
+        explicitly, all subfeature values individually expressed, and all
+        components of composite properties expanded. Non-free features
+        directly expressed in the input properties cause any values of
+        those features due to composite feature expansion to be dropped. If
+        two values of a given non-free feature are directly expressed in the
+        input, an error is issued.
+    """
+    expanded = expand_subfeatures (properties)
+    return expand_composites (expanded)
+    
+
+def split_top_feature (feature_plus):
+    """ Given an ungristed string, finds the longest prefix which is a
+        top-level feature name followed by a dash, and return a pair
+        consisting of the parts before and after that dash.  More
+        interesting than a simple split because feature names can contain
+        dashes.
+    """
+    e = feature_plus.split ('-')
+    f = e [0]
+
+    v = None
+    while e:
+        if add_grist (f) in __all_top_features:
+            if len (e) > 1:
+                after = '-'.join (e [1:])
+            else:
+                after = ''
+                
+            v = (f, after)
+
+        e = e [1:]
+        f = f + '-'
+        if len (e): f += e [0]
+
+    return v
+
+def add_defaults (properties):
+    """ Given a set of properties, add default values for features not
+        represented in the set. 
+        Note: if there's there's ordinary feature F1 and composite feature
+        F2, which includes some value for F1, and both feature have default values,
+        then the default value of F1 will be added, not the value in F2. This might
+        not be right idea: consider
+        
+          feature variant : debug ... ;
+               <variant>debug : .... <runtime-debugging>on
+          feature <runtime-debugging> : off on ;
+          
+          Here, when adding default for an empty property set, we'll get
+        
+            <variant>debug <runtime_debugging>off
+         
+          and that's kind of strange.        
+    """
+    result = [ x for x in properties ]
+    
+    for v in replace_grist (properties, ''):
+        if v in properties:
+            raise BaseException ("'add_defaults' requires explicitly specified features, but '%s' appears to be the value of an un-expanded implicit feature" % v)
+
+    # We don't add default for elements with ":" inside. This catches:
+    # 1. Conditional properties --- we don't want <variant>debug:<define>DEBUG
+    #    to be takes as specified value for <variant>
+    # 2. Free properties with ":" in values. We don't care, since free properties
+    #    don't have defaults.
+    xproperties = [ property for property in properties if __re_no_hyphen.match (property) ]
+    missing_top = set.difference (__all_top_features, get_grist (xproperties))
+    more =  defaults (missing_top)
+    result += more
+    xproperties += more
+    
+    # Add defaults for subfeatures of features which are present
+    for p in xproperties:
+        gp = get_grist (p)
+        s = []
+        if __all_features.has_key (gp):
+            s = __all_features [gp]['subfeatures']
+        f = ungrist (gp)
+        
+        xbase = ['<%s-%s>' % (f, xs) for xs in s]
+            
+        missing_subs = set.difference (xbase, get_grist (result))
+        result += defaults (__select_subfeatures (p, missing_subs))
+    
+    return result
+
+def minimize (properties):
+    """ Given an expanded property set, eliminate all redundancy: properties
+        which are elements of other (composite) properties in the set will
+        be eliminated. Non-symmetric properties equal to default values will be
+        eliminated, unless the override a value from some composite property.
+        Implicit properties will be expressed without feature
+        grist, and sub-property values will be expressed as elements joined
+        to the corresponding main property.
+    """    
+# FXIME: the code below was in the original feature.jam file, however 'p' is not defined.
+#       # Precondition checking
+#       local implicits = [ set.intersection $(p:G=) : $(p:G) ] ;
+#       if $(implicits)
+#       {
+#           error minimize requires an expanded property set, but \"$(implicits[1])\"
+#             appears to be the value of an un-expanded implicit feature ;
+#       }
+           
+    # remove properties implied by composite features
+    components = []
+    for property in properties:
+        if __composite_properties.has_key (property):
+            components.extend (__composite_properties [property]['components'])
+    
+    x = set.difference (properties, components)
+    
+    # handle subfeatures and implicit features
+    x = __move_subfeatures_to_the_end (x)
+    
+    result = []
+    while x:
+        fullp = x [0]
+        p = fullp
+        f = get_grist (p)
+        v = replace_grist (p, '')
+        
+        # eliminate features in implicit properties.
+        if 'implicit' in __all_features [f]['attributes']:
+            p = v
+
+        # locate all subproperties of $(x[1]) in the property set
+        subproperties = __select_subproperties (fullp, x)
+        
+        if subproperties:
+            # reconstitute the joined property name
+            subproperties.sort ()
+            joined = p + '-' + '-'.join (replace_grist (subproperties, ''))
+            result.append (joined)
+
+            x = set.difference (x [1:], subproperties)
+
+        else:
+            # eliminate properties whose value is equal to feature's
+            # default and which are not symmetric and which do not
+            # contradict values implied by composite properties.
+            
+            # since all component properties of composites in the set
+            # have been eliminated, any remaining property whose
+            # feature is the same as a component of a composite in the
+            # set must have a non-redundant value.
+            if [fullp] != defaults ([f]) or 'symmetric' in attributes (f)\
+                   or get_grist (fullp) in get_grist (components):
+                result.append (p)
+
+            x = x [1:]
+
+    return result
+
+
+def split (properties):
+    """ Given a property-set of the form
+        v1/v2/...vN-1/<fN>vN/<fN+1>vN+1/...<fM>vM
+
+    Returns
+        v1 v2 ... vN-1 <fN>vN <fN+1>vN+1 ... <fM>vM
+
+    Note that vN...vM may contain slashes. This is resilient to the
+    substitution of backslashes for slashes, since Jam, unbidden,
+    sometimes swaps slash direction on NT.
+    """
+
+    def split_one (properties):
+        pieces = re.split (__re_slash_or_backslash, properties)
+        result = []
+        
+        for x in pieces:
+            if not get_grist (x) and len (result) > 0 and get_grist (result [-1]):
+                result = result [0:-1] + [ result [-1] + '/' + x ]
+            else:
+                result.append (x)
+        
+        return result
+
+    if isinstance (properties, str):
+        return split_one (properties)
+
+    result = []
+    for p in properties:
+        result += split_one (p)
+    return result
+    
+
+def compress_subproperties (properties):
+    """ Combine all subproperties into their parent properties
+
+        Requires: for every subproperty, there is a parent property.  All
+        features are explicitly expressed.
+        
+        This rule probably shouldn't be needed, but
+        build-request.expand-no-defaults is being abused for unintended
+        purposes and it needs help
+    """
+    result = []
+    matched_subs = []
+    for p in properties:
+        pg = get_grist (p)
+        if not pg:
+            raise BaseException ("Gristed variable exppected. Got '%s'." % p)
+        
+        if not 'subfeature' in __all_features [pg]['attributes']:
+            subs = __select_subproperties (p, properties)
+            
+            matched_subs.extend (subs)
+
+            subvalues = '-'.join (get_value (subs))
+            if subvalues: subvalues = '-' + subvalues
+
+            result.append (p + subvalues)
+
+        else:
+            all_subs.append (p)
+
+    # TODO: this variables are used just for debugging. What's the overhead?
+    assert (set.equal (all_subs, matched_subs))
+
+    return result
+
+######################################################################################
+# Private methods
+
+def __select_subproperties (parent_property, properties):
+    return [ x for x in properties if __is_subproperty_of (parent_property, x) ]
+
+def __move_subfeatures_to_the_end (properties):
+    """ Helper for minimize, below - returns the list with
+        the same properties, but where all subfeatures
+        are in the end of the list
+    """
+    x1 = []
+    x2 = []
+    for p in properties:
+        if 'subfeature' in __all_features [get_grist (p)]['attributes']:
+            x2.append (p)
+
+        else:
+            x1.append (p)
+            
+    return x1 + x2
+
+def __get_subfeature_name (subfeature, value_string):
+    if value_string == None: 
+        prefix = ''
+    else:
+        prefix = value_string + ':'
+
+    return prefix + subfeature
+
+
+def __validate_feature_attributes (name, attributes):
+    for attribute in attributes:
+        if not attribute in __all_attributes:
+            raise InvalidAttribute ("unknown attributes: '%s' in feature declaration: '%s'" % (str (set.difference (attributes, __all_attributes)), name))
+    
+    if name in __all_features:
+            raise AlreadyDefined ("feature '%s' already defined" % name)
+    elif 'implicit' in attributes and 'free' in attributes:
+        raise InvalidAttribute ("free features cannot also be implicit (in declaration of feature '%s')" % name)
+    elif 'free' in attributes and 'propagated' in attributes:
+        raise InvalidAttribute ("free features cannot also be propagated (in declaration of feature '%s')" % name)
+
+    
+def __validate_feature (feature):
+    """ Generates an error if the feature is unknown.
+    """
+    if not __all_features.has_key (feature):
+        raise BaseException ('unknown feature "%s"' % feature)
+
+def __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues):
+    # provide a way to get from the given feature or property and
+    # subfeature value to the subfeature name.
+    if value_string == None: value_string = ''
+    
+    if not __subfeature_value_to_name.has_key (feature):
+        __subfeature_value_to_name [feature] = {}
+        
+    if not __subfeature_value_to_name [feature].has_key (value_string):
+        __subfeature_value_to_name [feature][value_string] = {}
+        
+    for subvalue in subvalues:
+        __subfeature_value_to_name [feature][value_string][subvalue] = subfeature_name
+    
+
+def __select_subfeatures (parent_property, features):
+    """ Given a property, return the subset of features consisting of all
+        ordinary subfeatures of the property's feature, and all specific
+        subfeatures of the property's feature which are conditional on the
+        property's value.
+    """
+    return [f for f in features if is_subfeature_of (parent_property, f)]
+  
+# FIXME: copy over tests.
Added: trunk/tools/build/v2/build/generators.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/generators.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,967 @@
+# Status: being ported by Vladimir Prus
+# Base revision: 41557
+# TODO: replace the logging with dout
+
+# Copyright Vladimir Prus 2002.
+# Copyright Rene Rivera 2006.
+#
+# Distributed under the Boost Software License, Version 1.0.
+#    (See accompanying file LICENSE_1_0.txt or copy at
+#          http://www.boost.org/LICENSE_1_0.txt)
+
+#  Manages 'generators' --- objects which can do transformation between different
+#  target types and contain algorithm for finding transformation from sources
+#  to targets.
+#
+#  The main entry point to this module is generators.construct rule. It is given
+#  a list of source targets, desired target type and a set of properties.
+#  It starts by selecting 'viable generators', which have any chances of producing
+#  the desired target type with the required properties. Generators are ranked and
+#  a set of most specific ones is selected.
+# 
+#  The most specific generators have their 'run' methods called, with the properties
+#  and list of sources. Each one selects target which can be directly consumed, and
+#  tries to convert the remaining ones to the types it can consume. This is done
+#  by recursively calling 'construct' with all consumable types.
+#
+#  If the generator has collected all the targets it needs, it creates targets 
+#  corresponding to result, and returns it. When all generators have been run,
+#  results of one of them are selected and returned as result.
+#
+#  It's quite possible that 'construct' returns more targets that it was asked for.
+#  For example, it was asked to target type EXE, but the only found generators produces
+#  both EXE and TDS (file with debug) information. The extra target will be returned.
+#
+#  Likewise, when generator tries to convert sources to consumable types, it can get
+#  more targets that it was asked for. The question is what to do with extra targets.
+#  Boost.Build attempts to convert them to requested types, and attempts as early as
+#  possible. Specifically, this is done after invoking each generator. (Later I'll 
+#  document the rationale for trying extra target conversion at that point).
+#
+#  That early conversion is not always desirable. Suppose a generator got a source of
+#  type Y and must consume one target of type X_1 and one target of type X_2.
+#  When converting Y to X_1 extra target of type Y_2 is created. We should not try to
+#  convert it to type X_1, because if we do so, the generator will get two targets
+#  of type X_1, and will be at loss as to which one to use. Because of that, the
+#  'construct' rule has a parameter, telling if multiple targets can be returned. If
+#  the parameter is false, conversion of extra targets is not performed.
+
+
+import re
+import cStringIO
+import os.path
+
+from virtual_target import Subvariant
+import virtual_target, type, property_set, property
+from b2.util.logger import *
+from b2.util.utility import *
+from b2.util import set
+from b2.util.sequence import unique
+import b2.util.sequence as sequence
+from b2.manager import get_manager
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __generators, __type_to_generators, __generators_for_toolset, __construct_stack
+    global __overrides, __active_generators
+    global __viable_generators_cache, __viable_source_types_cache
+
+    __generators = {}
+    __type_to_generators = {}
+    __generators_for_toolset = {}
+    __overrides = {}
+    
+    # TODO: can these be global? 
+    __construct_stack = []
+    __viable_generators_cache = {}
+    __viable_source_types_cache = {}
+    __active_generators = []
+
+reset ()
+
+_re_separate_types_prefix_and_postfix = re.compile ('([^\\(]*)(\\((.*)%(.*)\\))?')
+_re_match_type = re.compile('([^\\(]*)(\\(.*\\))?')
+
+
+__debug = None
+__indent = ""
+
+def debug():
+    global __debug
+    if __debug is None:
+        __debug = "--debug-generators" in bjam.variable("ARGV")        
+    return __debug
+
+def increase_indent():
+    global __indent
+    __indent += "    "
+
+def decrease_indent():
+    global __indent
+    __indent = __indent[0:-4]
+
+def dout(message):
+    if debug():
+        print __indent + message
+
+def normalize_target_list (targets):
+    """ Takes a vector of 'virtual-target' instances and makes a normalized
+        representation, which is the same for given set of targets,
+        regardless of their order.
+    """
+    return (targets[0], targets[1].sort ())
+
+
+class Generator:
+    """ Creates a generator.
+            manager:                 the build manager.
+            id:                      identifies the generator
+            
+            rule:                    the rule which sets up build actions.
+
+            composing:               whether generator processes each source target in
+                                     turn, converting it to required types.
+                                     Ordinary generators pass all sources together to
+                                     recusrive generators.construct_types call.
+
+            source_types (optional): types that this generator can handle
+    
+            target_types_and_names:  types the generator will create and, optionally, names for
+                                     created targets. Each element should have the form
+                                         type["(" name-pattern ")"]
+                                     for example, obj(%_x). Name of generated target will be found
+                                     by replacing % with the name of source, provided explicit name
+                                     was not specified.
+    
+            requirements (optional)
+            
+            NOTE: all subclasses must have a similar signature for clone to work!
+    """
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        assert(not isinstance(source_types, str))
+        assert(not isinstance(target_types_and_names, str))
+        self.id_ = id
+        self.composing_ = composing
+        self.source_types_ = source_types
+        self.target_types_and_names_ = target_types_and_names
+        self.requirements_ = requirements
+        
+        self.target_types_ = []
+        self.name_prefix_ = []
+        self.name_postfix_ = []
+        
+        for e in target_types_and_names:
+            # Create three parallel lists: one with the list of target types,
+            # and two other with prefixes and postfixes to be added to target 
+            # name. We use parallel lists for prefix and postfix (as opposed
+            # to mapping), because given target type might occur several times,
+            # for example "H H(%_symbols)".
+            m = _re_separate_types_prefix_and_postfix.match (e)
+            
+            if not m:
+                raise BaseException ("Invalid type and name '%s' in declaration of type '%s'" % (e, id))
+            
+            target_type = m.group (1)
+            if not target_type: target_type = ''
+            prefix = m.group (3)
+            if not prefix: prefix = ''
+            postfix = m.group (4)
+            if not postfix: postfix = ''
+            
+            self.target_types_.append (target_type)
+            self.name_prefix_.append (prefix)
+            self.name_postfix_.append (postfix)
+
+        for x in self.source_types_:
+            type.validate (x)
+
+        for x in self.target_types_:
+            type.validate (x)
+
+    def clone (self, new_id, new_toolset_properties):
+        """ Returns another generator which differers from $(self) in
+              - id
+              - value to <toolset> feature in properties
+        """
+        return self.__class__ (new_id, 
+                               self.composing_, 
+                               self.source_types_, 
+                               self.target_types_and_names_,
+                               # Note: this does not remove any subfeatures of <toolset>
+                               # which might cause problems
+                               property.change (self.requirements_, '<toolset>') + new_toolset_properties)
+
+    def clone_and_change_target_type(self, base, type):
+        """Creates another generator that is the same as $(self), except that
+        if 'base' is in target types of $(self), 'type' will in target types
+        of the new generator."""
+        target_types = []
+        for t in self.target_types_and_names_:
+            m = _re_match_type.match(t)
+            assert m
+            
+            if m.group(1) == base:
+                if m.group(2):
+                    target_types.append(type + m.group(2))
+                else:
+                    target_types.append(type)
+            else:
+                target_types.append(t)
+
+        return self.__class__(self.id_, self.composing_,
+                              self.source_types_,
+                              target_types,
+                              self.requirements_)
+                              
+
+    def id (self):
+        return self.id_
+
+    def source_types (self):
+        """ Returns the list of target type the generator accepts.
+        """
+        return self.source_types_
+
+    def target_types (self):
+        """ Returns the list of target types that this generator produces.
+            It is assumed to be always the same -- i.e. it cannot change depending
+            list of sources.    
+        """
+        return self.target_types_
+
+    def requirements (self):
+        """ Returns the required properties for this generator. Properties
+            in returned set must be present in build properties if this 
+            generator is to be used. If result has grist-only element,
+            that build properties must include some value of that feature.
+        """
+        return self.requirements_
+
+    def match_rank (self, property_set_to_match):
+        """ Returns true if the generator can be run with the specified 
+            properties.
+        """
+        # See if generator's requirements are satisfied by
+        # 'properties'.  Treat a feature name in requirements
+        # (i.e. grist-only element), as matching any value of the
+        # feature.
+        all_requirements = self.requirements ()
+        
+        property_requirements = []
+        feature_requirements = []
+        for r in all_requirements:
+            if get_value (r):
+                property_requirements.append (r)
+
+            else:
+                feature_requirements.append (r)
+
+        properties_to_match = property_set_to_match.raw ()
+        
+        return set.contains (property_requirements, properties_to_match) \
+            and set.contains (feature_requirements, get_grist (properties_to_match))
+        
+    def run (self, project, name, prop_set, sources):
+        """ Tries to invoke this generator on the given sources. Returns a
+            list of generated targets (instances of 'virtual-target').
+
+            project:        Project for which the targets are generated.
+            
+            name:           Determines the name of 'name' attribute for 
+                            all generated targets. See 'generated_targets' method.
+                            
+            prop_set:       Desired properties for generated targets.
+            
+            sources:        Source targets.
+        """
+        
+        if project.manager ().logger ().on ():
+            project.manager ().logger ().log (__name__, "  generator '%s'" % self.id_)
+            project.manager ().logger ().log (__name__, "  composing: '%s'" % self.composing_)
+        
+        if not self.composing_ and len (sources) > 1 and len (self.source_types_) > 1:
+            raise BaseException ("Unsupported source/source_type combination")
+                
+        # We don't run composing generators if no name is specified. The reason
+        # is that composing generator combines several targets, which can have
+        # different names, and it cannot decide which name to give for produced
+        # target. Therefore, the name must be passed.
+        #
+        # This in effect, means that composing generators are runnable only
+        # at top-level of transofrmation graph, or if name is passed explicitly.
+        # Thus, we dissallow composing generators in the middle. For example, the
+        # transofrmation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE won't be allowed 
+        # (the OBJ -> STATIC_LIB generator is composing)
+        if not self.composing_ or name:
+            return self.run_really (project, name, prop_set, sources)
+        else:
+            return []
+
+    def run_really (self, project, name, prop_set, sources):
+
+        # consumed: Targets that this generator will consume directly.
+        # bypassed: Targets that can't be consumed and will be returned as-is.
+        
+        if self.composing_:
+            (consumed, bypassed) = self.convert_multiple_sources_to_consumable_types (project, prop_set, sources)
+        else:
+            (consumed, bypassed) = self.convert_to_consumable_types (project, name, prop_set, sources)
+                
+        result = []
+        if consumed:
+            result = self.construct_result (consumed, project, name, prop_set)
+            result.extend (bypassed)
+
+        if result:
+            if project.manager ().logger ().on ():
+                project.manager ().logger ().log (__name__, "  SUCCESS: ", result)
+
+        else:
+                project.manager ().logger ().log (__name__, "  FAILURE")
+
+        return result
+
+    def construct_result (self, consumed, project, name, prop_set):
+        """ Constructs the dependency graph that will be returned by this 
+            generator.
+                consumed:        Already prepared list of consumable targets
+                                 If generator requires several source files will contain 
+                                 exactly len $(self.source_types_) targets with matching types
+                                 Otherwise, might contain several targets with the type of 
+                                 self.source_types_ [0]
+                project:
+                name:
+                prop_set:        Properties to be used for all actions create here
+        """
+        result = []
+        # If this is 1->1 transformation, apply it to all consumed targets in order.
+        if len (self.source_types_) < 2 and not self.composing_:
+
+            for r in consumed:
+                result.extend (self.generated_targets ([r], prop_set, project, name))
+
+        else:
+
+            if consumed:
+                result.extend (self.generated_targets (consumed, prop_set, project, name))
+
+        return result
+
+    def determine_output_name(self, sources):
+        """Determine the name of the produced target from the
+        names of the sources."""
+        
+        # The simple case if when a name
+        # of source has single dot. Then, we take the part before
+        # dot. Several dots can be caused by:
+        # - Using source file like a.host.cpp
+        # - A type which suffix has a dot. Say, we can
+        #   type 'host_cpp' with extension 'host.cpp'.
+        # In the first case, we want to take the part till the last
+        # dot. In the second case -- no sure, but for now take
+        # the part till the last dot too.
+        name = os.path.splitext(sources[0].name())[0]
+                        
+        for s in sources[1:]:
+            n2 = os.path.splitext(s.name())
+            if n2 != name:
+                get_manager().errors()(
+                    "%s: source targets have different names: cannot determine target name"
+                    % (self.id_))
+                        
+        # Names of sources might include directory. We should strip it.        
+        return os.path.basename(name)
+        
+        
+    def generated_targets (self, sources, prop_set, project, name):
+        """ Constructs targets that are created after consuming 'sources'.
+            The result will be the list of virtual-target, which the same length
+            as 'target_types' attribute and with corresponding types.
+            
+            When 'name' is empty, all source targets must have the same value of 
+            the 'name' attribute, which will be used instead of the 'name' argument.
+            
+            The value of 'name' attribute for each generated target will be equal to
+            the 'name' parameter if there's no name pattern for this type. Otherwise,
+            the '%' symbol in the name pattern will be replaced with the 'name' parameter 
+            to obtain the 'name' attribute.
+            
+            For example, if targets types are T1 and T2(with name pattern "%_x"), suffixes
+            for T1 and T2 are .t1 and t2, and source if foo.z, then created files would
+            be "foo.t1" and "foo_x.t2". The 'name' attribute actually determined the
+            basename of a file.
+            
+            Note that this pattern mechanism has nothing to do with implicit patterns
+            in make. It's a way to produce target which name is different for name of 
+            source.
+        """
+        if not name:
+            name = self.determine_output_name(sources)
+        
+        # Assign an action for each target
+        action = self.action_class()        
+        a = action (project.manager(), sources, self.id_, prop_set)
+                
+        # Create generated target for each target type.
+        targets = []
+        pre = self.name_prefix_
+        post = self.name_postfix_
+        for t in self.target_types_:
+            generated_name = pre[0] + name + post[0]
+            pre = pre[1:]
+            post = post[1:]
+            
+            targets.append(virtual_target.FileTarget(generated_name, False, t, project, a))
+        
+        return [ project.manager().virtual_targets().register(t) for t in targets ]
+
+    def convert_to_consumable_types (self, project, name, prop_set, sources, only_one=False):
+        """ Attempts to convert 'source' to the types that this generator can
+            handle. The intention is to produce the set of targets can should be
+            used when generator is run.
+            only_one:   convert 'source' to only one of source types
+                        if there's more that one possibility, report an
+                        error.
+                        
+            Returns a pair:
+                consumed: all targets that can be consumed. 
+                bypassed: all targets that cannot be consumed.
+        """
+        consumed = []
+        bypassed = []
+        missing_types = [] 
+
+        if len (sources) > 1:
+            # Don't know how to handle several sources yet. Just try 
+            # to pass the request to other generator
+            missing_types = self.source_types_
+
+        else:
+            (c, m) = self.consume_directly (sources [0])
+            consumed += c
+            missing_types += m
+        
+        # No need to search for transformation if
+        # some source type has consumed source and
+        # no more source types are needed.
+        if only_one and consumed:
+            missing_types = []
+            
+        #TODO: we should check that only one source type
+        #if create of 'only_one' is true.
+        # TODO: consider if consuned/bypassed separation should
+        # be done by 'construct_types'.
+                    
+        if missing_types:
+            transformed = construct_types (project, name, missing_types, prop_set, sources)
+                                
+            # Add targets of right type to 'consumed'. Add others to
+            # 'bypassed'. The 'generators.construct' rule has done
+            # its best to convert everything to the required type.
+            # There's no need to rerun it on targets of different types.
+                
+            # NOTE: ignoring usage requirements
+            for t in transformed[1]:
+                if t.type() in missing_types:
+                    consumed.append(t)
+
+                else:
+                    bypassed.append(t)
+        
+        consumed = unique(consumed)
+        bypassed = unique(bypassed)
+        
+        # remove elements of 'bypassed' that are in 'consumed'
+        
+        # Suppose the target type of current generator, X is produced from 
+        # X_1 and X_2, which are produced from Y by one generator.
+        # When creating X_1 from Y, X_2 will be added to 'bypassed'
+        # Likewise, when creating X_2 from Y, X_1 will be added to 'bypassed'
+        # But they are also in 'consumed'. We have to remove them from
+        # bypassed, so that generators up the call stack don't try to convert
+        # them. 
+
+        # In this particular case, X_1 instance in 'consumed' and X_1 instance
+        # in 'bypassed' will be the same: because they have the same source and
+        # action name, and 'virtual-target.register' won't allow two different
+        # instances. Therefore, it's OK to use 'set.difference'.
+        
+        bypassed = set.difference(bypassed, consumed)
+
+        return (consumed, bypassed)
+    
+
+    def convert_multiple_sources_to_consumable_types (self, project, prop_set, sources):
+        """ Converts several files to consumable types.
+        """        
+        consumed = []
+        bypassed = []
+
+        # We process each source one-by-one, trying to convert it to
+        # a usable type.
+        for s in sources:
+            # TODO: need to check for failure on each source.
+            (c, b) = self.convert_to_consumable_types (project, None, prop_set, [s], True)
+            if not c:
+                project.manager ().logger ().log (__name__, " failed to convert ", s)
+
+            consumed.extend (c)
+            bypassed.extend (b)
+
+        return (consumed, bypassed)
+
+    def consume_directly (self, source):
+        real_source_type = source.type ()
+
+        # If there are no source types, we can consume anything
+        source_types = self.source_types
+        if not source_types:
+            source_types = [real_source_type]
+
+        consumed = []
+        missing_types = []
+        for st in self.source_types_:
+            # The 'source' if of right type already)
+            if real_source_type == st or type.is_derived (real_source_type, st):
+                consumed.append (source)
+
+            else:
+               missing_types.append (st)
+       
+        return (consumed, missing_types)
+    
+    def action_class (self):
+        """ Returns the class to be used to actions. Default implementation 
+            returns "action".
+        """
+        return virtual_target.Action
+
+
+def find (id):
+    """ Finds the generator with id. Returns None if not found.
+    """
+    return __generators.get (id, None)
+
+def register (g):
+    """ Registers new generator instance 'g'.
+    """
+    id = g.id ()
+
+    __generators [id] = g
+
+    # A generator can produce several targets of the
+    # same type. We want unique occurence of that generator
+    # in .generators.$(t) in that case, otherwise, it will
+    # be tried twice and we'll get false ambiguity.
+    for t in sequence.unique(g.target_types()):
+        __type_to_generators.setdefault(t, []).append(g)
+
+    # Update the set of generators for toolset
+
+    # TODO: should we check that generator with this id
+    # is not already registered. For example, the fop.jam
+    # module intentionally declared two generators with the
+    # same id, so such check will break it.
+
+    # Some generators have multiple periods in their name, so the
+    # normal $(id:S=) won't generate the right toolset name.
+    # e.g. if id = gcc.compile.c++, then
+    # .generators-for-toolset.$(id:S=) will append to
+    # .generators-for-toolset.gcc.compile, which is a separate
+    # value from .generators-for-toolset.gcc. Correcting this
+    # makes generator inheritance work properly.
+    # See also inherit-generators in module toolset
+    base = id.split ('.', 100) [0]
+
+    __generators_for_toolset.setdefault(base, []).append(g)
+
+def register_standard (id, source_types, target_types, requirements = []):
+    """ Creates new instance of the 'generator' class and registers it.
+        Returns the creates instance.
+        Rationale: the instance is returned so that it's possible to first register
+        a generator and then call 'run' method on that generator, bypassing all
+        generator selection.
+    """
+    g = Generator (id, False, source_types, target_types, requirements)
+    register (g)
+    return g
+
+def register_composing (id, source_types, target_types, requirements = []):
+    g = Generator (id, True, source_types, target_types, requirements)
+    register (g)
+    return g
+
+def generators_for_toolset (toolset):
+    """ Returns all generators which belong to 'toolset'.
+    """
+    return __generators_for_toolset.get(toolset, [])
+
+def override (overrider_id, overridee_id):
+    """Make generator 'overrider-id' be preferred to
+    'overridee-id'. If, when searching for generators
+    that could produce a target of certain type,
+    both those generators are amoung viable generators,
+    the overridden generator is immediately discarded.
+    
+    The overridden generators are discarded immediately
+    after computing the list of viable generators, before
+    running any of them."""
+    
+    __overrides.get(overrider_id, []).append(overridee_id)
+
+def __viable_source_types_real (target_type):
+    """ Returns a list of source type which can possibly be converted
+        to 'target_type' by some chain of generator invocation.
+        
+        More formally, takes all generators for 'target_type' and
+        returns union of source types for those generators and result
+        of calling itself recusrively on source types.
+    """
+    generators = []
+        
+    t = type.all_bases (target_type)
+    
+    result = []
+    # 't' is the list of types which are not yet processed    
+    while t:
+        # Find all generators for current type. 
+        # Unlike 'find_viable_generators' we don't care about prop_set.
+        generators = __type_to_generators.get (t [0], [])
+        t = t[1:]
+        
+        for g in generators:
+            if not g.source_types():
+                # Empty source types -- everything can be accepted
+                result = "*"
+                # This will terminate outer loop.
+                t = None
+                break
+            
+            for source_type in g.source_types ():
+                if not source_type in result:
+                    # If generator accepts 'source_type' it
+                    # will happily accept any type derived from it
+                    all = type.all_derived (source_type)
+                    for n in all:
+                        if not n in result:
+                            t.append (n)
+                            result.append (n)
+        
+    result = unique (result)
+    
+    return result
+
+
+def viable_source_types (target_type):
+    """ Helper rule, caches the result of '__viable_source_types_real'.
+    """
+    if not __viable_source_types_cache.has_key (target_type):
+         __viable_source_types_cache [target_type] = __viable_source_types_real (target_type)
+    return __viable_source_types_cache [target_type]
+
+def viable_source_types_for_generator_real (generator):
+    """ Returns the list of source types, which, when passed to 'run'
+        method of 'generator', has some change of being eventually used
+        (probably after conversion by other generators)
+    """
+    source_types = generator.source_types ()
+
+    if not source_types:
+        # If generator does not specify any source types,
+        # it might be special generator like builtin.lib-generator
+        # which just relays to other generators. Return '*' to
+        # indicate that any source type is possibly OK, since we don't
+        # know for sure.
+        return ['*']
+
+    else:
+        result = []
+        for s in source_types:
+            result += type.all_derived (s) + viable_source_types (s)            
+        result = unique (result)
+        if "*" in result:
+            result = ["*"]
+        return result
+
+def viable_source_types_for_generator (generator):
+    """ Caches the result of 'viable_source_types_for_generator'.
+    """
+    key = str (generator)
+    if not __viable_source_types_cache.has_key (key):
+        __viable_source_types_cache [key] = viable_source_types_for_generator_real (generator)
+    
+    return __viable_source_types_cache [key]
+
+def try_one_generator_really (project, name, generator, target_type, properties, sources):
+    """ Returns usage requirements + list of created targets.
+    """
+    targets = generator.run (project, name, properties, sources)
+
+    usage_requirements = []
+    success = False
+
+    dout("returned " + str(targets))
+
+    if targets:
+        success = True;
+        
+        if isinstance (targets[0], property_set.PropertySet):
+            usage_requirements = targets [0]
+            targets = targets [1]
+
+        else:
+            usage_requirements = property_set.empty ()
+
+    dout(  "  generator" + generator.id() + " spawned ")
+    #    generators.dout [ indent ] " " $(targets) ; 
+#    if $(usage-requirements)
+#    {
+#        generators.dout [ indent ] "  with usage requirements:" $(x) ;
+#    }
+
+    if success:
+        return (usage_requirements, targets)
+    else:
+        return None
+
+def try_one_generator (project, name, generator, target_type, properties, sources):
+    """ Checks if generator invocation can be pruned, because it's guaranteed
+        to fail. If so, quickly returns empty list. Otherwise, calls
+        try_one_generator_really.
+    """
+    source_types = []
+
+    for s in sources:
+        source_types.append (s.type ())
+
+    viable_source_types = viable_source_types_for_generator (generator)
+    
+    if source_types and viable_source_types != ['*'] and\
+           not set.intersection (source_types, viable_source_types):
+        if project.manager ().logger ().on ():
+            id = generator.id ()            
+            project.manager ().logger ().log (__name__, "generator '%s' pruned" % id)
+            project.manager ().logger ().log (__name__, "source_types" '%s' % source_types)
+            project.manager ().logger ().log (__name__, "viable_source_types '%s'" % viable_source_types)
+        
+        return []
+
+    else:
+        return try_one_generator_really (project, name, generator, target_type, properties, sources)
+
+
+def construct_types (project, name, target_types, prop_set, sources):
+    
+    result = []
+    usage_requirements = property_set.empty()
+    
+    for t in target_types:
+        r = construct (project, name, t, prop_set, sources)
+
+        if r:
+            (ur, targets) = r
+            usage_requirements = usage_requirements.add(ur)
+            result.extend(targets)
+
+    # TODO: have to introduce parameter controlling if
+    # several types can be matched and add appropriate
+    # checks 
+
+    # TODO: need to review the documentation for
+    # 'construct' to see if it should return $(source) even
+    # if nothing can be done with it. Currents docs seem to
+    # imply that, contrary to the behaviour.
+    if result:
+        return (usage_requirements, result)
+
+    else:
+        return (usage_requirements, sources)
+
+def __ensure_type (targets):
+    """ Ensures all 'targets' have types. If this is not so, exists with 
+        error.
+    """
+    for t in targets:
+        if not t.type ():
+            raise BaseException ("target '%s' has no type" % str (t))
+
+def find_viable_generators_aux (target_type, prop_set):
+    """ Returns generators which can be used to construct target of specified type
+        with specified properties. Uses the following algorithm:
+        - iterates over requested target_type and all it's bases (in the order returned bt
+          type.all-bases.
+        - for each type find all generators that generate that type and which requirements
+          are satisfied by properties.
+        - if the set of generators is not empty, returns that set.
+        
+        Note: this algorithm explicitly ignores generators for base classes if there's
+        at least one generator for requested target_type.
+    """
+    # Select generators that can create the required target type.
+    viable_generators = []
+    initial_generators = []
+
+    import type
+
+    # Try all-type generators first. Assume they have
+    # quite specific requirements.
+    all_bases = type.all_bases(target_type)
+        
+    for t in all_bases:
+        
+        initial_generators = __type_to_generators.get(t, [])
+        
+        if initial_generators:
+            dout("there are generators for this type")
+            if t != target_type:
+                # We're here, when no generators for target-type are found,
+                # but there are some generators for a base type.
+                # We'll try to use them, but they will produce targets of
+                # base type, not of 'target-type'. So, we clone the generators
+                # and modify the list of target types.
+                generators2 = []
+                for g in initial_generators[:]:
+                    # generators.register adds generator to the list of generators
+                    # for toolsets, which is a bit strange, but should work.
+                    # That list is only used when inheriting toolset, which
+                    # should have being done before generators are run.
+                    ng = g.clone_and_change_target_type(t, target_type)
+                    generators2.append(ng)
+                    register(ng)
+                    
+                initial_generators = generators2
+            break
+    
+    for g in initial_generators:
+        dout("trying generator " + g.id()
+             + "(" + str(g.source_types()) + "->" + str(g.target_types()) + ")")
+        
+        m = g.match_rank(prop_set)
+        if m:
+            dout("  is viable")
+            viable_generators.append(g)
+                            
+    return viable_generators
+
+def find_viable_generators (target_type, prop_set):
+    key = target_type + '.' + str (prop_set)
+
+    l = __viable_generators_cache.get (key, None)
+
+    if not l:
+        l = find_viable_generators_aux (target_type, prop_set)
+
+        __viable_generators_cache [key] = l
+
+    viable_generators = []
+    for g in l:
+        # Avoid trying the same generator twice on different levels.
+        # TODO: is this really used?
+        if not g in __active_generators:
+            viable_generators.append (g)
+
+    # Generators which override 'all'.
+    all_overrides = []
+    
+    # Generators which are overriden
+    overriden_ids = [] 
+       
+    for g in viable_generators:
+        id = g.id ()
+        
+        this_overrides = __overrides.get (id, [])
+        
+        if this_overrides:
+            overriden_ids.extend (this_overrides)
+            if 'all' in this_overrides:
+                all_overrides.append (g)
+
+    if all_overrides:
+        viable_generators = all_overrides
+
+    result = []
+    for g in viable_generators:
+        if not g.id () in overriden_ids:
+            result.append (g)
+        
+    return result
+    
+def __construct_really (project, name, target_type, prop_set, sources):
+    """ Attempts to construct target by finding viable generators, running them
+        and selecting the dependency graph.
+    """
+    viable_generators = find_viable_generators (target_type, prop_set)
+                    
+    result = []
+    
+    project.manager ().logger ().log (__name__, "*** %d viable generators" % len (viable_generators))
+
+    generators_that_succeeded = []
+    
+    for g in viable_generators:
+        __active_generators.append(g)        
+        r = try_one_generator (project, name, g, target_type, prop_set, sources)
+        del __active_generators[-1]
+        
+        if r:
+            generators_that_succeeded.append(g)
+            if result:
+                output = cStringIO.StringIO()
+                print >>output, "ambiguity found when searching for best transformation"
+                print >>output, "Trying to produce type '%s' from: " % (target_type)
+                for s in sources:
+                    print >>output, " - " + s.str()
+                print >>output, "Generators that succeeded:"
+                for g in generators_that_succeeded:
+                    print >>output, " - " + g.id()
+                print >>output, "First generator produced: "
+                for t in result[1:]:
+                    print >>output, " - " + str(t)
+                print >>output, "Second generator produced:"
+                for t in r[1:]:
+                    print >>output, " - " + str(t)
+                get_manager().errors()(output.getvalue())
+            else:
+                result = r;
+            
+    return result;
+
+
+def construct (project, name, target_type, prop_set, sources):
+    """ Attempts to create target of 'target-type' with 'properties'
+        from 'sources'. The 'sources' are treated as a collection of
+        *possible* ingridients -- i.e. it is not required to consume
+        them all. If 'multiple' is true, the rule is allowed to return
+        several targets of 'target-type'.          
+        
+        Returns a list of target. When this invocation is first instance of
+        'construct' in stack, returns only targets of requested 'target-type',
+        otherwise, returns also unused sources and additionally generated
+        targets.            
+    """
+    # TODO: Why is global needed here?
+    global __construct_stack
+    if __construct_stack:
+        __ensure_type (sources)
+        
+    __construct_stack.append (1)
+
+    if project.manager().logger().on():
+        increase_indent ()
+        
+        dout( "*** construct " + target_type)
+        
+        for s in sources:
+            dout("    from " + str(s))
+
+        project.manager().logger().log (__name__, "    properties: ", prop_set.raw ())
+             
+    result = __construct_really(project, name, target_type, prop_set, sources)
+
+    project.manager().logger().decrease_indent()
+        
+    __construct_stack = __construct_stack [1:]
+
+    return result
+    
Added: trunk/tools/build/v2/build/project.ann.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/project.ann.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,996 @@
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   1) # Status: being ported by Vladimir Prus
+ddc17f01 (vladimir_prus 2007-10-26 14:57:56 +0000   2) # Base revision: 40480
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   3) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   4) # Copyright 2002, 2003 Dave Abrahams 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   5) # Copyright 2002, 2005, 2006 Rene Rivera 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   6) # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   7) # Distributed under the Boost Software License, Version 1.0. 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   8) # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000   9) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  10) #  Implements project representation and loading.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  11) #   Each project is represented by 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  12) #   - a module where all the Jamfile content live. 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  13) #   - an instance of 'project-attributes' class.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  14) #     (given module name, can be obtained by 'attributes' rule)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  15) #   - an instance of 'project-target' class (from targets.jam)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  16) #     (given a module name, can be obtained by 'target' rule)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  17) #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  18) #  Typically, projects are created as result of loading Jamfile, which is
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  19) #  do by rules 'load' and 'initialize', below. First, module for Jamfile
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  20) #  is loaded and new project-attributes instance is created. Some rules
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  21) #  necessary for project are added to the module (see 'project-rules' module)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  22) #  at the bottom of this file.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  23) #  Default project attributes are set (inheriting attributes of parent project, if
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  24) #  it exists). After that, Jamfile is read. It can declare its own attributes,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  25) #  via 'project' rule, which will be combined with already set attributes.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  26) #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  27) #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  28) #  The 'project' rule can also declare project id, which will be associated with 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  29) #  the project module.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  30) #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  31) #  There can also be 'standalone' projects. They are created by calling 'initialize'
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  32) #  on arbitrary module, and not specifying location. After the call, the module can
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  33) #  call 'project' rule, declare main target and behave as regular projects. However,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  34) #  since it's not associated with any location, it's better declare only prebuilt 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  35) #  targets.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  36) #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  37) #  The list of all loaded Jamfile is stored in variable .project-locations. It's possible
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  38) #  to obtain module name for a location using 'module-name' rule. The standalone projects
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  39) #  are not recorded, the only way to use them is by project id.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  40) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  41) import b2.util.path
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000  42) from b2.build import property_set, property
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000  43) from b2.build.errors import ExceptionWithUserContext
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  44) import b2.build.targets
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  45) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  46) import bjam
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  47) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  48) import re
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  49) import sys
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  50) import os
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  51) import string
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000  52) import imp
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000  53) import traceback
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  54) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  55) class ProjectRegistry:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  56) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  57)     def __init__(self, manager, global_build_dir):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  58)         self.manager = manager
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  59)         self.global_build_dir = None
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000  60)         self.project_rules_ = ProjectRules(self)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  61) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000  62)         # The target corresponding to the project being loaded now
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  63)         self.current_project = None
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  64)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  65)         # The set of names of loaded project modules
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  66)         self.jamfile_modules = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  67) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  68)         # Mapping from location to module name
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  69)         self.location2module = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  70) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  71)         # Mapping from project id to project module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  72)         self.id2module = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  73) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  74)         # Map from Jamfile directory to parent Jamfile/Jamroot
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  75)         # location.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  76)         self.dir2parent_jamfile = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  77) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  78)         # Map from directory to the name of Jamfile in
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  79)         # that directory (or None).
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  80)         self.dir2jamfile = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  81) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  82)         # Map from project module to attributes object.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  83)         self.module2attributes = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  84) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  85)         # Map from project module to target for the project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  86)         self.module2target = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  87) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000  88)         # Map from names to Python modules, for modules loaded
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000  89)         # via 'using' and 'import' rules in Jamfiles.
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000  90)         self.loaded_tool_modules_ = {}
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000  91) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  92)         # Map from project target to the list of
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  93)         # (id,location) pairs corresponding to all 'use-project'
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  94)         # invocations.
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  95)         # TODO: should not have a global map, keep this
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  96)         # in ProjectTarget.
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  97)         self.used_projects = {}
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000  98) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000  99)         self.saved_current_project = []
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 100) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 101)         self.JAMROOT = self.manager.getenv("JAMROOT");
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 102) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 103)         # Note the use of character groups, as opposed to listing
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 104)         # 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 105)         # matches on windows and would have to eliminate duplicates.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 106)         if not self.JAMROOT:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 107)             self.JAMROOT = ["project-root.jam", "[Jj]amroot", "[Jj]amroot.jam"]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 108) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 109)         # Default patterns to search for the Jamfiles to use for build
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 110)         # declarations.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 111)         self.JAMFILE = self.manager.getenv("JAMFILE")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 112) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 113)         if not self.JAMFILE:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 114)             self.JAMFILE = ["[Bb]uild.jam", "[Jj]amfile.v2", "[Jj]amfile",
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 115)                             "[Jj]amfile.jam"]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 116) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 117) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 118)     def load (self, jamfile_location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 119)         """Loads jamfile at the given location. After loading, project global
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 120)         file and jamfile needed by the loaded one will be loaded recursively.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 121)         If the jamfile at that location is loaded already, does nothing.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 122)         Returns the project module for the Jamfile."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 123) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 124)         absolute = os.path.join(os.getcwd(), jamfile_location)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 125)         absolute = os.path.normpath(absolute)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 126)         jamfile_location = b2.util.path.relpath(os.getcwd(), absolute)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 127) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 128)         if "--debug-loading" in self.manager.argv():
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 129)             print "Loading Jamfile at '%s'" % jamfile_location
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 130) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 131)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 132)         mname = self.module_name(jamfile_location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 133)         # If Jamfile is already loaded, don't try again.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 134)         if not mname in self.jamfile_modules:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 135)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 136)             self.load_jamfile(jamfile_location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 137)                 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 138)             # We want to make sure that child project are loaded only
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 139)             # after parent projects. In particular, because parent projects
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 140)             # define attributes whch are inherited by children, and we don't
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 141)             # want children to be loaded before parents has defined everything.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 142)             #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 143)             # While "build-project" and "use-project" can potentially refer
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 144)             # to child projects from parent projects, we don't immediately
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 145)             # load child projects when seing those attributes. Instead,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 146)             # we record the minimal information that will be used only later.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 147)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 148)             self.load_used_projects(mname)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 149)              
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 150)         return mname
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 151) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 152)     def load_used_projects(self, module_name):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 153)         # local used = [ modules.peek $(module-name) : .used-projects ] ;
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 154)         used = self.used_projects[module_name]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 155)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 156)         location = self.attribute(module_name, "location")
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 157)         for u in used:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 158)             id = u[0]
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 159)             where = u[1]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 160) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 161)             self.use(id, os.path.join(location, where))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 162) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 163)     def load_parent(self, location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 164)         """Loads parent of Jamfile at 'location'.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 165)         Issues an error if nothing is found."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 166) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 167)         found = b2.util.path.glob_in_parents(
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 168)             location, self.JAMROOT + self.JAMFILE) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 169) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 170)         if not found:
+1674e2d9 (jhunold       2008-08-08 19:52:05 +0000 171)             print "error: Could not find parent for project at '%s'" % location
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 172)             print "error: Did not find Jamfile or project-root.jam in any parent directory."
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 173)             sys.exit(1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 174)     
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 175)         return self.load(os.path.dirname(found[0]))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 176) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 177)     def act_as_jamfile(self, module, location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 178)         """Makes the specified 'module' act as if it were a regularly loaded Jamfile 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 179)         at 'location'. If Jamfile is already located for that location, it's an 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 180)         error."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 181) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 182)         if self.module_name(location) in self.jamfile_modules:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 183)             self.manager.errors()(
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 184)                 "Jamfile was already loaded for '%s'" % location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 185)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 186)         # Set up non-default mapping from location to module.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 187)         self.location2module[location] = module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 188)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 189)         # Add the location to the list of project locations
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 190)         # so that we don't try to load Jamfile in future
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 191)         self.jamfile_modules.append(location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 192)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 193)         self.initialize(module, location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 194) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 195)     def find(self, name, current_location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 196)         """Given 'name' which can be project-id or plain directory name,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 197)         return project module corresponding to that id or directory.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 198)         Returns nothing of project is not found."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 199) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 200)         project_module = None
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 201) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 202)         # Try interpreting name as project id.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 203)         if name[0] == '/':
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 204)             project_module = self.id2module.get(name)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 205) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 206)         if not project_module:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 207)             location = os.path.join(current_location, name)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 208)             # If no project is registered for the given location, try to
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 209)             # load it. First see if we have Jamfile. If not we might have project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 210)             # root, willing to act as Jamfile. In that case, project-root
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 211)             # must be placed in the directory referred by id.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 212)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 213)             project_module = self.module_name(location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 214)             if not project_module in self.jamfile_modules and \
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 215)                b2.util.path.glob([location], self.JAMROOT + self.JAMFILE):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 216)                 project_module = self.load(location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 217) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 218)         return project_module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 219) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 220)     def module_name(self, jamfile_location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 221)         """Returns the name of module corresponding to 'jamfile-location'.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 222)         If no module corresponds to location yet, associates default
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 223)         module name with that location."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 224)         module = self.location2module.get(jamfile_location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 225)         if not module:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 226)             # Root the path, so that locations are always umbiguious.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 227)             # Without this, we can't decide if '../../exe/program1' and '.'
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 228)             # are the same paths, or not.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 229)             jamfile_location = os.path.realpath(
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 230)                 os.path.join(os.getcwd(), jamfile_location))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 231)             module = "Jamfile<%s>" % jamfile_location
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 232)             self.location2module[jamfile_location] = module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 233)         return module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 234) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 235)     def find_jamfile (self, dir, parent_root=0, no_errors=0):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 236)         """Find the Jamfile at the given location. This returns the
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 237)         exact names of all the Jamfiles in the given directory. The optional
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 238)         parent-root argument causes this to search not the given directory
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 239)         but the ones above it up to the directory given in it."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 240)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 241)         # Glob for all the possible Jamfiles according to the match pattern.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 242)         #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 243)         jamfile_glob = None
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 244)         if parent_root:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 245)             parent = self.dir2parent_jamfile.get(dir)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 246)             if not parent:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 247)                 parent = b2.util.path.glob_in_parents(dir,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 248)                                                                self.JAMFILE)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 249)                 self.dir2parent_jamfile[dir] = parent
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 250)             jamfile_glob = parent
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 251)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 252)             jamfile = self.dir2jamfile.get(dir)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 253)             if not jamfile:
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 254)                 jamfile = b2.util.path.glob([dir], self.JAMFILE)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 255)                 self.dir2jamfile[dir] = jamfile
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 256)             jamfile_glob = jamfile
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 257) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 258)         if len(jamfile_glob):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 259)             # Multiple Jamfiles found in the same place. Warn about this.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 260)             # And ensure we use only one of them.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 261)             # As a temporary convenience measure, if there's Jamfile.v2 amount
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 262)             # found files, suppress the warning and use it.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 263)             #
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 264)             pattern = "(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 265)             v2_jamfiles = [x for x in jamfile_glob if re.match(pattern, x)]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 266)             if len(v2_jamfiles) == 1:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 267)                 jamfile_glob = v2_jamfiles
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 268)             else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 269)                 print """warning: Found multiple Jamfiles at '%s'!
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 270) Loading the first one: '%s'.""" % (dir, jamfile_glob[0])
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 271)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 272)         # Could not find it, error.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 273)         if not no_errors and not jamfile_glob:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 274)             self.manager.errors()(
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 275)                 """Unable to load Jamfile.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 276) Could not find a Jamfile in directory '%s'
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 277) Attempted to find it with pattern '%s'.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 278) Please consult the documentation at 'http://boost.org/b2.'."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 279)                 % (dir, string.join(self.JAMFILE)))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 280) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 281)         return jamfile_glob[0]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 282)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 283)     def load_jamfile(self, dir):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 284)         """Load a Jamfile at the given directory. Returns nothing.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 285)         Will attempt to load the file as indicated by the JAMFILE patterns.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 286)         Effect of calling this rule twice with the same 'dir' is underfined."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 287)       
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 288)         # See if the Jamfile is where it should be.
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 289)         jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 290)         if not jamfile_to_load:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 291)             jamfile_to_load = self.find_jamfile(dir)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 292)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 293)             jamfile_to_load = jamfile_to_load[0]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 294)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 295)         # The module of the jamfile.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 296)         dir = os.path.realpath(os.path.dirname(jamfile_to_load))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 297)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 298)         jamfile_module = self.module_name (dir)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 299) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 300)         # Initialize the jamfile module before loading.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 301)         #    
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 302)         self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 303) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 304)         saved_project = self.current_project
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 305) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 306)         self.used_projects[jamfile_module] = []
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 307)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 308)         # Now load the Jamfile in it's own context.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 309)         # Initialization might have load parent Jamfiles, which might have
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 310)         # loaded the current Jamfile with use-project. Do a final check to make
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 311)         # sure it's not loaded already.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 312)         if not jamfile_module in self.jamfile_modules:
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 313)             self.jamfile_modules[jamfile_module] = True
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 314) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 315)             # FIXME:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 316)             # mark-as-user $(jamfile-module) ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 317) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 318)             bjam.call("load", jamfile_module, jamfile_to_load)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 319)             basename = os.path.basename(jamfile_to_load)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 320)                         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 321)         # Now do some checks
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 322)         if self.current_project != saved_project:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 323)             self.manager.errors()(
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 324) """The value of the .current-project variable
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 325) has magically changed after loading a Jamfile.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 326) This means some of the targets might be defined a the wrong project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 327) after loading %s
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 328) expected value %s
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 329) actual value %s""" % (jamfile_module, saved_project, self.current_project))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 330)           
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 331)         if self.global_build_dir:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 332)             id = self.attribute(jamfile_module, "id")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 333)             project_root = self.attribute(jamfile_module, "project-root")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 334)             location = self.attribute(jamfile_module, "location")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 335) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 336)             if location and project_root == dir:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 337)                 # This is Jamroot
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 338)                 if not id:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 339)                     # FIXME: go via errors module, so that contexts are
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 340)                     # shown?
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 341)                     print "warning: the --build-dir option was specified"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 342)                     print "warning: but Jamroot at '%s'" % dir
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 343)                     print "warning: specified no project id"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 344)                     print "warning: the --build-dir option will be ignored"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 345) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 346) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 347)     def load_standalone(self, jamfile_module, file):
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 348)         """Loads 'file' as standalone project that has no location
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 349)         associated with it.  This is mostly useful for user-config.jam,
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 350)         which should be able to define targets, but although it has
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 351)         some location in filesystem, we don't want any build to
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 352)         happen in user's HOME, for example.
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 353) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 354)         The caller is required to never call this method twice on
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 355)         the same file.
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 356)         """
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 357) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 358)         self.initialize(jamfile_module)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 359)         self.used_projects[jamfile_module] = []
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 360)         bjam.call("load", jamfile_module, file)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 361)         self.load_used_projects(jamfile_module)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 362)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 363)     def is_jamroot(self, basename):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 364)         match = [ pat for pat in self.JAMROOT if re.match(pat, basename)]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 365)         if match:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 366)             return 1
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 367)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 368)             return 0
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 369) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 370)     def initialize(self, module_name, location=None, basename=None):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 371)         """Initialize the module for a project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 372)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 373)         module-name is the name of the project module.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 374)         location is the location (directory) of the project to initialize.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 375)                  If not specified, stanalone project will be initialized
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 376)         """
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 377) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 378)         if "--debug-loading" in self.manager.argv():
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 379)             print "Initializing project '%s'" % module_name
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 380) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 381)         # TODO: need to consider if standalone projects can do anything but defining
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 382)         # prebuilt targets. If so, we need to give more sensible "location", so that
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 383)         # source paths are correct.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 384)         if not location:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 385)             location = ""
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 386)         else:
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 387)             location = b2.util.path.relpath(os.getcwd(), location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 388) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 389)         attributes = ProjectAttributes(self.manager, location, module_name)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 390)         self.module2attributes[module_name] = attributes
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 391) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 392)         if location:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 393)             attributes.set("source-location", location, exact=1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 394)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 395)             attributes.set("source-location", "", exact=1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 396) 
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 397)         attributes.set("requirements", property_set.empty(), exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 398)         attributes.set("usage-requirements", property_set.empty(), exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 399)         attributes.set("default-build", [], exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 400)         attributes.set("projects-to-build", [], exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 401)         attributes.set("project-root", None, exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 402)         attributes.set("build-dir", None, exact=True)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 403)         
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 404)         self.project_rules_.init_project(module_name)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 405) 
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 406)         jamroot = False
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 407) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 408)         parent_module = None;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 409)         if module_name == "site-config":
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 410)             # No parent
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 411)             pass
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 412)         elif module_name == "user-config":
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 413)             parent_module = "site-config"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 414)         elif location and not self.is_jamroot(basename):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 415)             # We search for parent/project-root only if jamfile was specified 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 416)             # --- i.e
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 417)             # if the project is not standalone.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 418)             parent_module = self.load_parent(location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 419)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 420)             # It's either jamroot, or standalone project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 421)             # If it's jamroot, inherit from user-config.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 422)             if location:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 423)                 parent_module = "user-config" ;                
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 424)                 jamroot = True ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 425)                 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 426)         if parent_module:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 427)             self.inherit_attributes(module_name, parent_module)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 428)             attributes.set("parent-module", parent_module, exact=1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 429) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 430)         if jamroot:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 431)             attributes.set("project-root", location, exact=1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 432)                                 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 433)         parent = None
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 434)         if parent_module:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 435)             parent = self.target(parent_module)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 436) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 437)         if not self.module2target.has_key(module_name):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 438)             target = b2.build.targets.ProjectTarget(self.manager,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 439)                 module_name, module_name, parent,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 440)                 self.attribute(module_name,"requirements"),
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 441)                 # FIXME: why we need to pass this? It's not
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 442)                 # passed in jam code.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 443)                 self.attribute(module_name, "default-build"))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 444)             self.module2target[module_name] = target
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 445) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 446)         self.current_project = self.target(module_name)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 447) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 448)     def inherit_attributes(self, project_module, parent_module):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 449)         """Make 'project-module' inherit attributes of project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 450)         root and parent module."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 451) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 452)         attributes = self.module2attributes[project_module]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 453)         pattributes = self.module2attributes[parent_module]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 454)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 455)         # Parent module might be locationless user-config.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 456)         # FIXME:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 457)         #if [ modules.binding $(parent-module) ]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 458)         #{        
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 459)         #    $(attributes).set parent : [ path.parent 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 460)         #                                 [ path.make [ modules.binding $(parent-module) ] ] ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 461)         #    }
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 462)         
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 463)         attributes.set("project-root", pattributes.get("project-root"), exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 464)         attributes.set("default-build", pattributes.get("default-build"), exact=True)
+49c03622 (jhunold       2008-07-23 09:57:41 +0000 465)         attributes.set("requirements", pattributes.get("requirements"), exact=True)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 466)         attributes.set("usage-requirements",
+cde6f09a (vladimir_prus 2007-10-19 23:12:33 +0000 467)                        pattributes.get("usage-requirements"), exact=1)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 468) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 469)         parent_build_dir = pattributes.get("build-dir")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 470)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 471)         if parent_build_dir:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 472)         # Have to compute relative path from parent dir to our dir
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 473)         # Convert both paths to absolute, since we cannot
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 474)         # find relative path from ".." to "."
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 475) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 476)              location = attributes.get("location")
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 477)              parent_location = pattributes.get("location")
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 478) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 479)              our_dir = os.path.join(os.getcwd(), location)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 480)              parent_dir = os.path.join(os.getcwd(), parent_location)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 481) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 482)              build_dir = os.path.join(parent_build_dir,
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 483)                                       b2.util.path.relpath(parent_dir,
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 484)                                                                     our_dir))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 485) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 486)     def register_id(self, id, module):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 487)         """Associate the given id with the given project module."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 488)         self.id2module[id] = module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 489) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 490)     def current(self):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 491)         """Returns the project which is currently being loaded."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 492)         return self.current_project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 493) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 494)     def push_current(self, project):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 495)         """Temporary changes the current project to 'project'. Should
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 496)         be followed by 'pop-current'."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 497)         self.saved_current_project.append(self.current_project)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 498)         self.current_project = project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 499) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 500)     def pop_current(self):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 501)         self.current_project = self.saved_current_project[-1]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 502)         del self.saved_current_project[-1]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 503) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 504)     def attributes(self, project):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 505)         """Returns the project-attribute instance for the
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 506)         specified jamfile module."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 507)         return self.module2attributes[project]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 508) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 509)     def attribute(self, project, attribute):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 510)         """Returns the value of the specified attribute in the
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 511)         specified jamfile module."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 512)         return self.module2attributes[project].get(attribute)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 513) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 514)     def target(self, project_module):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 515)         """Returns the project target corresponding to the 'project-module'."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 516)         if not self.module2target[project_module]:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 517)             self.module2target[project_module] = \
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 518)                 ProjectTarget(project_module, project_module,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 519)                               self.attribute(project_module, "requirements"))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 520)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 521)         return self.module2target[project_module]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 522) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 523)     def use(self, id, location):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 524)         # Use/load a project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 525)         saved_project = self.current_project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 526)         project_module = self.load(location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 527)         declared_id = self.attribute(project_module, "id")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 528) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 529)         if not declared_id or declared_id != id:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 530)             # The project at 'location' either have no id or
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 531)             # that id is not equal to the 'id' parameter.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 532)             if self.id2module[id] and self.id2module[id] != project_module:
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 533)                 self.manager.errors()(
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 534) """Attempt to redeclare already existing project id '%s'""" % id)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 535)             self.id2module[id] = project_module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 536) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 537)         self.current_module = saved_project
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 538) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 539)     def add_rule(self, name, callable):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 540)         """Makes rule 'name' available to all subsequently loaded Jamfiles.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 541) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 542)         Calling that rule wil relay to 'callable'."""
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 543)         self.project_rules_.add_rule(name, callable)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 544) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 545)     def project_rules(self):
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 546)         return self.project_rules_
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 547) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 548)     def glob_internal(self, project, wildcards, excludes, rule_name):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 549)         location = project.get("source-location")
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 550) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 551)         result = []
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 552)         callable = b2.util.path.__dict__[rule_name]
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 553)         
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 554)         paths = callable(location, wildcards, excludes)
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 555)         has_dir = 0
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 556)         for w in wildcards:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 557)             if os.path.dirname(w):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 558)                 has_dir = 1
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 559)                 break
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 560) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 561)         if has_dir or rule_name != "glob":
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 562)             # The paths we've found are relative to current directory,
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 563)             # but the names specified in sources list are assumed to
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 564)             # be relative to source directory of the corresponding
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 565)             # prject. So, just make the name absolute.
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 566)             result = [os.path.join(os.getcwd(), p) for p in paths]
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 567)         else:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 568)             # There were not directory in wildcard, so the files are all
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 569)             # in the source directory of the project. Just drop the
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 570)             # directory, instead of making paths absolute.
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 571)             result = [os.path.basename(p) for p in paths]
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 572)             
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 573)         return result
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 574) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 575)     def load_module(self, name, extra_path=None):
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 576)         """Classic Boost.Build 'modules' are in fact global variables.
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 577)         Therefore, try to find an already loaded Python module called 'name' in sys.modules. 
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 578)         If the module ist not loaded, find it Boost.Build search
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 579)         path and load it.  The new module is not entered in sys.modules.
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 580)         The motivation here is to have disjoint namespace of modules
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 581)         loaded via 'import/using' in Jamfile, and ordinary Python
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 582)         modules. We don't want 'using foo' in Jamfile to load ordinary
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 583)         Python module 'foo' which is going to not work. And we
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 584)         also don't want 'import foo' in regular Python module to
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 585)         accidentally grab module named foo that is internal to
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 586)         Boost.Build and intended to provide interface to Jamfiles."""
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 587) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 588)         existing = self.loaded_tool_modules_.get(name)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 589)         if existing:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 590)             return existing
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 591) 
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 592)         modules = sys.modules
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 593)         for class_name in modules:
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 594)             if name in class_name:
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 595)                 module = modules[class_name]
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 596)                 self.loaded_tool_modules_[name] = module
+53b0faa2 (jhunold       2008-08-10 18:25:50 +0000 597)                 return module
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 598)         
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 599)         path = extra_path
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 600)         if not path:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 601)             path = []
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 602)         path.extend(self.manager.b2.path())
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 603)         location = None
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 604)         for p in path:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 605)             l = os.path.join(p, name + ".py")
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 606)             if os.path.exists(l):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 607)                 location = l
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 608)                 break
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 609) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 610)         if not location:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 611)             self.manager.errors()("Cannot find module '%s'" % name)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 612) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 613)         mname = "__build_build_temporary__"
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 614)         file = open(location)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 615)         try:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 616)             # TODO: this means we'll never make use of .pyc module,
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 617)             # which might be a problem, or not.
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 618)             module = imp.load_module(mname, file, os.path.basename(location),
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 619)                                      (".py", "r", imp.PY_SOURCE))
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 620)             del sys.modules[mname]
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 621)             self.loaded_tool_modules_[name] = module
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 622)             return module
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 623)         finally:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 624)             file.close()
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 625)         
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 626) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 627) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 628) # FIXME:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 629) # Defines a Boost.Build extension project. Such extensions usually
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 630) # contain library targets and features that can be used by many people.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 631) # Even though extensions are really projects, they can be initialize as
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 632) # a module would be with the "using" (project.project-rules.using)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 633) # mechanism.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 634) #rule extension ( id : options * : * )
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 635) #{
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 636) #    # The caller is a standalone module for the extension.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 637) #    local mod = [ CALLER_MODULE ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 638) #    
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 639) #    # We need to do the rest within the extension module.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 640) #    module $(mod)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 641) #    {
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 642) #        import path ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 643) #        
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 644) #        # Find the root project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 645) #        local root-project = [ project.current ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 646) #        root-project = [ $(root-project).project-module ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 647) #        while
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 648) #            [ project.attribute $(root-project) parent-module ] &&
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 649) #            [ project.attribute $(root-project) parent-module ] != user-config
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 650) #        {
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 651) #            root-project = [ project.attribute $(root-project) parent-module ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 652) #        }
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 653) #        
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 654) #        # Create the project data, and bring in the project rules
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 655) #        # into the module.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 656) #        project.initialize $(__name__) :
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 657) #            [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 658) #        
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 659) #        # Create the project itself, i.e. the attributes.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 660) #        # All extensions are created in the "/ext" project space.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 661) #        project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 662) #        local attributes = [ project.attributes $(__name__) ] ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 663) #        
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 664) #        # Inherit from the root project of whomever is defining us.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 665) #        project.inherit-attributes $(__name__) : $(root-project) ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 666) #        $(attributes).set parent-module : $(root-project) : exact ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 667) #    }
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 668) #}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 669)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 670) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 671) class ProjectAttributes:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 672)     """Class keeping all the attributes of a project.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 673) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 674)     The standard attributes are 'id', "location", "project-root", "parent"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 675)     "requirements", "default-build", "source-location" and "projects-to-build".
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 676)     """
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 677)         
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 678)     def __init__(self, manager, location, project_module):
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 679)         self.manager = manager
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 680)         self.location = location
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 681)         self.project_module = project_module
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 682)         self.attributes = {}
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 683)         self.usage_requirements = None
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 684)         
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 685)     def set(self, attribute, specification, exact):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 686)         """Set the named attribute from the specification given by the user.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 687)         The value actually set may be different."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 688) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 689)         if exact:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 690)             self.__dict__[attribute] = specification
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 691)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 692)         elif attribute == "requirements":
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 693)             self.requirements = property_set.refine_from_user_input(
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 694)                 self.requirements, specification,
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 695)                 self.project_module, self.location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 696)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 697)         elif attribute == "usage-requirements":
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 698)             unconditional = []
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 699)             for p in specification:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 700)                 split = property.split_conditional(p)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 701)                 if split:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 702)                     unconditional.append(split[1])
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 703)                 else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 704)                     unconditional.append(p)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 705) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 706)             non_free = property.remove("free", unconditional)
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 707)             if non_free:                
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 708)                 pass
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 709)                 # FIXME:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 710)                 #errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ;
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 711) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 712)             t = property.translate_paths(specification, self.location)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 713) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 714)             existing = self.__dict__.get("usage-requirements")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 715)             if existing:
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 716)                 new = property_set.create(existing.raw() +  t)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 717)             else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 718)                 new = property_set.create(t)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 719)             self.__dict__["usage-requirements"] = new
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 720) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 721)                 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 722)         elif attribute == "default-build":
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 723)             self.__dict__["default-build"] = property_set.create(specification)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 724)             
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 725)         elif attribute == "source-location":
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 726)             source_location = []
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 727)             for path in specification:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 728)                 source_location += os.path.join(self.location, path)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 729)             self.__dict__["source-location"] = source_location
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 730)                 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 731)         elif attribute == "build-dir":
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 732)             self.__dict__["build-dir"] = os.path.join(self.location, specification)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 733)                  
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 734)         elif not attribute in ["id", "default-build", "location",
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 735)                                "source-location", "parent",
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 736)                                "projects-to-build", "project-root"]:
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 737)             self.manager.errors()(
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 738) """Invalid project attribute '%s' specified
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 739) for project at '%s'""" % (attribute, self.location))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 740)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 741)             self.__dict__[attribute] = specification
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 742) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 743)     def get(self, attribute):
+cde6f09a (vladimir_prus 2007-10-19 23:12:33 +0000 744)         return self.__dict__[attribute]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 745) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 746)     def dump(self):
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 747)         """Prints the project attributes."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 748)         id = self.get("id")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 749)         if not id:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 750)             id = "(none)"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 751)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 752)             id = id[0]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 753) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 754)         parent = self.get("parent")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 755)         if not parent:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 756)             parent = "(none)"
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 757)         else:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 758)             parent = parent[0]
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 759) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 760)         print "'%s'" % id
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 761)         print "Parent project:%s", parent
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 762)         print "Requirements:%s", self.get("requirements")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 763)         print "Default build:%s", string.join(self.get("debuild-build"))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 764)         print "Source location:%s", string.join(self.get("source-location"))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 765)         print "Projects to build:%s", string.join(self.get("projects-to-build").sort());
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 766) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 767) class ProjectRules:
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 768)     """Class keeping all rules that are made available to Jamfile."""
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 769) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 770)     def __init__(self, registry):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 771)         self.registry = registry
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 772)         self.manager_ = registry.manager
+38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 773)         self.rules = {}
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 774)         self.local_names = [x for x in self.__class__.__dict__
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 775)                             if x not in ["__init__", "init_project", "add_rule",
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 776)                                          "error_reporting_wrapper", "add_rule_for_type"]]
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 777)         self.all_names_ = [x for x in self.local_names]
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 778) 
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 779)     def add_rule_for_type(self, type):
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 780)         rule_name = type.lower();
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 781) 
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 782)         def xpto (name, sources, requirements = [], default_build = None, usage_requirements = []):
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 783)             return self.manager_.targets().create_typed_target(
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 784)                 type, self.registry.current(), name[0], sources,
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 785)                 requirements, default_build, usage_requirements) 
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 786) 
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 787)         self.add_rule(type.lower(), xpto)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 788)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 789)     def add_rule(self, name, callable):
+38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 790)         self.rules[name] = callable
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 791)         self.all_names_.append(name)
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 792) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 793)     def all_names(self):
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 794)         return self.all_names_
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 795) 
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 796)     def call_and_report_errors(self, callable, *args):
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 797)         result = None
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 798)         try:
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 799)             self.manager_.errors().push_jamfile_context()
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 800)             result = callable(*args)
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 801)         except ExceptionWithUserContext, e:
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 802)             e.report()
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 803)         except Exception, e:
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 804)             try:
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 805)                 self.manager_.errors().handle_stray_exception (e)
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 806)             except ExceptionWithUserContext, e:
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 807)                 e.report()
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 808)         finally:                
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 809)             self.manager_.errors().pop_jamfile_context()
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 810)                                         
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 811)         return result
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 812) 
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 813)     def make_wrapper(self, callable):
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 814)         """Given a free-standing function 'callable', return a new
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 815)         callable that will call 'callable' and report all exceptins,
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 816)         using 'call_and_report_errors'."""
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 817)         def wrapper(*args):
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 818)             self.call_and_report_errors(callable, *args)
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 819)         return wrapper
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 820) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 821)     def init_project(self, project_module):
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 822) 
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 823)         for n in self.local_names:            
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 824)             # Using 'getattr' here gives us a bound method,
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 825)             # while using self.__dict__[r] would give unbound one.
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 826)             v = getattr(self, n)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 827)             if callable(v):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 828)                 if n == "import_":
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 829)                     n = "import"
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 830)                 else:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 831)                     n = string.replace(n, "_", "-")
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 832)                     
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 833)                 bjam.import_rule(project_module, n,
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 834)                                  self.make_wrapper(v))
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 835) 
+38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 836)         for n in self.rules:
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 837)             bjam.import_rule(project_module, n,
+0317671e (vladimir_prus 2007-10-28 14:02:06 +0000 838)                              self.make_wrapper(self.rules[n]))
+38d984eb (vladimir_prus 2007-10-13 17:52:25 +0000 839) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 840)     def project(self, *args):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 841) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 842)         jamfile_module = self.registry.current().project_module()
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 843)         attributes = self.registry.attributes(jamfile_module)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 844)         
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 845)         id = None
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 846)         if args and args[0]:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 847)             id = args[0][0]
+092119e3 (vladimir_prus 2007-10-16 05:45:31 +0000 848)             args = args[1:]
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 849) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 850)         if id:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 851)             if id[0] != '/':
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 852)                 id = '/' + id
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 853)             self.registry.register_id (id, jamfile_module)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 854) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 855)         explicit_build_dir = None
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 856)         for a in args:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 857)             if a:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 858)                 attributes.set(a[0], a[1:], exact=0)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 859)                 if a[0] == "build-dir":
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 860)                     explicit_build_dir = a[1]
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 861)         
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 862)         # If '--build-dir' is specified, change the build dir for the project.
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 863)         if self.registry.global_build_dir:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 864) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 865)             location = attributes.get("location")
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 866)             # Project with empty location is 'standalone' project, like
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 867)             # user-config, or qt.  It has no build dir.
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 868)             # If we try to set build dir for user-config, we'll then
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 869)             # try to inherit it, with either weird, or wrong consequences.
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 870)             if location and location == attributes.get("project-root"):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 871)                 # This is Jamroot.
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 872)                 if id:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 873)                     if explicit_build_dir and os.path.isabs(explicit_build_dir):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 874)                         self.register.manager.errors()(
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 875) """Absolute directory specified via 'build-dir' project attribute
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 876) Don't know how to combine that with the --build-dir option.""")
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 877) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 878)                     rid = id
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 879)                     if rid[0] == '/':
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 880)                         rid = rid[1:]
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 881) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 882)                     p = os.path.join(self.registry.global_build_dir,
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 883)                                      rid, explicit_build_dir)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 884)                     attributes.set("build-dir", p, exact=1)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 885)             elif explicit_build_dir:
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 886)                 self.registry.manager.errors()(
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 887) """When --build-dir is specified, the 'build-project'
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 888) attribute is allowed only for top-level 'project' invocations""")
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 889) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 890)     def constant(self, name, value):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 891)         """Declare and set a project global constant.
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 892)         Project global constants are normal variables but should
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 893)         not be changed. They are applied to every child Jamfile."""
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 894)         m = "Jamfile</home/ghost/Work/Boost/boost-svn/tools/build/v2_python/python/tests/bjam/make>"
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 895)         self.registry.current().add_constant(name[0], value)
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 896) 
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 897)     def path_constant(self, name, value):
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 898)         """Declare and set a project global constant, whose value is a path. The
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 899)         path is adjusted to be relative to the invocation directory. The given
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 900)         value path is taken to be either absolute, or relative to this project
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 901)         root."""
+0ed8e16d (vladimir_prus 2007-10-13 21:34:05 +0000 902)         self.registry.current().add_constant(name[0], value, path=1)
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 903) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 904)     def use_project(self, id, where):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 905)         # See comment in 'load' for explanation why we record the
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 906)         # parameters as opposed to loading the project now.
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 907)         m = self.registry.current().project_module();
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 908)         self.registry.used_projects[m].append((id, where))
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 909)         
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 910)     def build_project(self, dir):
+1674e2d9 (jhunold       2008-08-08 19:52:05 +0000 911)         assert(isinstance(dir, list))
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 912)         jamfile_module = self.registry.current().project_module()
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 913)         attributes = self.registry.attributes(jamfile_module)
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 914)         now = attributes.get("projects-to-build")
+1674e2d9 (jhunold       2008-08-08 19:52:05 +0000 915)         attributes.set("projects-to-build", now + dir, exact=True)
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 916) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 917)     def explicit(self, target_names):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 918)         t = self.registry.current()
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 919)         for n in target_names:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 920)             t.mark_target_as_explicit(n)
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 921) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 922)     def glob(self, wildcards, excludes=None):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 923)         return self.registry.glob_internal(self.registry.current(),
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 924)                                            wildcards, excludes, "glob")
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 925) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 926)     def glob_tree(self, wildcards, excludes=None):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 927)         bad = 0
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 928)         for p in wildcards:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 929)             if os.path.dirname(p):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 930)                 bad = 1
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 931) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 932)         if excludes:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 933)             for p in excludes:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 934)                 if os.path.dirname(p):
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 935)                     bad = 1
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 936) 
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 937)         if bad:
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 938)             self.registry.manager().errors()(
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 939) "The patterns to 'glob-tree' may not include directory")
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 940)         return self.registry.glob_internal(self.registry.current(),
+2a36874b (vladimir_prus 2007-10-14 07:20:55 +0000 941)                                            wildcards, excludes, "glob_tree")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 942)     
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 943) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 944)     def using(self, toolset, *args):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 945)         # The module referred by 'using' can be placed in
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 946)         # the same directory as Jamfile, and the user
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 947)         # will expect the module to be found even though
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 948)         # the directory is not in BOOST_BUILD_PATH.
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 949)         # So temporary change the search path.
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 950)         jamfile_module = self.registry.current().project_module()
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 951)         attributes = self.registry.attributes(jamfile_module)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 952)         location = attributes.get("location")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 953) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 954)         m = self.registry.load_module(toolset[0], [location])
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 955)         if not m.__dict__.has_key("init"):
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 956)             self.registry.manager.errors()(
+7da7f9c1 (vladimir_prus 2008-05-18 04:29:53 +0000 957)                 "Tool module '%s' does not define the 'init' method" % toolset[0])
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 958)         m.init(*args)
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 959) 
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 960) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 961)     def import_(self, name, names_to_import=None, local_names=None):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 962) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 963)         name = name[0]
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 964)         jamfile_module = self.registry.current().project_module()
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 965)         attributes = self.registry.attributes(jamfile_module)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 966)         location = attributes.get("location")
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 967) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 968)         m = self.registry.load_module(name, [location])
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 969) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 970)         for f in m.__dict__:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 971)             v = m.__dict__[f]
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 972)             if callable(v):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 973)                 bjam.import_rule(jamfile_module, name + "." + f, v)
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 974) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 975)         if names_to_import:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 976)             if not local_names:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 977)                 local_names = names_to_import
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 978) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 979)             if len(names_to_import) != len(local_names):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 980)                 self.registry.manager.errors()(
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 981) """The number of names to import and local names do not match.""")
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 982) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 983)             for n, l in zip(names_to_import, local_names):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 984)                 bjam.import_rule(jamfile_module, l, m.__dict__[n])
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 985)         
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 986)     def conditional(self, condition, requirements):
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 987)         """Calculates conditional requirements for multiple requirements
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 988)         at once. This is a shorthand to be reduce duplication and to
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 989)         keep an inline declarative syntax. For example:
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 990) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 991)             lib x : x.cpp : [ conditional <toolset>gcc <variant>debug :
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 992)                 <define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ;
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 993)         """
+f049766b (vladimir_prus 2007-10-10 09:31:06 +0000 994) 
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 995)         c = string.join(condition, ",")
+f2aef897 (vladimir_prus 2007-10-14 09:19:52 +0000 996)         return [c + ":" + r for r in requirements]
Added: trunk/tools/build/v2/build/project.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/project.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,996 @@
+# Status: being ported by Vladimir Prus
+# Base revision: 40480
+
+# Copyright 2002, 2003 Dave Abrahams 
+# Copyright 2002, 2005, 2006 Rene Rivera 
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+#  Implements project representation and loading.
+#   Each project is represented by 
+#   - a module where all the Jamfile content live. 
+#   - an instance of 'project-attributes' class.
+#     (given module name, can be obtained by 'attributes' rule)
+#   - an instance of 'project-target' class (from targets.jam)
+#     (given a module name, can be obtained by 'target' rule)
+#
+#  Typically, projects are created as result of loading Jamfile, which is
+#  do by rules 'load' and 'initialize', below. First, module for Jamfile
+#  is loaded and new project-attributes instance is created. Some rules
+#  necessary for project are added to the module (see 'project-rules' module)
+#  at the bottom of this file.
+#  Default project attributes are set (inheriting attributes of parent project, if
+#  it exists). After that, Jamfile is read. It can declare its own attributes,
+#  via 'project' rule, which will be combined with already set attributes.
+#
+#
+#  The 'project' rule can also declare project id, which will be associated with 
+#  the project module.
+#
+#  There can also be 'standalone' projects. They are created by calling 'initialize'
+#  on arbitrary module, and not specifying location. After the call, the module can
+#  call 'project' rule, declare main target and behave as regular projects. However,
+#  since it's not associated with any location, it's better declare only prebuilt 
+#  targets.
+#
+#  The list of all loaded Jamfile is stored in variable .project-locations. It's possible
+#  to obtain module name for a location using 'module-name' rule. The standalone projects
+#  are not recorded, the only way to use them is by project id.
+
+import b2.util.path
+from b2.build import property_set, property
+from b2.build.errors import ExceptionWithUserContext
+import b2.build.targets
+
+import bjam
+
+import re
+import sys
+import os
+import string
+import imp
+import traceback
+
+class ProjectRegistry:
+
+    def __init__(self, manager, global_build_dir):
+        self.manager = manager
+        self.global_build_dir = None
+        self.project_rules_ = ProjectRules(self)
+
+        # The target corresponding to the project being loaded now
+        self.current_project = None
+        
+        # The set of names of loaded project modules
+        self.jamfile_modules = {}
+
+        # Mapping from location to module name
+        self.location2module = {}
+
+        # Mapping from project id to project module
+        self.id2module = {}
+
+        # Map from Jamfile directory to parent Jamfile/Jamroot
+        # location.
+        self.dir2parent_jamfile = {}
+
+        # Map from directory to the name of Jamfile in
+        # that directory (or None).
+        self.dir2jamfile = {}
+
+        # Map from project module to attributes object.
+        self.module2attributes = {}
+
+        # Map from project module to target for the project
+        self.module2target = {}
+
+        # Map from names to Python modules, for modules loaded
+        # via 'using' and 'import' rules in Jamfiles.
+        self.loaded_tool_modules_ = {}
+
+        # Map from project target to the list of
+        # (id,location) pairs corresponding to all 'use-project'
+        # invocations.
+        # TODO: should not have a global map, keep this
+        # in ProjectTarget.
+        self.used_projects = {}
+
+        self.saved_current_project = []
+
+        self.JAMROOT = self.manager.getenv("JAMROOT");
+
+        # Note the use of character groups, as opposed to listing
+        # 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate
+        # matches on windows and would have to eliminate duplicates.
+        if not self.JAMROOT:
+            self.JAMROOT = ["project-root.jam", "[Jj]amroot", "[Jj]amroot.jam"]
+
+        # Default patterns to search for the Jamfiles to use for build
+        # declarations.
+        self.JAMFILE = self.manager.getenv("JAMFILE")
+
+        if not self.JAMFILE:
+            self.JAMFILE = ["[Bb]uild.jam", "[Jj]amfile.v2", "[Jj]amfile",
+                            "[Jj]amfile.jam"]
+
+
+    def load (self, jamfile_location):
+        """Loads jamfile at the given location. After loading, project global
+        file and jamfile needed by the loaded one will be loaded recursively.
+        If the jamfile at that location is loaded already, does nothing.
+        Returns the project module for the Jamfile."""
+
+        absolute = os.path.join(os.getcwd(), jamfile_location)
+        absolute = os.path.normpath(absolute)
+        jamfile_location = b2.util.path.relpath(os.getcwd(), absolute)
+
+        if "--debug-loading" in self.manager.argv():
+            print "Loading Jamfile at '%s'" % jamfile_location
+
+            
+        mname = self.module_name(jamfile_location)
+        # If Jamfile is already loaded, don't try again.
+        if not mname in self.jamfile_modules:
+        
+            self.load_jamfile(jamfile_location)
+                
+            # We want to make sure that child project are loaded only
+            # after parent projects. In particular, because parent projects
+            # define attributes whch are inherited by children, and we don't
+            # want children to be loaded before parents has defined everything.
+            #
+            # While "build-project" and "use-project" can potentially refer
+            # to child projects from parent projects, we don't immediately
+            # load child projects when seing those attributes. Instead,
+            # we record the minimal information that will be used only later.
+            
+            self.load_used_projects(mname)
+             
+        return mname
+
+    def load_used_projects(self, module_name):
+        # local used = [ modules.peek $(module-name) : .used-projects ] ;
+        used = self.used_projects[module_name]
+    
+        location = self.attribute(module_name, "location")
+        for u in used:
+            id = u[0]
+            where = u[1]
+
+            self.use(id, os.path.join(location, where))
+
+    def load_parent(self, location):
+        """Loads parent of Jamfile at 'location'.
+        Issues an error if nothing is found."""
+
+        found = b2.util.path.glob_in_parents(
+            location, self.JAMROOT + self.JAMFILE) 
+
+        if not found:
+            print "error: Could not find parent for project at '%s'" % location
+            print "error: Did not find Jamfile or project-root.jam in any parent directory."
+            sys.exit(1)
+    
+        return self.load(os.path.dirname(found[0]))
+
+    def act_as_jamfile(self, module, location):
+        """Makes the specified 'module' act as if it were a regularly loaded Jamfile 
+        at 'location'. If Jamfile is already located for that location, it's an 
+        error."""
+
+        if self.module_name(location) in self.jamfile_modules:
+            self.manager.errors()(
+                "Jamfile was already loaded for '%s'" % location)
+    
+        # Set up non-default mapping from location to module.
+        self.location2module[location] = module
+    
+        # Add the location to the list of project locations
+        # so that we don't try to load Jamfile in future
+        self.jamfile_modules.append(location)
+    
+        self.initialize(module, location)
+
+    def find(self, name, current_location):
+        """Given 'name' which can be project-id or plain directory name,
+        return project module corresponding to that id or directory.
+        Returns nothing of project is not found."""
+
+        project_module = None
+
+        # Try interpreting name as project id.
+        if name[0] == '/':
+            project_module = self.id2module.get(name)
+
+        if not project_module:
+            location = os.path.join(current_location, name)
+            # If no project is registered for the given location, try to
+            # load it. First see if we have Jamfile. If not we might have project
+            # root, willing to act as Jamfile. In that case, project-root
+            # must be placed in the directory referred by id.
+        
+            project_module = self.module_name(location)
+            if not project_module in self.jamfile_modules and \
+               b2.util.path.glob([location], self.JAMROOT + self.JAMFILE):
+                project_module = self.load(location)
+
+        return project_module
+
+    def module_name(self, jamfile_location):
+        """Returns the name of module corresponding to 'jamfile-location'.
+        If no module corresponds to location yet, associates default
+        module name with that location."""
+        module = self.location2module.get(jamfile_location)
+        if not module:
+            # Root the path, so that locations are always umbiguious.
+            # Without this, we can't decide if '../../exe/program1' and '.'
+            # are the same paths, or not.
+            jamfile_location = os.path.realpath(
+                os.path.join(os.getcwd(), jamfile_location))
+            module = "Jamfile<%s>" % jamfile_location
+            self.location2module[jamfile_location] = module
+        return module
+
+    def find_jamfile (self, dir, parent_root=0, no_errors=0):
+        """Find the Jamfile at the given location. This returns the
+        exact names of all the Jamfiles in the given directory. The optional
+        parent-root argument causes this to search not the given directory
+        but the ones above it up to the directory given in it."""
+        
+        # Glob for all the possible Jamfiles according to the match pattern.
+        #
+        jamfile_glob = None
+        if parent_root:
+            parent = self.dir2parent_jamfile.get(dir)
+            if not parent:
+                parent = b2.util.path.glob_in_parents(dir,
+                                                               self.JAMFILE)
+                self.dir2parent_jamfile[dir] = parent
+            jamfile_glob = parent
+        else:
+            jamfile = self.dir2jamfile.get(dir)
+            if not jamfile:
+                jamfile = b2.util.path.glob([dir], self.JAMFILE)
+                self.dir2jamfile[dir] = jamfile
+            jamfile_glob = jamfile
+
+        if len(jamfile_glob):
+            # Multiple Jamfiles found in the same place. Warn about this.
+            # And ensure we use only one of them.
+            # As a temporary convenience measure, if there's Jamfile.v2 amount
+            # found files, suppress the warning and use it.
+            #
+            pattern = "(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)"
+            v2_jamfiles = [x for x in jamfile_glob if re.match(pattern, x)]
+            if len(v2_jamfiles) == 1:
+                jamfile_glob = v2_jamfiles
+            else:
+                print """warning: Found multiple Jamfiles at '%s'!
+Loading the first one: '%s'.""" % (dir, jamfile_glob[0])
+    
+        # Could not find it, error.
+        if not no_errors and not jamfile_glob:
+            self.manager.errors()(
+                """Unable to load Jamfile.
+Could not find a Jamfile in directory '%s'
+Attempted to find it with pattern '%s'.
+Please consult the documentation at 'http://boost.org/boost-build2'."""
+                % (dir, string.join(self.JAMFILE)))
+
+        return jamfile_glob[0]
+    
+    def load_jamfile(self, dir):
+        """Load a Jamfile at the given directory. Returns nothing.
+        Will attempt to load the file as indicated by the JAMFILE patterns.
+        Effect of calling this rule twice with the same 'dir' is underfined."""
+      
+        # See if the Jamfile is where it should be.
+        jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT)
+        if not jamfile_to_load:
+            jamfile_to_load = self.find_jamfile(dir)
+        else:
+            jamfile_to_load = jamfile_to_load[0]
+            
+        # The module of the jamfile.
+        dir = os.path.realpath(os.path.dirname(jamfile_to_load))
+        
+        jamfile_module = self.module_name (dir)
+
+        # Initialize the jamfile module before loading.
+        #    
+        self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load))
+
+        saved_project = self.current_project
+
+        self.used_projects[jamfile_module] = []
+        
+        # Now load the Jamfile in it's own context.
+        # Initialization might have load parent Jamfiles, which might have
+        # loaded the current Jamfile with use-project. Do a final check to make
+        # sure it's not loaded already.
+        if not jamfile_module in self.jamfile_modules:
+            self.jamfile_modules[jamfile_module] = True
+
+            # FIXME:
+            # mark-as-user $(jamfile-module) ;
+
+            bjam.call("load", jamfile_module, jamfile_to_load)
+            basename = os.path.basename(jamfile_to_load)
+                        
+        # Now do some checks
+        if self.current_project != saved_project:
+            self.manager.errors()(
+"""The value of the .current-project variable
+has magically changed after loading a Jamfile.
+This means some of the targets might be defined a the wrong project.
+after loading %s
+expected value %s
+actual value %s""" % (jamfile_module, saved_project, self.current_project))
+          
+        if self.global_build_dir:
+            id = self.attribute(jamfile_module, "id")
+            project_root = self.attribute(jamfile_module, "project-root")
+            location = self.attribute(jamfile_module, "location")
+
+            if location and project_root == dir:
+                # This is Jamroot
+                if not id:
+                    # FIXME: go via errors module, so that contexts are
+                    # shown?
+                    print "warning: the --build-dir option was specified"
+                    print "warning: but Jamroot at '%s'" % dir
+                    print "warning: specified no project id"
+                    print "warning: the --build-dir option will be ignored"
+
+
+    def load_standalone(self, jamfile_module, file):
+        """Loads 'file' as standalone project that has no location
+        associated with it.  This is mostly useful for user-config.jam,
+        which should be able to define targets, but although it has
+        some location in filesystem, we don't want any build to
+        happen in user's HOME, for example.
+
+        The caller is required to never call this method twice on
+        the same file.
+        """
+
+        self.initialize(jamfile_module)
+        self.used_projects[jamfile_module] = []
+        bjam.call("load", jamfile_module, file)
+        self.load_used_projects(jamfile_module)
+        
+    def is_jamroot(self, basename):
+        match = [ pat for pat in self.JAMROOT if re.match(pat, basename)]
+        if match:
+            return 1
+        else:
+            return 0
+
+    def initialize(self, module_name, location=None, basename=None):
+        """Initialize the module for a project.
+        
+        module-name is the name of the project module.
+        location is the location (directory) of the project to initialize.
+                 If not specified, stanalone project will be initialized
+        """
+
+        if "--debug-loading" in self.manager.argv():
+            print "Initializing project '%s'" % module_name
+
+        # TODO: need to consider if standalone projects can do anything but defining
+        # prebuilt targets. If so, we need to give more sensible "location", so that
+        # source paths are correct.
+        if not location:
+            location = ""
+        else:
+            location = b2.util.path.relpath(os.getcwd(), location)
+
+        attributes = ProjectAttributes(self.manager, location, module_name)
+        self.module2attributes[module_name] = attributes
+
+        if location:
+            attributes.set("source-location", location, exact=1)
+        else:
+            attributes.set("source-location", "", exact=1)
+
+        attributes.set("requirements", property_set.empty(), exact=True)
+        attributes.set("usage-requirements", property_set.empty(), exact=True)
+        attributes.set("default-build", [], exact=True)
+        attributes.set("projects-to-build", [], exact=True)
+        attributes.set("project-root", None, exact=True)
+        attributes.set("build-dir", None, exact=True)
+        
+        self.project_rules_.init_project(module_name)
+
+        jamroot = False
+
+        parent_module = None;
+        if module_name == "site-config":
+            # No parent
+            pass
+        elif module_name == "user-config":
+            parent_module = "site-config"
+        elif location and not self.is_jamroot(basename):
+            # We search for parent/project-root only if jamfile was specified 
+            # --- i.e
+            # if the project is not standalone.
+            parent_module = self.load_parent(location)
+        else:
+            # It's either jamroot, or standalone project.
+            # If it's jamroot, inherit from user-config.
+            if location:
+                parent_module = "user-config" ;                
+                jamroot = True ;
+                
+        if parent_module:
+            self.inherit_attributes(module_name, parent_module)
+            attributes.set("parent-module", parent_module, exact=1)
+
+        if jamroot:
+            attributes.set("project-root", location, exact=1)
+                                
+        parent = None
+        if parent_module:
+            parent = self.target(parent_module)
+
+        if not self.module2target.has_key(module_name):
+            target = b2.build.targets.ProjectTarget(self.manager,
+                module_name, module_name, parent,
+                self.attribute(module_name,"requirements"),
+                # FIXME: why we need to pass this? It's not
+                # passed in jam code.
+                self.attribute(module_name, "default-build"))
+            self.module2target[module_name] = target
+
+        self.current_project = self.target(module_name)
+
+    def inherit_attributes(self, project_module, parent_module):
+        """Make 'project-module' inherit attributes of project
+        root and parent module."""
+
+        attributes = self.module2attributes[project_module]
+        pattributes = self.module2attributes[parent_module]
+        
+        # Parent module might be locationless user-config.
+        # FIXME:
+        #if [ modules.binding $(parent-module) ]
+        #{        
+        #    $(attributes).set parent : [ path.parent 
+        #                                 [ path.make [ modules.binding $(parent-module) ] ] ] ;
+        #    }
+        
+        attributes.set("project-root", pattributes.get("project-root"), exact=True)
+        attributes.set("default-build", pattributes.get("default-build"), exact=True)
+        attributes.set("requirements", pattributes.get("requirements"), exact=True)
+        attributes.set("usage-requirements",
+                       pattributes.get("usage-requirements"), exact=1)
+
+        parent_build_dir = pattributes.get("build-dir")
+        
+        if parent_build_dir:
+        # Have to compute relative path from parent dir to our dir
+        # Convert both paths to absolute, since we cannot
+        # find relative path from ".." to "."
+
+             location = attributes.get("location")
+             parent_location = pattributes.get("location")
+
+             our_dir = os.path.join(os.getcwd(), location)
+             parent_dir = os.path.join(os.getcwd(), parent_location)
+
+             build_dir = os.path.join(parent_build_dir,
+                                      b2.util.path.relpath(parent_dir,
+                                                                    our_dir))
+
+    def register_id(self, id, module):
+        """Associate the given id with the given project module."""
+        self.id2module[id] = module
+
+    def current(self):
+        """Returns the project which is currently being loaded."""
+        return self.current_project
+
+    def push_current(self, project):
+        """Temporary changes the current project to 'project'. Should
+        be followed by 'pop-current'."""
+        self.saved_current_project.append(self.current_project)
+        self.current_project = project
+
+    def pop_current(self):
+        self.current_project = self.saved_current_project[-1]
+        del self.saved_current_project[-1]
+
+    def attributes(self, project):
+        """Returns the project-attribute instance for the
+        specified jamfile module."""
+        return self.module2attributes[project]
+
+    def attribute(self, project, attribute):
+        """Returns the value of the specified attribute in the
+        specified jamfile module."""
+        return self.module2attributes[project].get(attribute)
+
+    def target(self, project_module):
+        """Returns the project target corresponding to the 'project-module'."""
+        if not self.module2target[project_module]:
+            self.module2target[project_module] = \
+                ProjectTarget(project_module, project_module,
+                              self.attribute(project_module, "requirements"))
+        
+        return self.module2target[project_module]
+
+    def use(self, id, location):
+        # Use/load a project.
+        saved_project = self.current_project
+        project_module = self.load(location)
+        declared_id = self.attribute(project_module, "id")
+
+        if not declared_id or declared_id != id:
+            # The project at 'location' either have no id or
+            # that id is not equal to the 'id' parameter.
+            if self.id2module[id] and self.id2module[id] != project_module:
+                self.manager.errors()(
+"""Attempt to redeclare already existing project id '%s'""" % id)
+            self.id2module[id] = project_module
+
+        self.current_module = saved_project
+
+    def add_rule(self, name, callable):
+        """Makes rule 'name' available to all subsequently loaded Jamfiles.
+
+        Calling that rule wil relay to 'callable'."""
+        self.project_rules_.add_rule(name, callable)
+
+    def project_rules(self):
+        return self.project_rules_
+
+    def glob_internal(self, project, wildcards, excludes, rule_name):
+        location = project.get("source-location")
+
+        result = []
+        callable = b2.util.path.__dict__[rule_name]
+        
+        paths = callable(location, wildcards, excludes)
+        has_dir = 0
+        for w in wildcards:
+            if os.path.dirname(w):
+                has_dir = 1
+                break
+
+        if has_dir or rule_name != "glob":
+            # The paths we've found are relative to current directory,
+            # but the names specified in sources list are assumed to
+            # be relative to source directory of the corresponding
+            # prject. So, just make the name absolute.
+            result = [os.path.join(os.getcwd(), p) for p in paths]
+        else:
+            # There were not directory in wildcard, so the files are all
+            # in the source directory of the project. Just drop the
+            # directory, instead of making paths absolute.
+            result = [os.path.basename(p) for p in paths]
+            
+        return result
+
+    def load_module(self, name, extra_path=None):
+        """Classic Boost.Build 'modules' are in fact global variables.
+        Therefore, try to find an already loaded Python module called 'name' in sys.modules. 
+        If the module ist not loaded, find it Boost.Build search
+        path and load it.  The new module is not entered in sys.modules.
+        The motivation here is to have disjoint namespace of modules
+        loaded via 'import/using' in Jamfile, and ordinary Python
+        modules. We don't want 'using foo' in Jamfile to load ordinary
+        Python module 'foo' which is going to not work. And we
+        also don't want 'import foo' in regular Python module to
+        accidentally grab module named foo that is internal to
+        Boost.Build and intended to provide interface to Jamfiles."""
+
+        existing = self.loaded_tool_modules_.get(name)
+        if existing:
+            return existing
+
+        modules = sys.modules
+        for class_name in modules:
+            if name is class_name:
+                module = modules[class_name]
+                self.loaded_tool_modules_[name] = module
+                return module
+        
+        path = extra_path
+        if not path:
+            path = []
+        path.extend(self.manager.boost_build_path())
+        location = None
+        for p in path:
+            l = os.path.join(p, name + ".py")
+            if os.path.exists(l):
+                location = l
+                break
+
+        if not location:
+            self.manager.errors()("Cannot find module '%s'" % name)
+
+        mname = "__build_build_temporary__"
+        file = open(location)
+        try:
+            # TODO: this means we'll never make use of .pyc module,
+            # which might be a problem, or not.
+            module = imp.load_module(mname, file, os.path.basename(location),
+                                     (".py", "r", imp.PY_SOURCE))
+            del sys.modules[mname]
+            self.loaded_tool_modules_[name] = module
+            return module
+        finally:
+            file.close()
+        
+
+
+# FIXME:
+# Defines a Boost.Build extension project. Such extensions usually
+# contain library targets and features that can be used by many people.
+# Even though extensions are really projects, they can be initialize as
+# a module would be with the "using" (project.project-rules.using)
+# mechanism.
+#rule extension ( id : options * : * )
+#{
+#    # The caller is a standalone module for the extension.
+#    local mod = [ CALLER_MODULE ] ;
+#    
+#    # We need to do the rest within the extension module.
+#    module $(mod)
+#    {
+#        import path ;
+#        
+#        # Find the root project.
+#        local root-project = [ project.current ] ;
+#        root-project = [ $(root-project).project-module ] ;
+#        while
+#            [ project.attribute $(root-project) parent-module ] &&
+#            [ project.attribute $(root-project) parent-module ] != user-config
+#        {
+#            root-project = [ project.attribute $(root-project) parent-module ] ;
+#        }
+#        
+#        # Create the project data, and bring in the project rules
+#        # into the module.
+#        project.initialize $(__name__) :
+#            [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ;
+#        
+#        # Create the project itself, i.e. the attributes.
+#        # All extensions are created in the "/ext" project space.
+#        project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+#        local attributes = [ project.attributes $(__name__) ] ;
+#        
+#        # Inherit from the root project of whomever is defining us.
+#        project.inherit-attributes $(__name__) : $(root-project) ;
+#        $(attributes).set parent-module : $(root-project) : exact ;
+#    }
+#}
+        
+
+class ProjectAttributes:
+    """Class keeping all the attributes of a project.
+
+    The standard attributes are 'id', "location", "project-root", "parent"
+    "requirements", "default-build", "source-location" and "projects-to-build".
+    """
+        
+    def __init__(self, manager, location, project_module):
+        self.manager = manager
+        self.location = location
+        self.project_module = project_module
+        self.attributes = {}
+        self.usage_requirements = None
+        
+    def set(self, attribute, specification, exact):
+        """Set the named attribute from the specification given by the user.
+        The value actually set may be different."""
+
+        if exact:
+            self.__dict__[attribute] = specification
+            
+        elif attribute == "requirements":
+            self.requirements = property_set.refine_from_user_input(
+                self.requirements, specification,
+                self.project_module, self.location)
+            
+        elif attribute == "usage-requirements":
+            unconditional = []
+            for p in specification:
+                split = property.split_conditional(p)
+                if split:
+                    unconditional.append(split[1])
+                else:
+                    unconditional.append(p)
+
+            non_free = property.remove("free", unconditional)
+            if non_free:                
+                pass
+                # FIXME:
+                #errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ;
+
+            t = property.translate_paths(specification, self.location)
+
+            existing = self.__dict__.get("usage-requirements")
+            if existing:
+                new = property_set.create(existing.raw() +  t)
+            else:
+                new = property_set.create(t)
+            self.__dict__["usage-requirements"] = new
+
+                
+        elif attribute == "default-build":
+            self.__dict__["default-build"] = property_set.create(specification)
+            
+        elif attribute == "source-location":
+            source_location = []
+            for path in specification:
+                source_location += os.path.join(self.location, path)
+            self.__dict__["source-location"] = source_location
+                
+        elif attribute == "build-dir":
+            self.__dict__["build-dir"] = os.path.join(self.location, specification)
+                 
+        elif not attribute in ["id", "default-build", "location",
+                               "source-location", "parent",
+                               "projects-to-build", "project-root"]:
+            self.manager.errors()(
+"""Invalid project attribute '%s' specified
+for project at '%s'""" % (attribute, self.location))
+        else:
+            self.__dict__[attribute] = specification
+
+    def get(self, attribute):
+        return self.__dict__[attribute]
+
+    def dump(self):
+        """Prints the project attributes."""
+        id = self.get("id")
+        if not id:
+            id = "(none)"
+        else:
+            id = id[0]
+
+        parent = self.get("parent")
+        if not parent:
+            parent = "(none)"
+        else:
+            parent = parent[0]
+
+        print "'%s'" % id
+        print "Parent project:%s", parent
+        print "Requirements:%s", self.get("requirements")
+        print "Default build:%s", string.join(self.get("debuild-build"))
+        print "Source location:%s", string.join(self.get("source-location"))
+        print "Projects to build:%s", string.join(self.get("projects-to-build").sort());
+
+class ProjectRules:
+    """Class keeping all rules that are made available to Jamfile."""
+
+    def __init__(self, registry):
+        self.registry = registry
+        self.manager_ = registry.manager
+        self.rules = {}
+        self.local_names = [x for x in self.__class__.__dict__
+                            if x not in ["__init__", "init_project", "add_rule",
+                                         "error_reporting_wrapper", "add_rule_for_type"]]
+        self.all_names_ = [x for x in self.local_names]
+
+    def add_rule_for_type(self, type):
+        rule_name = type.lower();
+
+        def xpto (name, sources, requirements = [], default_build = None, usage_requirements = []):
+            return self.manager_.targets().create_typed_target(
+                type, self.registry.current(), name[0], sources,
+                requirements, default_build, usage_requirements) 
+
+        self.add_rule(type.lower(), xpto)
+    
+    def add_rule(self, name, callable):
+        self.rules[name] = callable
+        self.all_names_.append(name)
+
+    def all_names(self):
+        return self.all_names_
+
+    def call_and_report_errors(self, callable, *args):
+        result = None
+        try:
+            self.manager_.errors().push_jamfile_context()
+            result = callable(*args)
+        except ExceptionWithUserContext, e:
+            e.report()
+        except Exception, e:
+            try:
+                self.manager_.errors().handle_stray_exception (e)
+            except ExceptionWithUserContext, e:
+                e.report()
+        finally:                
+            self.manager_.errors().pop_jamfile_context()
+                                        
+        return result
+
+    def make_wrapper(self, callable):
+        """Given a free-standing function 'callable', return a new
+        callable that will call 'callable' and report all exceptins,
+        using 'call_and_report_errors'."""
+        def wrapper(*args):
+            self.call_and_report_errors(callable, *args)
+        return wrapper
+
+    def init_project(self, project_module):
+
+        for n in self.local_names:            
+            # Using 'getattr' here gives us a bound method,
+            # while using self.__dict__[r] would give unbound one.
+            v = getattr(self, n)
+            if callable(v):
+                if n == "import_":
+                    n = "import"
+                else:
+                    n = string.replace(n, "_", "-")
+                    
+                bjam.import_rule(project_module, n,
+                                 self.make_wrapper(v))
+
+        for n in self.rules:
+            bjam.import_rule(project_module, n,
+                             self.make_wrapper(self.rules[n]))
+
+    def project(self, *args):
+
+        jamfile_module = self.registry.current().project_module()
+        attributes = self.registry.attributes(jamfile_module)
+        
+        id = None
+        if args and args[0]:
+            id = args[0][0]
+            args = args[1:]
+
+        if id:
+            if id[0] != '/':
+                id = '/' + id
+            self.registry.register_id (id, jamfile_module)
+
+        explicit_build_dir = None
+        for a in args:
+            if a:
+                attributes.set(a[0], a[1:], exact=0)
+                if a[0] == "build-dir":
+                    explicit_build_dir = a[1]
+        
+        # If '--build-dir' is specified, change the build dir for the project.
+        if self.registry.global_build_dir:
+
+            location = attributes.get("location")
+            # Project with empty location is 'standalone' project, like
+            # user-config, or qt.  It has no build dir.
+            # If we try to set build dir for user-config, we'll then
+            # try to inherit it, with either weird, or wrong consequences.
+            if location and location == attributes.get("project-root"):
+                # This is Jamroot.
+                if id:
+                    if explicit_build_dir and os.path.isabs(explicit_build_dir):
+                        self.register.manager.errors()(
+"""Absolute directory specified via 'build-dir' project attribute
+Don't know how to combine that with the --build-dir option.""")
+
+                    rid = id
+                    if rid[0] == '/':
+                        rid = rid[1:]
+
+                    p = os.path.join(self.registry.global_build_dir,
+                                     rid, explicit_build_dir)
+                    attributes.set("build-dir", p, exact=1)
+            elif explicit_build_dir:
+                self.registry.manager.errors()(
+"""When --build-dir is specified, the 'build-project'
+attribute is allowed only for top-level 'project' invocations""")
+
+    def constant(self, name, value):
+        """Declare and set a project global constant.
+        Project global constants are normal variables but should
+        not be changed. They are applied to every child Jamfile."""
+        m = "Jamfile</home/ghost/Work/Boost/boost-svn/tools/build/v2_python/python/tests/bjam/make>"
+        self.registry.current().add_constant(name[0], value)
+
+    def path_constant(self, name, value):
+        """Declare and set a project global constant, whose value is a path. The
+        path is adjusted to be relative to the invocation directory. The given
+        value path is taken to be either absolute, or relative to this project
+        root."""
+        self.registry.current().add_constant(name[0], value, path=1)
+
+    def use_project(self, id, where):
+        # See comment in 'load' for explanation why we record the
+        # parameters as opposed to loading the project now.
+        m = self.registry.current().project_module();
+        self.registry.used_projects[m].append((id, where))
+        
+    def build_project(self, dir):
+        assert(isinstance(dir, list))
+        jamfile_module = self.registry.current().project_module()
+        attributes = self.registry.attributes(jamfile_module)
+        now = attributes.get("projects-to-build")
+        attributes.set("projects-to-build", now + dir, exact=True)
+
+    def explicit(self, target_names):
+        t = self.registry.current()
+        for n in target_names:
+            t.mark_target_as_explicit(n)
+
+    def glob(self, wildcards, excludes=None):
+        return self.registry.glob_internal(self.registry.current(),
+                                           wildcards, excludes, "glob")
+
+    def glob_tree(self, wildcards, excludes=None):
+        bad = 0
+        for p in wildcards:
+            if os.path.dirname(p):
+                bad = 1
+
+        if excludes:
+            for p in excludes:
+                if os.path.dirname(p):
+                    bad = 1
+
+        if bad:
+            self.registry.manager().errors()(
+"The patterns to 'glob-tree' may not include directory")
+        return self.registry.glob_internal(self.registry.current(),
+                                           wildcards, excludes, "glob_tree")
+    
+
+    def using(self, toolset, *args):
+        # The module referred by 'using' can be placed in
+        # the same directory as Jamfile, and the user
+        # will expect the module to be found even though
+        # the directory is not in BOOST_BUILD_PATH.
+        # So temporary change the search path.
+        jamfile_module = self.registry.current().project_module()
+        attributes = self.registry.attributes(jamfile_module)
+        location = attributes.get("location")
+
+        m = self.registry.load_module(toolset[0], [location])
+        if not m.__dict__.has_key("init"):
+            self.registry.manager.errors()(
+                "Tool module '%s' does not define the 'init' method" % toolset[0])
+        m.init(*args)
+
+
+    def import_(self, name, names_to_import=None, local_names=None):
+
+        name = name[0]
+        jamfile_module = self.registry.current().project_module()
+        attributes = self.registry.attributes(jamfile_module)
+        location = attributes.get("location")
+
+        m = self.registry.load_module(name, [location])
+
+        for f in m.__dict__:
+            v = m.__dict__[f]
+            if callable(v):
+                bjam.import_rule(jamfile_module, name + "." + f, v)
+
+        if names_to_import:
+            if not local_names:
+                local_names = names_to_import
+
+            if len(names_to_import) != len(local_names):
+                self.registry.manager.errors()(
+"""The number of names to import and local names do not match.""")
+
+            for n, l in zip(names_to_import, local_names):
+                bjam.import_rule(jamfile_module, l, m.__dict__[n])
+        
+    def conditional(self, condition, requirements):
+        """Calculates conditional requirements for multiple requirements
+        at once. This is a shorthand to be reduce duplication and to
+        keep an inline declarative syntax. For example:
+
+            lib x : x.cpp : [ conditional <toolset>gcc <variant>debug :
+                <define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ;
+        """
+
+        c = string.join(condition, ",")
+        return [c + ":" + r for r in requirements]
Added: trunk/tools/build/v2/build/property.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/property.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,636 @@
+# Status: ported, except for tests and --abbreviate-paths.
+# Base revision: 40480
+#
+# Copyright 2001, 2002, 2003 Dave Abrahams 
+# Copyright 2006 Rene Rivera 
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+import re
+from b2.util.utility import *
+from b2.build import feature
+from b2.util import sequence, set
+
+__re_two_ampersands = re.compile ('&&')
+__re_comma = re.compile (',')
+__re_split_condition = re.compile ('(.*):(<.*)')
+__re_toolset_feature = re.compile ('^(<toolset>|<toolset->)')
+__re_os_feature = re.compile ('^(<os>)')
+__re_split_conditional = re.compile (r'(.+):<(.+)')
+__re_colon = re.compile (':')
+__re_has_condition = re.compile (r':<')
+__re_separate_condition_and_property = re.compile (r'(.*):(<.*)')
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __results
+
+    # A cache of results from as_path
+    __results = {}
+    
+reset ()
+
+        
+def path_order (x, y):
+    """ Helper for as_path, below. Orders properties with the implicit ones
+        first, and within the two sections in alphabetical order of feature
+        name.
+    """
+    if x == y:
+        return 0
+        
+    xg = get_grist (x)
+    yg = get_grist (y)
+
+    if yg and not xg:
+        return -1
+
+    elif xg and not yg:
+        return 1
+
+    else:
+        if not xg:            
+            x = feature.expand_subfeatures([x])
+            y = feature.expand_subfeatures([y])
+        
+        if x < y:
+            return -1
+        elif x > y:
+            return 1
+        else:
+            return 0
+
+def abbreviate_dashed(string):
+    # FIXME: string.abbreviate?
+    return [string.abbreviate(part) for part in string.split('-')].join('-')
+
+def identify(string):
+    return string 
+
+# FIXME: --abbreviate-paths        
+    
+def as_path (properties):
+    """ Returns a path which represents the given expanded property set.
+    """
+    key = '-'.join (properties)
+    
+    if not __results.has_key (key):
+        # trim redundancy
+        properties = feature.minimize (properties)
+    
+        # sort according to path_order
+        properties.sort (path_order)
+    
+        components = []
+        for p in properties:
+            pg = get_grist (p)
+            # FIXME: abbrev?
+            if pg:
+                f = ungrist (pg)
+                components.append (f + '-' + replace_grist (p, ''))
+
+            else:
+                components.append (p)
+        
+        __results [key] = '/'.join (components)
+    
+    return __results [key]
+    
+def refine (properties, requirements):
+    """ Refines 'properties' by overriding any non-free properties 
+        for which a different value is specified in 'requirements'. 
+        Conditional requirements are just added without modification.
+        Returns the resulting list of properties.
+    """
+    # The result has no duplicates, so we store it in a map
+    # TODO: use a set from Python 2.4?
+    result = {}
+    
+    # Records all requirements.
+    required = {}
+    
+    # All the elements of requirements should be present in the result
+    # Record them so that we can handle 'properties'.
+    for r in requirements:
+        # Don't consider conditional requirements.
+        if not is_conditional (r):
+            # Note: cannot use local here, so take an ugly name
+            required [get_grist (r)] = replace_grist (r, '')
+
+    for p in properties:
+        # Skip conditional properties
+        if is_conditional (p):
+            result [p] = None
+        # No processing for free properties
+        elif 'free' in feature.attributes (get_grist (p)):
+            result [p] = None
+        else:
+            if required.has_key (get_grist (p)):
+                required_value = required [get_grist (p)]
+                
+                value = replace_grist (p, '')
+
+                if value != required_value:
+                    result [replace_grist (required_value, get_grist (p))] = None
+                else:
+                    result [p] = None
+            else:
+                result [p] = None
+
+    return result.keys () + requirements
+
+def translate_paths (properties, path):
+    """ Interpret all path properties in 'properties' as relative to 'path'
+        The property values are assumed to be in system-specific form, and
+        will be translated into normalized form.
+        """
+    result = []
+
+    for p in properties:
+        split = split_conditional (p)
+
+        condition = ''
+
+        if split:
+            condition = split [0]
+            p = split [1]
+        
+        if get_grist (p) and 'path' in feature.attributes (get_grist (p)):
+            values = __re_two_ampersands.split (forward_slashes (get_grist (p)))
+
+            t = [os.path.join(path, v) for v in values]
+            t = '&&'.join (t)
+            tp = backslashes_to_slashes (replace_grist (t, get_grist (p)))
+            result.append (condition + tp)
+            
+        else:
+            result.append (condition + p)
+
+    return result
+
+def translate_indirect(specification, context_module):
+    """Assumes that all feature values that start with '@' are
+    names of rules, used in 'context-module'. Such rules can be
+    either local to the module or global. Qualified local rules
+    with the name of the module."""
+    result = []
+    for p in specification:
+        if p[0] == '@':
+            m = p[1:]
+            if not '.' in p:
+                # This is unqualified rule name. The user might want
+                # to set flags on this rule name, and toolset.flag
+                # auto-qualifies the rule name. Need to do the same
+                # here so set flag setting work.
+                # We can arrange for toolset.flag to *not* auto-qualify
+                # the argument, but then two rules defined in two Jamfiles
+                # will conflict.
+                m = context_module + "." + m
+
+            result.append(get_grist(p) + "@" + m)
+        else:
+            result.append(p)
+
+    return result
+
+def validate (properties):
+    """ Exit with error if any of the properties is not valid.
+        properties may be a single property or a sequence of properties.
+    """
+    
+    if isinstance (properties, str):
+        __validate1 (properties)
+    else:
+        for p in properties:
+            __validate1 (p)
+
+def expand_subfeatures_in_conditions (properties):
+
+    result = []
+    for p in properties:
+        s = __re_split_condition.match (p)
+
+        if not s:
+            result.append (p)
+
+        else:
+            condition = s.group (1)
+
+            # Condition might include several elements
+            condition = __re_comma.split (condition)
+
+            value = s.group (2)
+
+            e = []
+            for c in condition:
+
+                cg = get_grist (c)
+                if __re_toolset_feature.match (cg) or __re_os_feature.match (cg):
+                    # It common that condition includes a toolset which
+                    # was never defined, or mentiones subfeatures which
+                    # were never defined. In that case, validation will
+                    # only produce an spirious error, so don't validate.
+                    e.append (feature.expand_subfeatures (c, True))
+
+                else:
+                    e.append (feature.expand_subfeatures (c))
+            
+            if e == condition:
+                result.append (p)
+
+            else:
+                individual_subfeatures = Set.difference (e, condition)
+                result.append (','.join (individual_subfeatures) + ':' + value)
+
+    return result
+
+def make (specification):
+    """ Converts implicit values into full properties.
+    """
+    result = []
+    for e in specification:
+        if get_grist (e):
+            result.append (e)
+
+        elif feature.is_implicit_value (e):
+            f = feature.implied_feature (e)
+            result.append (f + e)
+
+        else:
+            raise InvalidProperty ("'%s' is not a valid for property specification" % e)
+
+    return result
+
+
+def split_conditional (property):
+    """ If 'property' is conditional property, returns
+        condition and the property, e.g
+        <variant>debug,<toolset>gcc:<inlining>full will become
+        <variant>debug,<toolset>gcc <inlining>full.
+        Otherwise, returns empty string.
+    """
+    m = __re_split_conditional.match (property)
+    
+    if m:
+        return (m.group (1), '<' + m.group (2))
+
+    return None
+
+def is_conditional (property):
+    """ Returns True if a property is conditional.
+    """
+    if __re_colon.search (replace_grist (property, '')):
+        return True
+    else:
+        return False
+
+def select (features, properties):
+    """ Selects properties which correspond to any of the given features.
+    """
+    result = []
+    
+    # add any missing angle brackets
+    features = add_grist (features)
+
+    return [p for p in properties if get_grist(p) in features]
+
+def validate_property_sets (sets):
+    for s in sets:
+        validate(feature.split(s))
+
+
+def evaluate_conditionals_in_context (properties, context):
+    """ Removes all conditional properties which conditions are not met
+        For those with met conditions, removes the condition. Properies
+        in conditions are looked up in 'context'
+    """
+    base = []
+    conditionals = []
+
+    for p in properties:
+        if __re_has_condition.search (p):
+            conditionals.append (p)
+        else:
+            base.append (p)
+
+    result = base
+    for p in conditionals:
+
+        # Separate condition and property
+        s = __re_separate_condition_and_property.match (p)
+
+        # Split condition into individual properties
+        conditions = s.group (1).split (',')
+
+        # Evaluate condition        
+        if set.contains (c, context):
+            result.append (s.group (2))
+
+    return result
+
+def expand_subfeatures_in_conditions(properties):
+
+    result = []
+    for p in properties:
+
+        s = __re_separate_condition_and_property.match(p)
+        if not s:
+            result.append(p)
+        else:
+            condition = s.group(1)
+            # Condition might include several elements
+            condition = condition.split(",")
+            value = s.group(2)
+            
+            e = []
+            for c in condition:
+                # It common that condition includes a toolset which
+                # was never defined, or mentiones subfeatures which
+                # were never defined. In that case, validation will
+                # only produce an spirious error, so prevent
+                # validation by passing 'true' as second parameter.
+                e.extend(feature.expand_subfeatures(c, dont_validate=True))
+
+            if e == condition:
+                result.append(p)
+            else:
+                individual_subfeatures = set.difference(e, condition)
+                result.append(",".join(individual_subfeatures) + ":" + value)
+                
+    return result
+
+def change (properties, feature, value = None):
+    """ Returns a modified version of properties with all values of the
+        given feature replaced by the given value.
+        If 'value' is None the feature will be removed.
+    """
+    result = []
+    
+    feature = add_grist (feature)
+
+    for p in properties:
+        if get_grist (p) == feature:
+            if value:
+                result.append (replace_grist (value, feature))
+
+        else:
+            result.append (p)
+
+    return result
+
+
+################################################################
+# Private functions
+
+def __validate1 (property):
+    """ Exit with error if property is not valid.
+    """        
+    msg = None
+
+    f = get_grist (property)
+    if f:
+        value = get_value (property)
+
+        if not feature.valid (f):
+            f = ungrist (get_grist (property)) # Ungrist for better error messages
+            msg = "Unknown feature '%s'" % f
+
+        elif value and not 'free' in feature.attributes (f):
+            feature.validate_value_string (f, value)
+
+        elif not value:
+            f = ungrist (get_grist (property)) # Ungrist for better error messages
+            msg = "No value specified for feature '%s'" % f
+
+    else:
+        f = feature.implied_feature (property)
+        feature.validate_value_string (f, property)
+
+    if msg:
+        # FIXME: don't use globals like this. Import here to
+        # break circular dependency.
+        from b2.manager import get_manager
+        get_manager().errors()("Invalid property '%s': %s" % (property, msg))
+
+
+###################################################################
+# Still to port.
+# Original lines are prefixed with "#   "
+#
+#   
+#   import utility : ungrist ;
+#   import sequence : unique ;
+#   import errors : error ;
+#   import feature ;
+#   import regex ;
+#   import sequence ;
+#   import set ;
+#   import path ;
+#   import assert ;
+#   
+#   
+
+
+#   rule validate-property-sets ( property-sets * )
+#   {
+#       for local s in $(property-sets)
+#       {
+#           validate [ feature.split $(s) ] ;
+#       }
+#   }
+#
+
+def remove(attributes, properties):
+    """Returns a property sets which include all the elements
+    in 'properties' that do not have attributes listed in 'attributes'."""
+    
+    result = []
+    for e in properties:
+        attributes_new = feature.attributes(get_grist(e))
+        has_common_features = 0
+        for a in attributes_new:
+            if a in attributes:
+                has_common_features = 1
+                break
+
+        if not has_common_features:
+            result += e
+
+    return result
+
+
+def take(attributes, properties):
+    """Returns a property set which include all
+    properties in 'properties' that have any of 'attributes'."""
+    result = []
+    for e in properties:
+        if set.intersection(attributes, feature.attributes(get_grist(e))):
+            result.append(e)
+    return result
+
+
+class PropertyMap:
+    """ Class which maintains a property set -> string mapping.
+    """
+    def __init__ (self):
+        self.__properties = []
+        self.__values = []
+    
+    def insert (self, properties, value):
+        """ Associate value with properties.
+        """
+        self.__properties.append(properties)
+        self.__values.append(value)
+
+    def find (self, properties):
+        """ Return the value associated with properties
+        or any subset of it. If more than one
+        subset has value assigned to it, return the
+        value for the longest subset, if it's unique.
+        """
+        return self.find_replace (properties)
+
+    def find_replace(self, properties, value=None):
+        matches = []
+        match_ranks = []
+        
+        for i in range(0, len(self.__properties)):
+            p = self.__properties[i]
+                        
+            if set.contains (p, properties):
+                matches.append (i)
+                match_ranks.append(len(p))
+
+        best = sequence.select_highest_ranked (matches, match_ranks)
+
+        if not best:
+            return None
+
+        if len (best) > 1:
+            raise NoBestMatchingAlternative ()
+
+        best = best [0]
+            
+        original = self.__values[best]
+
+        if value:
+            self.__values[best] = value
+
+        return original
+
+#   local rule __test__ ( )
+#   {
+#       import errors : try catch ;
+#       import feature ;
+#       import feature : feature subfeature compose ;
+#       
+#       # local rules must be explicitly re-imported
+#       import property : path-order ;
+#       
+#       feature.prepare-test property-test-temp ;
+#   
+#       feature toolset : gcc : implicit symmetric ;
+#       subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4
+#         3.0 3.0.1 3.0.2 : optional ;
+#       feature define : : free ;
+#       feature runtime-link : dynamic static : symmetric link-incompatible ;
+#       feature optimization : on off ;
+#       feature variant : debug release : implicit composite symmetric ;
+#       feature rtti : on off : link-incompatible ;
+#   
+#       compose <variant>debug : <define>_DEBUG <optimization>off ;
+#       compose <variant>release : <define>NDEBUG <optimization>on ;
+#   
+#       import assert ;
+#       import "class" : new ;
+#       
+#       validate <toolset>gcc  <toolset>gcc-3.0.1 : $(test-space) ;
+#       
+#       assert.result <toolset>gcc <rtti>off <define>FOO
+#           : refine <toolset>gcc <rtti>off
+#           : <define>FOO
+#           : $(test-space)
+#           ;
+#   
+#       assert.result <toolset>gcc <optimization>on
+#           : refine <toolset>gcc <optimization>off
+#           : <optimization>on
+#           : $(test-space)
+#           ;
+#   
+#       assert.result <toolset>gcc <rtti>off
+#           : refine <toolset>gcc : <rtti>off : $(test-space)
+#           ;
+#   
+#       assert.result <toolset>gcc <rtti>off <rtti>off:<define>FOO
+#           : refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO 
+#           : $(test-space)
+#           ;
+#       
+#       assert.result <toolset>gcc:<define>foo <toolset>gcc:<define>bar 
+#           : refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar 
+#           : $(test-space)
+#           ;
+#   
+#       assert.result <define>MY_RELEASE
+#           : evaluate-conditionals-in-context 
+#             <variant>release,<rtti>off:<define>MY_RELEASE
+#             : <toolset>gcc <variant>release <rtti>off
+#                    
+#           ;
+#   
+#       try ;
+#           validate <feature>value : $(test-space) ;
+#       catch "Invalid property '<feature>value': unknown feature 'feature'." ;
+#   
+#       try ;
+#           validate <rtti>default : $(test-space) ;
+#       catch \"default\" is not a known value of feature <rtti> ;
+#       
+#       validate <define>WHATEVER : $(test-space) ;
+#   
+#       try ;
+#           validate <rtti> : $(test-space) ;
+#       catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ;
+#   
+#       try ;
+#           validate value : $(test-space) ;
+#       catch "value" is not a value of an implicit feature ;
+#              
+#   
+#       assert.result <rtti>on 
+#           : remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
+#   
+#       assert.result <include>a 
+#           : select include : <include>a <toolset>gcc ;
+#   
+#       assert.result <include>a 
+#           : select include bar : <include>a <toolset>gcc ;
+#   
+#       assert.result <include>a <toolset>gcc
+#           : select include <bar> <toolset> : <include>a <toolset>gcc ;
+#       
+#       assert.result <toolset>kylix <include>a 
+#           : change <toolset>gcc <include>a : <toolset> kylix ;
+#   
+#       # Test ordinary properties 
+#       assert.result 
+#         : split-conditional <toolset>gcc 
+#         ;
+#       
+#       # Test properties with ":"
+#       assert.result
+#         : split-conditional <define>FOO=A::B
+#         ;
+#       
+#       # Test conditional feature
+#       assert.result <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO
+#         : split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO
+#         ;
+#       
+#       feature.finish-test property-test-temp ;
+#   }
+#   
+    
Added: trunk/tools/build/v2/build/property_set.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/property_set.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,368 @@
+# Status: ported.
+# Base revision: 40480
+
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+from b2.util.utility import *
+import property, feature, string
+from b2.exceptions import *
+from b2.util.sequence import unique
+from b2.util.set import difference
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __cache
+
+    # A cache of property sets
+    # TODO: use a map of weak refs?
+    __cache = {}
+    
+reset ()
+
+
+def create (raw_properties = []):
+    """ Creates a new 'PropertySet' instance for the given raw properties,
+        or returns an already existing one.
+    """
+    raw_properties.sort ()
+    raw_properties = unique (raw_properties)
+ 
+    key = '-'.join (raw_properties)
+
+    if not __cache.has_key (key):
+        __cache [key] = PropertySet (raw_properties)
+
+    return __cache [key]
+
+def create_with_validation (raw_properties):
+    """ Creates new 'PropertySet' instances after checking
+        that all properties are valid and converting incidental
+        properties into gristed form.
+    """
+    property.validate (raw_properties)
+    
+    return create (property.make (raw_properties))
+
+def empty ():
+    """ Returns PropertySet with empty set of properties.
+    """
+    return create ()
+
+def create_from_user_input(raw_properties, jamfile_module, location):
+    """Creates a property-set from the input given by the user, in the
+    context of 'jamfile-module' at 'location'"""
+
+    property.validate(raw_properties)
+
+    specification = property.translate_paths(raw_properties, location)
+    specification = property.translate_indirect(specification, jamfile_module)
+    specification = property.expand_subfeatures_in_conditions(specification)
+    specification = property.make(specification)
+    return create(specification)
+
+
+def refine_from_user_input(parent_requirements, specification, jamfile_module,
+                           location):
+    """Refines requirements with requirements provided by the user.
+    Specially handles "-<property>value" syntax in specification
+     to remove given requirements.
+     - parent-requirements -- property-set object with requirements
+       to refine
+     - specification -- string list of requirements provided by the use
+     - project-module -- the module to which context indirect features
+       will be bound.
+     - location -- the path to which path features are relative."""
+
+
+    if not specification:
+        return parent_requirements
+
+    
+    add_requirements = []
+    remove_requirements = []
+
+    for r in specification:
+        if r[0] == '-':
+            remove_requirements.append(r[1:])
+        else:
+            add_requirements.append(r)
+        
+        if remove_requirements:
+            # Need to create property set, so that path features
+            # and indirect features are translated just like they
+            # are in project requirements.
+            ps = create_from_user_input(remove_requirements,
+                                        jamfile_module, location)
+            
+            parent_requirements = create(difference(parent_requirements.raw(),
+                                                    ps.raw()))
+            specification = add_requirements
+
+        requirements = create_from_user_input(specification,
+                                              jamfile_module, location)
+        
+        return parent_requirements.refine(requirements)
+    
+class PropertySet:
+    """ Class for storing a set of properties.
+        - there's 1<->1 correspondence between identity and value. No
+          two instances of the class are equal. To maintain this property,
+          the 'PropertySet.create' rule should be used to create new instances.
+          Instances are immutable.
+
+        - each property is classified with regard to it's effect on build
+          results. Incidental properties have no effect on build results, from
+          Boost.Build point of view. Others are either free, or non-free, which we
+          call 'base'. Each property belong to exactly one of those categories and
+          it's possible to get list of properties in each category.
+
+          In addition, it's possible to get list of properties with specific
+          attribute.
+
+        - several operations, like and refine and as_path are provided. They all use
+          caching whenever possible.
+    """
+    def __init__ (self, raw_properties = []):
+
+        self.raw_ = raw_properties
+        
+        self.incidental_ = []
+        self.free_ = []
+        self.base_ = []
+        self.dependency_ = []
+        self.non_dependency_ = []
+        self.conditional_ = []
+        self.non_conditional_ = []
+        self.propagated_ = []
+        self.link_incompatible = []
+        
+        # A cache of refined properties.
+        self.refined_ = {}
+        
+        # A cache of property sets created by adding properties to this one.
+        self.added_ = {}
+
+        # Cache for the default properties.
+        self.defaults_ = None
+
+        # Cache for the expanded properties.
+        self.expanded_ = None
+
+        # Cache for the expanded composite properties
+        self.composites_ = None
+
+        # Cache for the property set containing propagated properties.
+        self.propagated_ps_ = None
+        
+        # A map of features to its values.
+        self.feature_map_ = None
+        
+        # A tuple (target path, is relative to build directory)
+        self.target_path_ = None
+        
+        self.as_path_ = None
+        
+        # A cache for already evaluated sets.
+        self.evaluated_ = {}
+        
+        for p in raw_properties:
+            if not get_grist (p):
+                raise BaseException ("Invalid property: '%s'" % p)
+            
+            att = feature.attributes (get_grist (p))
+            
+            # A feature can be both incidental and free,
+            # in which case we add it to incidental.
+            if 'incidental' in att:
+                self.incidental_.append (p)
+            elif 'free' in att:
+                self.free_.append (p)
+            else:
+                self.base_.append (p)
+        
+            if 'dependency' in att:
+                self.dependency_.append (p)
+            else:
+                self.non_dependency_.append (p)
+            
+            if property.is_conditional (p):
+                self.conditional_.append (p)
+            else:
+                self.non_conditional_.append (p)
+                                    
+            if 'propagated' in att:
+                self.propagated_.append (p)
+
+            if 'link_incompatible' in att:
+                self.link_incompatible.append (p)
+    
+    def raw (self):
+        """ Returns the list of stored properties.
+        """
+        return self.raw_
+
+    def __str__(self):
+        return string.join(self.raw_)
+    
+    def base (self):
+        """ Returns properties that are neither incidental nor free.
+        """
+        return self.base_
+    
+    def free (self):
+        """ Returns free properties which are not dependency properties.
+        """
+        return self.free_
+
+    def dependency (self):
+        """ Returns dependency properties.
+        """
+        return self.dependency_
+    
+    def non_dependency (self):
+        """ Returns properties that are not dependencies.
+        """
+        return self.non_dependency_
+    
+    def conditional (self):
+        """ Returns conditional properties.
+        """
+        return self.conditional_
+        
+    def non_conditional (self):
+        """ Returns properties that are not conditional.
+        """
+        return self.non_conditional_
+              
+    def incidental (self):
+        """ Returns incidental properties.
+        """
+        return self.incidental_
+    
+    def refine (self, requirements):
+        """ Refines this set's properties using the requirements passed as an argument.
+        """
+        str_req = str (requirements)
+        if not self.refined_.has_key (str_req):
+            r = property.refine (self.raw (), requirements.raw ())
+
+            self.refined_ [str_req] = create (r)
+
+        return self.refined_ [str_req]
+
+    def expand (self):
+        if not self.expanded_:
+            expanded = feature.expand (self.raw_)
+            self.expanded_ = create (expanded)
+        return self.expanded_
+
+    def expand_componsite(self):
+        if not self.componsites_:
+            self.composites_ = create(feature.expand_composires(self.raw_))
+        return self.composites_
+
+    def evaluate_conditionals(self, context=None):
+        if not context:
+            context = self
+
+        if not self.evaluated_.has_key(context):
+            self.evaluated_[context] = create(
+                property.evaluate_conditionals_in_context(self.raw_,
+                                                          context.raw()))
+
+        return self.evaluated_[context]
+
+    def propagated (self):
+        if not self.propagated_ps_:
+            self.propagated_ps_ = create (self.propagated_)
+        return self.propagated_ps_
+
+    def add_defaults (self):
+        if not self.defaults_:
+            expanded = feature.add_defaults(self.raw_)
+            self.defaults_ = create(expanded)
+        return self.defaults_
+
+    def as_path (self):
+        if not self.as_path_:
+            self.as_path_ = property.as_path(self.base_)
+
+        return self.as_path_
+
+    def target_path (self):
+        """ Computes the target path that should be used for 
+            target with these properties.
+            Returns a tuple of
+              - the computed path
+              - if the path is relative to build directory, a value of
+                'true'. 
+        """
+        if not self.target_path_:
+            # The <location> feature can be used to explicitly
+            # change the location of generated targets
+            l = self.get ('<location>')
+            if l:
+                computed = l
+                is_relative = False
+
+            else:
+                p = self.as_path ()
+                
+                # Really, an ugly hack. Boost regression test system requires
+                # specific target paths, and it seems that changing it to handle
+                # other directory layout is really hard. For that reason,
+                # we teach V2 to do the things regression system requires.
+                # The value o '<location-prefix>' is predended to the path.
+                prefix = self.get ('<location-prefix>')
+                
+                if prefix:
+                    if len (prefix) > 1:
+                        raise AlreadyDefined ("Two <location-prefix> properties specified: '%s'" % prefix)
+                        
+                    computed = os.path.join(prefix[0], p)
+
+                else:
+                    computed = p
+
+                if not computed:
+                    computed = "."
+
+                is_relative = True
+
+            self.target_path_ = (computed, is_relative)
+            
+        return self.target_path_
+                    
+    def add (self, ps):
+        """ Creates a new property set containing the properties in this one,
+            plus the ones of the property set passed as argument.
+        """
+        if not self.added_.has_key (str (ps)):
+            self.added_ [str (ps)] = create (self.raw_ + ps.raw ())
+        return self.added_ [str (ps)]
+    
+    def add_raw (self, properties):
+        """ Creates a new property set containing the properties in this one,
+            plus the ones passed as argument.
+        """
+        return self.add (create (properties))
+
+    
+    def get (self, feature):
+        """ Returns all values of 'feature'.
+        """
+        if not self.feature_map_:
+            self.feature_map_ = {}
+
+            for v in self.raw_:
+                key = get_grist (v)
+                if not self.feature_map_.has_key (key):
+                    self.feature_map_ [key] = []
+                self.feature_map_ [get_grist (v)].append (replace_grist (v, ''))
+        
+        return self.feature_map_.get (feature, [])
+    
Added: trunk/tools/build/v2/build/scanner.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/scanner.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,157 @@
+# Status: ported.
+# Base revision: 45462
+# 
+# Copyright 2003 Dave Abrahams 
+# Copyright 2002, 2003, 2004, 2005 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+#  Implements scanners: objects that compute implicit dependencies for
+#  files, such as includes in C++.
+#
+#  Scanner has a regular expression used to find dependencies, some
+#  data needed to interpret those dependencies (for example, include
+#  paths), and a code which actually established needed relationship
+#  between actual jam targets.
+#
+#  Scanner objects are created by actions, when they try to actualize
+#  virtual targets, passed to 'virtual-target.actualize' method and are
+#  then associated with actual targets. It is possible to use
+#  several scanners for a virtual-target. For example, a single source
+#  might be used by to compile actions, with different include paths.
+#  In this case, two different actual targets will be created, each 
+#  having scanner of its own.
+#
+#  Typically, scanners are created from target type and action's 
+#  properties, using the rule 'get' in this module. Directly creating
+#  scanners is not recommended, because it might create many equvivalent
+#  but different instances, and lead in unneeded duplication of
+#  actual targets. However, actions can also create scanners in a special
+#  way, instead of relying on just target type.
+
+import property
+import bjam
+from b2.exceptions import *
+from b2.manager import get_manager
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __scanners, __rv_cache, __scanner_cache
+
+    # Maps registered scanner classes to relevant properties
+    __scanners = {}
+    
+    # A cache of scanners.
+    # The key is: class_name.properties_tag, where properties_tag is the concatenation 
+    # of all relevant properties, separated by '-'
+    __scanner_cache = {}
+    
+reset ()
+
+
+def register(scanner_class, relevant_properties):
+    """ Registers a new generator class, specifying a set of 
+        properties relevant to this scanner.  Ctor for that class
+        should have one parameter: list of properties.
+    """
+    __scanners[str(scanner_class)] = relevant_properties
+
+def registered(scanner_class):
+    """ Returns true iff a scanner of that class is registered
+    """
+    return __scanners.has_key(str(scanner_class))
+    
+def get(scanner_class, properties):
+    """ Returns an instance of previously registered scanner
+        with the specified properties.
+    """
+    scanner_name = str(scanner_class)
+    
+    if not registered(scanner_name):
+        raise BaseException ("attempt to get unregisted scanner: %s" % scanner_name)
+
+    relevant_properties = __scanners[scanner_name]
+    r = property.select(relevant_properties, properties)
+
+    scanner_id = scanner_name + '.' + '-'.join(r)
+    
+    if not __scanner_cache.has_key(scanner_name):
+        __scanner_cache[scanner_name] = scanner_class(r)
+
+    return __scanner_cache[scanner_name]
+
+class Scanner:
+    """ Base scanner class.
+    """
+    def __init__ (self):
+        pass
+    
+    def pattern (self):
+        """ Returns a pattern to use for scanning.
+        """
+        raise BaseException ("method must be overriden")
+
+    def process (self, target, matches):
+        """ Establish necessary relationship between targets,
+            given actual target beeing scanned, and a list of
+            pattern matches in that file.
+        """
+        raise BaseException ("method must be overriden")
+
+
+# Common scanner class, which can be used when there's only one
+# kind of includes (unlike C, where "" and <> includes have different
+# search paths).
+def CommonScanner(Scanner):
+
+    def __init__ (self, includes):
+        Scanner.__init__(self)
+        self.includes = includes
+
+    def process(self, target, matches, binding):
+
+        target_path = os.path.normpath(os.path.dirname(binding[0]))
+        bjam.call("mark-included", target, matches)
+
+        engine.set_target_variable(matches, "SEARCH",
+                                   [target_path] + self.includes_)
+        get_manager().scanners().propagate(self, matches)
+
+class ScannerRegistry:
+    
+    def __init__ (self, manager):
+        self.manager_ = manager
+        self.count_ = 0
+        self.exported_scanners_ = {}
+
+    def install (self, scanner, target, vtarget):
+        """ Installs the specified scanner on actual target 'target'. 
+            vtarget: virtual target from which 'target' was actualized.
+        """
+        engine = self.manager_.engine()
+        engine.set_target_variable(target, "HDRSCAN", scanner.pattern())
+        if not self.exported_scanners_.has_key(scanner):
+            exported_name = "scanner_" + str(self.count_)
+            self.count_ = self.count_ + 1
+            self.exported_scanners_[scanner] = exported_name
+            bjam.import_rule("", exported_name, scanner.process)
+        else:
+            exported_name = self.exported_scanners_[scanner]
+
+        engine.set_target_variable(target, "HDRRULE", exported_name)
+        
+        # scanner reflects difference in properties affecting    
+        # binding of 'target', which will be known when processing
+        # includes for it, will give information on how to
+        # interpret quoted includes.
+        engine.set_target_variable(target, "HDRGRIST", str(id(scanner)))
+        pass
+
+    def propagate(self, scanner, targets):
+        engine = self.manager_.engine()
+        engine.set_target_variable(targets, "HDRSCAN", scanner.pattern())
+        engine.set_target_variable(targets, "HDRRULE",
+                                   self.exported_scanners_[scanner])
+        engine.set_target_variable(targets, "HDRGRIST", str(id(scanner)))
+
Added: trunk/tools/build/v2/build/targets.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/targets.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,1264 @@
+# Status: being ported by Vladimir Prus
+# Still to do: call toolset.requirements when those are ported.
+# Remember the location of target.
+# Base revision: 40480
+
+# Copyright Vladimir Prus 2002-2007.
+# Copyright Rene Rivera 2006.
+#
+# Distributed under the Boost Software License, Version 1.0.
+#    (See accompanying file LICENSE_1_0.txt or copy at
+#          http://www.boost.org/LICENSE_1_0.txt)
+
+#   Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
+#
+#   Abstract targets are represented by classes derived from 'AbstractTarget' class. 
+#   The first abstract target is 'project_target', which is created for each
+#   Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
+#   (see project.jam). 
+#
+#   Project targets keep a list of 'MainTarget' instances.
+#   A main target is what the user explicitly defines in a Jamfile. It is
+#   possible to have several definitions for a main target, for example to have
+#   different lists of sources for different platforms. So, main targets
+#   keep a list of alternatives.
+#
+#   Each alternative is an instance of 'AbstractTarget'. When a main target
+#   subvariant is defined by some rule, that rule will decide what class to
+#   use, create an instance of that class and add it to the list of alternatives
+#   for the main target.
+#
+#   Rules supplied by the build system will use only targets derived
+#   from 'BasicTarget' class, which will provide some default behaviour.
+#   There will be two classes derived from it, 'make-target', created by the
+#   'make' rule, and 'TypedTarget', created by rules such as 'exe' and 'dll'.
+
+#
+#                         +------------------------+
+#                         |AbstractTarget          |
+#                         +========================+
+#                         |name                    |
+#                         |project                 |                                   
+#                         |                        |                                   
+#                         |generate(properties) = 0|                                   
+#                         +-----------+------------+                                   
+#                                     |                                                
+#                                     ^                                                
+#                                    / \                                               
+#                                   +-+-+                                              
+#                                     |                                                
+#                                     |                                                
+#            +------------------------+------+------------------------------+          
+#            |                               |                              |          
+#            |                               |                              |          
+# +----------+-----------+            +------+------+                +------+-------+  
+# | project_target       |            | MainTarget  |                | BasicTarget  |  
+# +======================+ 1        * +=============+  alternatives  +==============+  
+# | generate(properties) |o-----------+ generate    |<>------------->| generate     |  
+# | main-target          |            +-------------+                | construct = 0|
+# +----------------------+                                           +--------------+  
+#                                                                           |          
+#                                                                           ^          
+#                                                                          / \         
+#                                                                         +-+-+        
+#                                                                           |          
+#                                                                           |          
+#                 ...--+----------------+------------------+----------------+---+      
+#                      |                |                  |                    |      
+#                      |                |                  |                    |      
+#               ... ---+-----+   +------+-------+   +------+------+    +--------+-----+
+#                            |   | TypedTarget  |   | make-target |    | stage-target |
+#                            .   +==============+   +=============+    +==============+
+#                            .   | construct    |   | construct   |    | construct    |
+#                                +--------------+   +-------------+    +--------------+
+
+import re
+import os.path
+import sys
+
+from b2.util.utility import *
+import property, project, virtual_target, property_set, feature, generators
+from virtual_target import Subvariant
+from b2.exceptions import *
+from b2.util.sequence import unique
+from b2.util import set, path
+from b2.build.errors import user_error_checkpoint
+
+_re_separate_target_from_properties = re.compile (r'^([^<]*)(/(<.*))?$')
+
+class TargetRegistry:
+    
+    def __init__ (self):
+        # All targets that are currently being built.
+        # Only the key is id (target), the value is the actual object.
+        self.targets_being_built_ = {}
+
+        # Current indent for debugging messages
+        self.indent_ = ""
+
+        self.debug_building_ = "--debug-building" in bjam.variable("ARGV")
+
+    def main_target_alternative (self, target):
+        """ Registers the specified target as a main target alternatives.
+            Returns 'target'.
+        """
+        target.project ().add_alternative (target)
+        return target
+
+    def main_target_sources (self, sources, main_target_name, no_remaning=0):
+        """Return the list of sources to use, if main target rule is invoked
+        with 'sources'. If there are any objects in 'sources', they are treated
+        as main target instances, and the name of such targets are adjusted to
+        be '<name_of_this_target>__<name_of_source_target>'. Such renaming
+        is disabled is non-empty value is passed for 'no-renaming' parameter."""
+        result = []
+
+        for t in sources:
+            if isinstance (t, AbstractTarget):
+                name = t.name ()
+
+                if not no_renaming:
+                    new_name = main_target_name + '__' + name
+                    t.rename (new_name)
+
+                # Inline targets are not built by default.
+                p = t.project()
+                p.mark_target_as_explicit(name)                    
+                result.append (new_name)
+
+            else:
+                result.append (t)
+
+        return result
+
+
+    def main_target_requirements(self, specification, project):
+        """Returns the requirement to use when declaring a main target,
+         which are obtained by
+         - translating all specified property paths, and
+         - refining project requirements with the one specified for the target
+        
+         'specification' are the properties xplicitly specified for a
+          main target
+         'project' is the project where the main taret is to be declared."""
+
+        # FIXME: revive after toolset.requirements are ported.
+        #specification.append(toolset.requirements)
+
+        requirements = property_set.refine_from_user_input(
+            project.get("requirements"), specification,
+            project.project_module, project.get("location"))
+
+        return requirements
+
+    def main_target_usage_requirements (self, specification, project):
+        """ Returns the use requirement to use when declaraing a main target,
+            which are obtained by
+            - translating all specified property paths, and
+            - adding project's usage requirements
+            specification:  Use-properties explicitly specified for a main target
+            project:        Project where the main target is to be declared
+        """
+        project_usage_requirements = project.get ('usage-requirements')
+
+        # We don't use 'refine-from-user-input' because I'm not sure if:
+        # - removing of parent's usage requirements makes sense
+        # - refining of usage requirements is not needed, since usage requirements
+        #   are always free.
+        usage_requirements = property_set.create_from_user_input(
+            specification, project.project_module(), project.get("location"))
+        
+        return project_usage_requirements.add (usage_requirements)
+
+    def main_target_default_build (self, specification, project):
+        """ Return the default build value to use when declaring a main target,
+            which is obtained by using specified value if not empty and parent's
+            default build attribute otherwise.
+            specification:  Default build explicitly specified for a main target
+            project:        Project where the main target is to be declared
+        """
+        if specification:
+            result = specification
+
+        else:
+            result = project.get ('default-build')
+
+        return property_set.create_with_validation (result)
+
+    def start_building (self, main_target_instance):
+        """ Helper rules to detect cycles in main target references.
+        """
+        if self.targets_being_built_.has_key(id(main_target_instance)):
+            names = []
+            for t in self.targets_being_built_.values():
+                names.append (t.full_name())
+            
+            raise Recursion ("Recursion in main target references" 
+                "the following target are being built currently: '%s'" % names)
+        
+        self.targets_being_built_[id(main_target_instance)] = main_target_instance
+
+    def end_building (self, main_target_instance):
+        assert (self.targets_being_built_.has_key (id (main_target_instance)))
+        del self.targets_being_built_ [id (main_target_instance)]
+
+    def create_typed_target (self, type, project, name, sources, requirements, default_build, usage_requirements):
+        """ Creates a TypedTarget with the specified properties.
+            The 'name', 'sources', 'requirements', 'default_build' and
+            'usage_requirements' are assumed to be in the form specified
+            by the user in Jamfile corresponding to 'project'.
+        """
+        return self.main_target_alternative (TypedTarget (name, project, type,
+            self.main_target_sources (sources, name),
+            self.main_target_requirements (requirements, project),
+            self.main_target_default_build (default_build, project),
+            self.main_target_usage_requirements (usage_requirements, project)))
+
+    def increase_indent(self):
+        self.indent_ += "    "
+
+    def decrease_indent(self):
+        self.indent_ = self.indent_[0:-4]
+
+    def logging(self):
+        return self.debug_building_
+
+    def log(self, message):
+        if self.debug_building_:
+            print self.indent_ + message
+
+class GenerateResult:
+    
+    def __init__ (self, ur=None, targets=None):
+        if not targets:
+            targets = []
+        
+        self.__usage_requirements = ur
+        self.__targets = targets
+
+        if not self.__usage_requirements:
+            self.__usage_requirements = property_set.empty ()
+
+    def usage_requirements (self):
+        return self.__usage_requirements
+
+    def targets (self):
+        return self.__targets
+    
+    def extend (self, other):
+        assert (isinstance (other, GenerateResult))
+        
+        self.__usage_requirements = self.__usage_requirements.add (other.usage_requirements ())
+        self.__targets.extend (other.targets ())
+
+class AbstractTarget:
+    """ Base class for all abstract targets.
+    """
+    def __init__ (self, name, project, manager = None):
+        """ manager:     the Manager object
+            name:        name of the target
+            project:     the project target to which this one belongs
+            manager:the manager object. If none, uses project.manager ()
+        """
+        assert (isinstance (project, ProjectTarget))
+        # Note: it might seem that we don't need either name or project at all.
+        # However, there are places where we really need it. One example is error
+        # messages which should name problematic targets. Another is setting correct
+        # paths for sources and generated files.
+        
+        # Why allow manager to be specified? Because otherwise project target could not derive
+        # from this class.
+        if manager:
+            self.manager_ = manager
+        else:
+            self.manager_ = project.manager ()
+
+        self.name_ = name
+        self.project_ = project        
+    
+    def manager (self):
+        return self.manager_
+    
+    def name (self):
+        """ Returns the name of this target.
+        """
+        return self.name_
+    
+    def project (self):
+        """ Returns the project for this target.
+        """
+        return self.project_
+    
+    def location (self):
+        """ Return the location where the target was declared.
+        """
+        return self.location_
+            
+    def full_name (self):
+        """ Returns a user-readable name for this target.
+        """
+        location = self.project ().get ('location')
+        return location + '/' + self.name_
+        
+    def generate (self, property_set):
+        """ Takes a property set.  Generates virtual targets for this abstract
+            target, using the specified properties, unless a different value of some
+            feature is required by the target. 
+            On success, returns a GenerateResult instance with:
+                - a property_set with the usage requirements to be
+                  applied to dependents 
+                - a list of produced virtual targets, which may be
+                   empty.  
+            If 'property_set' is empty, performs default build of this
+            target, in a way specific to derived class.
+        """
+        raise BaseException ("method should be defined in derived classes")
+    
+    def rename (self, new_name):
+        self.name_ = new_name
+
+class ProjectTarget (AbstractTarget):
+    """ Project target class (derived from 'AbstractTarget')
+
+        This class these responsibilities:
+        - maintaining a list of main target in this project and
+          building it
+
+        Main targets are constructed in two stages:
+        - When Jamfile is read, a number of calls to 'add_alternative' is made.
+          At that time, alternatives can also be renamed to account for inline
+          targets.
+        - The first time 'main-target' or 'has-main-target' rule is called,
+          all alternatives are enumerated an main targets are created.
+    """
+    def __init__ (self, manager, name, project_module, parent_project, requirements, default_build):
+        AbstractTarget.__init__ (self, name, self, manager)
+        
+        self.project_module_ = project_module
+        self.location_ = manager.projects().attribute (project_module, 'location')
+        self.requirements_ = requirements
+        self.default_build_ = default_build
+       
+        self.build_dir_ = None
+
+        if parent_project:
+            self.inherit (parent_project)
+        
+        # A cache of IDs
+        self.ids_cache_ = {}
+        
+        # True is main targets have already been built.
+        self.built_main_targets_ = False
+        
+        # A list of the registered alternatives for this project.
+        self.alternatives_ = []
+
+        # A map from main target name to the target corresponding
+        # to it.
+        self.main_target_ = {}
+        
+        # Targets marked as explicit.
+        self.explicit_targets_ = []
+
+        # The constants defined for this project.
+        self.constants_ = {}
+
+        # Whether targets for all main target are already created.
+        self.built_main_targets_ = 0
+
+    # TODO: This is needed only by the 'make' rule. Need to find the
+    # way to make 'make' work without this method.
+    def project_module (self):
+        return self.project_module_
+    
+    def get (self, attribute):
+        return self.manager().projects().attribute(
+            self.project_module_, attribute)
+
+    def build_dir (self):
+        if not self.build_dir_:
+            self.build_dir_ = self.get ('build-dir')
+            if not self.build_dir_:
+                self.build_dir_ = os.path.join (os.path.dirname(
+                    self.project_.get ('location')), 'bin')
+
+        return self.build_dir_
+
+    def generate (self, ps):
+        """ Generates all possible targets contained in this project.
+        """
+        self.manager_.targets().log(
+            "Building project '%s' with '%s'" % (self.name (), ps.raw ()))
+        self.manager_.targets().increase_indent ()
+        
+        result = GenerateResult ()
+                
+        for t in self.targets_to_build ():
+            g = t.generate (ps)
+            result.extend (g)
+            
+        self.manager_.targets().decrease_indent ()
+        return result
+
+    def targets_to_build (self):
+        """ Computes and returns a list of AbstractTarget instances which
+            must be built when this project is built.
+        """
+        result = []
+        
+        if not self.built_main_targets_:
+            self.build_main_targets ()
+        
+        # Collect all main targets here, except for "explicit" ones.
+        for n, t  in self.main_target_.iteritems ():
+            if not t.name () in self.explicit_targets_:
+                result.append (t)
+
+        # Collect all projects referenced via "projects-to-build" attribute.
+        self_location = self.get ('location')
+        for pn in self.get ('projects-to-build'):
+            result.append (self.find(pn))
+                        
+        return result
+
+    def mark_target_as_explicit (self, target_name):
+        """Add 'target' to the list of targets in this project
+        that should be build only by explicit request."""
+        
+        # Record the name of the target, not instance, since this
+        # rule is called before main target instaces are created.
+        self.explicit_.append(target_name)
+    
+    def add_alternative (self, target_instance):
+        """ Add new target alternative.
+        """
+        if self.built_main_targets_:
+            raise IllegalOperation ("add-alternative called when main targets are already created for project '%s'" % self.full_name ())
+
+        self.alternatives_.append (target_instance)
+
+    def main_target (self, name):
+        if not self.built_main_targets_:
+            self.build_main_targets()
+
+        return self.main_target_[name]
+
+    def has_main_target (self, name):
+        """Tells if a main target with the specified name exists."""
+        if not self.built_main_targets_:
+            self.build_main_targets()
+
+        return self.main_target_.has_key(name)
+            
+    def create_main_target (self, name):
+        """ Returns a 'MainTarget' class instance corresponding to the 'name'.
+        """
+        if not self.built_main_targets_:
+            self.build_main_targets ()
+                        
+        return self.main_targets_.get (name, None)
+
+
+    def find_really(self, id):
+        """ Find and return the target with the specified id, treated
+            relative to self.
+        """
+        result = None        
+        current_location = self.get ('location')
+
+        __re_split_project_target = re.compile (r'(.*)//(.*)')
+        split = __re_split_project_target.match (id)
+
+        project_part = None
+        target_part = None
+
+        if split:
+            project_part = split.group (1)
+            target_part = split.group (2)
+
+        project_registry = self.project_.manager ().projects ()
+        
+        extra_error_message = ''
+        if project_part:
+            # There's explicit project part in id. Looks up the
+            # project and pass the request to it.
+            pm = project_registry.find (project_part, current_location)
+            
+            if pm:
+                project_target = project_registry.target (pm)
+                result = project_target.find (target_part, no_error=1)
+
+            else:
+                extra_error_message = "error: could not find project '$(project_part)'"
+
+        else:
+            # Interpret target-name as name of main target
+            # Need to do this before checking for file. Consider this:
+            #
+            #  exe test : test.cpp ;
+            #  install s : test : <location>. ;
+            #
+            # After first build we'll have target 'test' in Jamfile and file
+            # 'test' on the disk. We need target to override the file.
+            
+            result = None
+            if self.has_main_target(id):
+                result = self.main_target(id)
+
+            if not result:
+                result = FileReference (self.manager_, id, self.project_)
+                if not result.exists ():
+                    # File actually does not exist.
+                    # Reset 'target' so that an error is issued.
+                    result = None
+                    
+
+            if not result:
+                # Interpret id as project-id
+                project_module = project_registry.find (id, current_location)
+                if project_module:
+                    result = project_registry.target (project_module)
+                                    
+        return result
+
+    def find (self, id, no_error = False):
+        v = self.ids_cache_.get (id, None)
+        
+        if not v:
+            v = self.find_really (id)
+            self.ids_cache_ [id] = v
+
+        if v or no_error:
+            return v
+
+        raise BaseException ("Unable to find file or target named '%s'\nreferred from project at '%s'" % (id, self.get ('location')))
+
+    
+    def build_main_targets (self):
+        self.built_main_targets_ = True
+        
+        for a in self.alternatives_:
+            name = a.name ()
+            if not self.main_target_.has_key (name):
+                t = MainTarget (name, self.project_)
+                self.main_target_ [name] = t
+            
+            self.main_target_ [name].add_alternative (a)
+
+    def add_constant(self, name, value, path=0):
+        """Adds a new constant for this project.
+
+        The constant will be available for use in Jamfile
+        module for this project. If 'path' is true,
+        the constant will be interpreted relatively
+        to the location of project.
+        """
+
+        if path:
+            value = os.path.join(self.location_, value)
+            # Now make the value absolute path
+            value = os.path.join(os.getcwd(), value)
+            
+        self.constants_[name] = value
+        bjam.call("set-variable", self.project_module(), name, value)
+
+    def inherit(self, parent_project):
+        for c in parent_project.constants_:
+            # No need to pass the type. Path constants were converted to
+            # absolute paths already by parent.
+            self.add-constant(parent_project.constants_[c])
+        
+        # Import rules from parent 
+        this_module = self.project_module()
+        parent_module = parent_project.project_module()
+
+        rules = bjam.call("RULENAMES", parent_module)
+        if not rules:
+            rules = []
+        user_rules = [x for x in rules
+                      if x not in self.manager().projects().project_rules()]
+        if user_rules:
+            bjam.call("import-rules-from-parent", parent_module, this_module, user_rules)
+        
+class MainTarget (AbstractTarget):
+    """ A named top-level target in Jamfile.
+    """
+    def __init__ (self, name, project):
+        AbstractTarget.__init__ (self, name, project)    
+        self.alternatives_ = []
+        self.default_build_ = property_set.empty ()
+        
+    def add_alternative (self, target):
+        """ Add a new alternative for this target.
+        """
+        d = target.default_build ()
+        
+        if self.alternatives_ and self.default_build_ != d:
+            raise BaseException ("Default build must be identical in all alternatives\n"
+              "main target is '%s'\n"
+              "with '%s'\n"
+              "differing from previous default build: '%s'" % (full_name (), d.raw (), self.default_build_.raw ()))
+
+        else:
+            self.default_build_ = d
+
+        self.alternatives_.append (target)
+
+    def __select_alternatives (self, property_set, debug):
+        """ Returns the best viable alternative for this property_set
+            See the documentation for selection rules.
+            # TODO: shouldn't this be 'alternative' (singular)?
+        """
+        # When selecting alternatives we have to consider defaults,
+        # for example:
+        #    lib l : l.cpp : <variant>debug ;
+        #    lib l : l_opt.cpp : <variant>release ;
+        # won't work unless we add default value <variant>debug.
+        property_set = property_set.add_defaults ()
+        
+        # The algorithm: we keep the current best viable alternative.
+        # When we've got new best viable alternative, we compare it
+        # with the current one. 
+        best = None
+        best_properties = None
+                        
+        if len (self.alternatives_) == 0:
+            return None
+
+        if len (self.alternatives_) == 1:
+            return self.alternatives_ [0]
+
+        for v in self.alternatives_:
+            properties = v.match (property_set, debug)
+                       
+            if properties:
+                if not best:
+                    best = v
+                    best_properties = properties
+
+                else:
+                    if set.equal (properties, best_properties):
+                        return None
+
+                    elif set.contains (properties, best_properties):
+                        # Do nothing, this alternative is worse
+                        pass
+
+                    elif set.contains (best_properties, properties):
+                        best = v
+                        best_properties = properties
+
+                    else:
+                        return None
+
+        return best
+
+    def apply_default_build (self, property_set):
+        # 1. First, see what properties from default_build
+        # are already present in property_set. 
+        
+        raw = property_set.raw ()
+        specified_features = get_grist (raw)
+        
+        defaults_to_apply = []
+        for d in self.default_build_.raw ():
+            if not get_grist (d) in specified_features:
+                defaults_to_apply.append (d)
+        
+        # 2. If there's any defaults to be applied, form the new
+        # build request. Pass it throw 'expand-no-defaults', since
+        # default_build might contain "release debug", which will
+        # result in two property_sets.
+        result = []
+        if defaults_to_apply:
+
+            # We have to compress subproperties here to prevent
+            # property lists like:
+            #
+            #    <toolset>msvc <toolset-msvc:version>7.1 <threading>multi
+            #
+            # from being expanded into:
+            #
+            #    <toolset-msvc:version>7.1/<threading>multi
+            #    <toolset>msvc/<toolset-msvc:version>7.1/<threading>multi
+            #
+            # due to cross-product property combination.  That may
+            # be an indication that
+            # build_request.expand-no-defaults is the wrong rule
+            # to use here.
+            compressed = feature.compress-subproperties (raw)
+
+            properties = build_request.expand_no_defaults (compressed, defaults_to_apply)
+              
+            if properties:
+                for p in properties:
+                    result.append (property_set.create (feature.expand (feature.split (p))))
+
+            else:
+                result .append (property_set.empty ())
+            
+        else:
+            result.append (property_set)
+
+        return result
+
+    def generate (self, ps):
+        """ Select an alternative for this main target, by finding all alternatives
+            which requirements are satisfied by 'properties' and picking the one with
+            longest requirements set.
+            Returns the result of calling 'generate' on that alternative.
+        """
+        self.manager_.targets ().start_building (self)
+
+        # We want composite properties in build request act as if
+        # all the properties it expands too are explicitly specified.
+        ps = ps.expand ()
+        
+        all_property_sets = self.apply_default_build (ps)
+
+        result = GenerateResult ()
+        
+        for p in all_property_sets:
+            result.extend (self.__generate_really (p))
+
+        self.manager_.targets ().end_building (self)
+
+        return result
+        
+    def __generate_really (self, prop_set):
+        """ Generates the main target with the given property set
+            and returns a list which first element is property_set object
+            containing usage_requirements of generated target and with
+            generated virtual target in other elements. It's possible
+            that no targets are generated.
+        """
+        best_alternative = self.__select_alternatives (prop_set, debug=0)
+
+        if not best_alternative:
+            self.__select_alternatives(prop_set, debug=1)
+            raise NoBestMatchingAlternative (
+                "Failed to build '%s'\n"
+                "with properties '%s'\n"
+                "because no best-matching alternative could be found."
+                  % (full_name, prop_set.raw ()))
+
+        result = best_alternative.generate (prop_set)
+                    
+        # Now return virtual targets for the only alternative
+        return result
+    
+    def rename(self, new_name):
+        AbstractTarget.rename(self, new_name)
+        for a in self.alternatives_:
+            a.rename(new_name)
+
+class FileReference (AbstractTarget):
+    """ Abstract target which refers to a source file.
+        This is artificial creature; it's usefull so that sources to 
+        a target can be represented as list of abstract target instances.
+    """
+    def __init__ (self, manager, file, project):
+        AbstractTarget.__init__ (self, file, project)
+        self.file_location_ = None
+    
+    def generate (self, properties):
+         return GenerateResult (None, [
+             self.manager_.virtual_targets ().from_file (
+             self.name_, self.location(), self.project_) ])
+
+    def exists (self):
+        """ Returns true if the referred file really exists.
+        """
+        if self.location ():
+            return True
+        else:
+            return False
+
+    def location (self):
+        # Returns the location of target. Needed by 'testing.jam'
+        if not self.file_location_:
+            source_location = self.project_.get ('source-location')
+            
+            for src_dir in source_location:
+                location = os.path.join(src_dir, self.name())
+                if os.path.isfile(location):
+                    self.file_location_ = src_dir
+                    self.file_path = location
+                    break
+
+        return self.file_location_
+
+class BasicTarget (AbstractTarget):
+    """ Implements the most standard way of constructing main target
+        alternative from sources. Allows sources to be either file or
+        other main target and handles generation of those dependency
+        targets.
+    """
+    def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None):
+        AbstractTarget.__init__ (self, name, project)
+    
+        for s in sources:
+            if get_grist (s):
+                raise InvalidSource ("property '%s' found in the 'sources' parameter for '%s'" (s, name))
+    
+        self.sources_ = sources
+        
+        if not requirements: requirements = property_set.empty ()
+        self.requirements_ = requirements
+
+        if not default_build: default_build = property_set.empty ()
+        self.default_build_ = default_build
+
+        if not usage_requirements: usage_requirements = property_set.empty ()
+        self.usage_requirements_ = usage_requirements
+        
+        # A cache for resolved references
+        self.source_targets_ = None
+        
+        # A cache for generated targets
+        self.generated_ = {}
+        
+        # A cache for build requests
+        self.request_cache = {}
+
+        self.user_context_ = self.manager_.errors().capture_user_context()
+        
+    def sources (self):
+        """ Returns the list of AbstractTargets which are used as sources.
+            The extra properties specified for sources are not represented.
+            The only used of this rule at the moment is the '--dump-test'
+            feature of the test system.            
+        """
+        if self.source_targets_ == None:
+            self.source_targets_ = []
+            for s in self.sources_:
+                self.source_targets_.append (self.resolve_reference (s, self.project_))
+
+        return self.source_targets_
+
+    def requirements (self):
+        return self.requirements_
+                        
+    def default_build (self):
+        return self.default_build_
+
+    def resolve_reference (self, target_reference, project):
+        """ Given a target_reference, made in context of 'project',
+            returns the AbstractTarget instance that is referred to, as well
+            as properties explicitly specified for this reference.
+        """
+        # Separate target name from properties override
+        split = _re_separate_target_from_properties.match (target_reference)
+        if not split:
+            raise BaseException ("Invalid reference: '%s'" % target_reference)
+        
+        id = split.group (1)
+        
+        sproperties = []
+        
+        if split.group (3):
+            sproperties = property.make (feature.split (split.group (3)))
+            sproperties = self.manager.features ().expand_composites (sproperties)
+    
+        # Find the target
+        target = project.find (id)
+        
+        return (target, property_set.create (sproperties))
+
+    def common_properties (self, build_request, requirements):
+        """ Given build request and requirements, return properties
+            common to dependency build request and target build
+            properties.
+        """
+        # For optimization, we add free requirements directly,
+        # without using complex algorithsm.
+        # This gives the complex algorithm better chance of caching results.
+        free = requirements.free ()        
+        non_free = property_set.create (requirements.base () + requirements.incidental ())
+        
+        key = str (build_request) + '-' + str (non_free)
+        if not self.request_cache.has_key (key):
+            self.request_cache [key] = self.__common_properties2 (build_request, non_free)       
+
+        return self.request_cache [key].add_raw (free)
+
+    # Given 'context' -- a set of already present properties, and 'requirements',
+    # decide which extra properties should be applied to 'context'. 
+    # For conditional requirements, this means evaluating condition. For 
+    # indirect conditional requirements, this means calling a rule. Ordinary
+    # requirements are always applied.
+    #
+    # Handles situation where evaluating one conditional requirements affects
+    # condition of another conditional requirements, for example:
+    #
+    #     <toolset>gcc:<variant>release <variant>release:<define>RELEASE
+    #
+    # If 'what' is 'refined' returns context refined with new requirements. 
+    # If 'what' is 'added' returns just the requirements that must be applied.
+    def evaluate_requirements(self, requirements, context, what):
+        # Apply non-conditional requirements. 
+        # It's possible that that further conditional requirement change 
+        # a value set by non-conditional requirements. For example:
+        #
+        #    exe a : a.cpp : <threading>single <toolset>foo:<threading>multi ;
+        # 
+        # I'm not sure if this should be an error, or not, especially given that
+        #
+        #    <threading>single 
+        #
+        # might come from project's requirements.
+    
+        unconditional = feature.expand(requirements.non_conditional())
+    
+        raw = context.raw()
+        raw = property.refine(raw, unconditional)
+      
+        # We've collected properties that surely must be present in common
+        # properties. We now try to figure out what other properties
+        # should be added in order to satisfy rules (4)-(6) from the docs.
+    
+        conditionals = requirements.conditional()
+
+        # It's supposed that #conditionals iterations
+        # should be enough for properties to propagate along conditions in any
+        # direction.
+        max_iterations = len(conditionals) +\
+                         len(requirements.get("<conditional>")) + 1
+    
+        added_requirements = []
+        current = raw
+    
+        # It's assumed that ordinary conditional requirements can't add
+        # <indirect-conditional> properties, and that rules referred
+        # by <indirect-conditional> properties can't add new 
+        # <indirect-conditional> properties. So the list of indirect conditionals
+        # does not change.
+        indirect = requirements.get("<conditional>")
+        indirect = [s[1:] for s in indirect]
+    
+        ok = 0
+        for i in range(0, max_iterations):
+
+            e = property.evaluate_conditionals_in_context(conditionals, current)
+        
+            # Evaluate indirect conditionals.
+            for i in indirect:
+                e.extend(bjam.call(i, current))
+
+            if e == added_requirements:
+                # If we got the same result, we've found final properties.
+                ok = 1
+                break
+            else:
+                # Oops, results of evaluation of conditionals has changed.
+                # Also 'current' contains leftover from previous evaluation.
+                # Recompute 'current' using initial properties and conditional
+                # requirements.
+                added_requirements = e
+                current = property.refine(raw, feature.expand(e))
+
+        if not ok:
+            self.manager().errors()("Can't evaluate conditional properties "
+                                    + str(conditionals))
+
+    
+        if what == "added":
+            return property_set.create(unconditional + added_requirements)
+        elif what == "refined":
+            return property_set.create(current)
+        else:
+            self.manager().errors("Invalid value of the 'what' parameter")
+
+    def __common_properties2(self, build_request, requirements):
+        # This guarantees that default properties are present
+        # in result, unless they are overrided by some requirement.
+        # TODO: There is possibility that we've added <foo>bar, which is composite
+        # and expands to <foo2>bar2, but default value of <foo2> is not bar2,
+        # in which case it's not clear what to do.
+        # 
+        build_request = build_request.add_defaults()
+        # Featured added by 'add-default' can be composite and expand
+        # to features without default values -- so they are not added yet.
+        # It could be clearer/faster to expand only newly added properties
+        # but that's not critical.
+        build_request = build_request.expand()
+        
+        return self.evaluate_requirements(requirements, build_request,
+                                          "refined")
+    
+    def match (self, property_set, debug):
+        """ Returns the alternative condition for this alternative, if
+            the condition is satisfied by 'property_set'.
+        """
+        # The condition is composed of all base non-conditional properties.
+        # It's not clear if we should expand 'self.requirements_' or not.
+        # For one thing, it would be nice to be able to put
+        #    <toolset>msvc-6.0 
+        # in requirements.
+        # On the other hand, if we have <variant>release in condition it 
+        # does not make sense to require <optimization>full to be in
+        # build request just to select this variant.
+        bcondition = self.requirements_.base ()
+        ccondition = self.requirements_.conditional ()
+        condition = set.difference (bcondition, ccondition)
+
+        if debug:
+            print "    next alternative: required properties:", str(condition)
+        
+        if set.contains (condition, property_set.raw ()):
+
+            if debug:
+                print "        matched"
+            
+            return condition
+
+        else:
+            return None
+    
+    def generate_dependencies (self, dependencies, property_set):
+        """ Takes a target reference, which might be either target id
+            or a dependency property, and generates that target using
+            'property_set' as build request.
+
+            Returns a tuple (result, usage_requirements).
+        """
+        result_var = []
+        usage_requirements = []
+        for dependency in dependencies:
+            grist = get_grist (dependency)
+            id = replace_grist (dependency, '')
+        
+            result = self.generate_from_reference (id, self.project_, property_set)
+
+            # FIXME:
+            # TODO: this is a problem: the grist must be kept and the value
+            #       is the object itself. This won't work in python.
+            targets = [ self.manager_.register_object (x) for x in result.targets () ]
+            
+            result_var += replace_grist (targets, grist)
+            usage_requirements += result.usage_requirements ().raw ()
+
+        return (result_var, usage_requirements)
+
+    @user_error_checkpoint
+    def generate (self, ps):
+        """ Determines final build properties, generates sources,
+        and calls 'construct'. This method should not be
+        overridden.
+        """
+        self.manager_.errors().push_user_context(
+            "Generating target " + self.full_name(), self.user_context_)
+        
+        if self.manager().targets().logging():
+            self.manager().targets().log(
+                "Building target '%s'" % self.name_)
+            self.manager().targets().increase_indent ()
+            self.manager().targets().log(
+                "Build request: '%s'" % str (ps.raw ()))
+            cf = self.manager().command_line_free_features()
+            self.manager().targets().log(
+                "Command line free features: '%s'" % str (cf.raw ()))            
+            self.manager().targets().log(
+                "Target requirements: %s'" % str (self.requirements().raw ()))
+
+        if not self.generated_.has_key (str (ps)):
+
+            # Apply free features form the command line.  If user
+            # said 
+            #   define=FOO
+            # he most likely want this define to be set for all compiles.
+            ps = ps.refine(self.manager().command_line_free_features())            
+            rproperties = self.common_properties (ps, self.requirements_)
+
+            self.manager().targets().log(
+                "Common properties are '%s'" % str (rproperties.raw ()))
+            
+            if rproperties.get("<build>") != "no":
+                
+                result = GenerateResult ()
+
+                properties = rproperties.non_dependency ()
+                
+                (p, u) = self.generate_dependencies (rproperties.dependency (), rproperties)
+                properties += p
+                usage_requirements = u
+
+                (source_targets, u) = self.generate_dependencies (self.sources_, rproperties)
+                usage_requirements += u
+
+                self.manager_.targets().log(
+                    "Usage requirements for '%s' are '%s'" % (self.name_, usage_requirements))
+
+                rproperties = property_set.create (properties + usage_requirements)
+                usage_requirements = property_set.create (usage_requirements)
+
+                self.manager_.targets().log(
+                    "Build properties: '%s'" % str(rproperties.raw()))
+                
+                extra = rproperties.get ('<source>')
+                source_targets += replace_grist (extra, '')               
+                source_targets = replace_references_by_objects (self.manager (), source_targets)
+                
+                # We might get duplicate sources, for example if
+                # we link to two library which have the same <library> in
+                # usage requirements.
+                source_targets = unique (source_targets)
+
+                result = self.construct (self.name_, source_targets, rproperties)
+                if result:
+                    assert len(result) == 2
+                    gur = result [0]
+                    result = result [1]
+
+                    s = self.create_subvariant (
+                        result,
+                        self.manager().virtual_targets().recent_targets(), ps,
+                        source_targets, rproperties, usage_requirements)
+                    self.manager().virtual_targets().clear_recent_targets()
+                    
+                    ur = self.compute_usage_requirements (s)
+                    ur = ur.add (gur)
+                    s.set_usage_requirements (ur)
+
+                    self.manager_.targets().log (
+                        "Usage requirements from '%s' are '%s'" %
+                        (self.name, str(rproperties.raw())))
+                    
+                    self.generated_ [str (ps)] = GenerateResult (ur, result)
+                else:
+                    self.generated_ [str (ps)] = GenerateResult (property_set.empty(), [])
+            else:
+                self.manager().targets().log(
+                    "Skipping build: <build>no in common properties")
+
+                # We're here either because there's error computing
+                # properties, or there's <build>no in properties.
+                # In the latter case we don't want any diagnostic.
+                # In the former case, we need diagnostics. TODOo
+                self.generated_ [str (ps)] = GenerateResult (rproperties, [])
+        else:
+            self.manager().targets().log ("Already built")
+
+        self.manager().targets().decrease_indent()
+
+        return self.generated_ [str (ps)]
+
+    def generate_from_reference (self, target_reference, project, property_set):
+        """ Attempts to generate the target given by target reference, which
+            can refer both to a main target or to a file.
+            Returns a list consisting of
+            - usage requirements
+            - generated virtual targets, if any
+            target_reference:  Target reference
+            project:           Project where the reference is made
+            property_set:      Properties of the main target that makes the reference
+        """
+        target, sproperties = self.resolve_reference (target_reference, project)
+        
+        # Take properties which should be propagated and refine them
+        # with source-specific requirements.
+        propagated = property_set.propagated ()
+        rproperties = propagated.refine (sproperties)
+            
+        return target.generate (rproperties)
+    
+    def compute_usage_requirements (self, subvariant):
+        """ Given the set of generated targets, and refined build 
+            properties, determines and sets appripriate usage requirements
+            on those targets.
+        """
+        rproperties = subvariant.build_properties ()
+        xusage_requirements =self.evaluate_requirements(
+            self.usage_requirements_, rproperties, "added")
+        
+        # We generate all dependency properties and add them,
+        # as well as their usage requirements, to result.
+        (r1, r2) = self.generate_dependencies (xusage_requirements.dependency (), rproperties)
+        extra = r1 + r2
+                
+        result = property_set.create (xusage_requirements.non_dependency () + extra)
+
+        # Propagate usage requirements we've got from sources, except
+        # for the <pch-header> and <pch-file> features.
+        #
+        # That feature specifies which pch file to use, and should apply
+        # only to direct dependents. Consider:
+        #
+        #   pch pch1 : ...
+        #   lib lib1 : ..... pch1 ;
+        #   pch pch2 : 
+        #   lib lib2 : pch2 lib1 ;
+        #
+        # Here, lib2 should not get <pch-header> property from pch1.
+        #
+        # Essentially, when those two features are in usage requirements,
+        # they are propagated only to direct dependents. We might need
+        # a more general mechanism, but for now, only those two
+        # features are special.
+        raw = subvariant.sources_usage_requirements().raw()
+        raw = property.change(raw, "<pch-header>", None);
+        raw = property.change(raw, "<pch-file>", None);              
+        result = result.add(property_set.create(raw))
+        
+        return result
+
+    def create_subvariant (self, root_targets, all_targets,
+                           build_request, sources,
+                           rproperties, usage_requirements):
+        """Creates a new subvariant-dg instances for 'targets'
+         - 'root-targets' the virtual targets will be returned to dependents
+         - 'all-targets' all virtual 
+              targets created while building this main target
+         - 'build-request' is property-set instance with
+         requested build properties"""
+         
+        for e in root_targets:
+            e.root (True)
+
+        s = Subvariant (self, build_request, sources,
+                        rproperties, usage_requirements, all_targets)
+        
+        for v in all_targets:
+            if not v.creating_subvariant():
+                v.creating_subvariant(s)
+                
+        return s
+        
+    def construct (self, name, source_targets, properties):
+        """ Constructs the virtual targets for this abstract targets and
+            the dependecy graph. Returns a tuple consisting of the properties and the list of virtual targets.
+            Should be overrided in derived classes.
+        """
+        raise BaseException ("method should be defined in derived classes")
+
+
+class TypedTarget (BasicTarget):
+    import generators
+    
+    def __init__ (self, name, project, type, sources, requirements, default_build, usage_requirements):
+        BasicTarget.__init__ (self, name, project, sources, requirements, default_build, usage_requirements)
+        self.type_ = type
+    
+    def type (self):
+        return self.type_
+            
+    def construct (self, name, source_targets, prop_set):
+        r = generators.construct (self.project_, name, self.type_, 
+            property_set.create (prop_set.raw () + ['<main-target-type>' + self.type_]),
+            source_targets)
+
+        if not r:
+            print "warning: Unable to construct '%s'" % self.full_name ()
+
+            # Are there any top-level generators for this type/property set.
+            if not generators.find_viable_generators (self.type_, prop_set):
+                print "error: no generators were found for type '$(self.type)'"
+                print "error: and the requested properties"
+                print "error: make sure you've configured the needed tools"
+                print "See http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
+                
+                print "To debug this problem, try the --debug-generators option."
+                sys.exit(1)
+        
+        return r
+
Added: trunk/tools/build/v2/build/toolset.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/toolset.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,402 @@
+# Status: being ported by Vladimir Prus
+# Base revision:  40958
+#
+# Copyright 2003 Dave Abrahams 
+# Copyright 2005 Rene Rivera 
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+""" Support for toolset definition.
+"""
+
+import feature, property, generators
+from b2.util.utility import *
+from b2.util import set
+
+__re_split_last_segment = re.compile (r'^(.+)\.([^\.])*')
+__re_two_ampersands = re.compile ('(&&)')
+__re_first_segment = re.compile ('([^.]*).*')
+__re_first_group = re.compile (r'[^.]*\.(.*)')
+
+# Flag is a mechanism to set a value 
+# A single toolset flag. Specifies that when certain
+# properties are in build property set, certain values
+# should be appended to some variable.
+#
+# A flag applies to a specific action in specific module.
+# The list of all flags for a module is stored, and each
+# flag further contains the name of the rule it applies
+# for, 
+class Flag:
+
+    def __init__(self, variable_name, values, condition, rule = None):
+        self.variable_name = variable_name
+        self.values = values
+        self.condition = condition        
+        self.rule = rule
+
+    def __str__(self):
+        return("Flag(" + str(self.variable_name) + ", " + str(self.values) +\
+               ", " + str(self.condition) + ", " + str(self.rule) + ")")
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __module_flags, __flags, __stv
+    
+    # Mapping from module name to a list of all flags that apply
+    # to either that module directly, or to any rule in that module.
+    # Each element of the list is Flag instance.
+    # So, for module named xxx this might contain flags for 'xxx',
+    # for 'xxx.compile', for 'xxx.compile.c++', etc.
+    __module_flags = {}
+
+    # Mapping from specific rule or module name to a list of Flag instances
+    # that apply to that name.
+    # Say, it might contain flags for 'xxx.compile.c++'. If there are
+    # entries for module name 'xxx', they are flags for 'xxx' itself,
+    # not including any rules in that module.
+    __flags = {}
+    
+    # A cache for varaible settings. The key is generated from the rule name and the properties.
+    __stv = {}
+    
+reset ()
+
+# FIXME: --ignore-toolset-requirements
+# FIXME: using
+    
+def normalize_condition (property_sets):
+    """ Expands subfeatures in each property set.
+        e.g
+            <toolset>gcc-3.2
+        will be converted to
+        <toolset>gcc/<toolset-version>3.2
+
+        TODO: does this one belong here or in feature?
+    """
+    result = []
+    for p in property_sets:
+        split = feature.split (p)
+        expanded = feature.expand_subfeatures (split)
+        result.append ('/'.join (expanded))
+
+    return result
+
+# FIXME push-checking-for-flags-module ....
+# FIXME: investigate existing uses of 'hack-hack' parameter
+# in jam code.
+
+def flags (rule_or_module, variable_name, condition, values = []):
+    """ Specifies the flags (variables) that must be set on targets under certain
+        conditions, described by arguments.
+        rule_or_module:   If contains dot, should be a rule name.
+                          The flags will be applied when that rule is
+                          used to set up build actions.
+                          
+                          If does not contain dot, should be a module name.
+                          The flags will be applied for all rules in that
+                          module.
+                          If module for rule is different from the calling
+                          module, an error is issued.
+
+         variable_name:   Variable that should be set on target
+         
+         condition        A condition when this flag should be applied.
+                          Should be set of property sets. If one of
+                          those property sets is contained in build
+                          properties, the flag will be used.
+                          Implied values are not allowed:
+                          "<toolset>gcc" should be used, not just
+                          "gcc". Subfeatures, like in "<toolset>gcc-3.2"
+                          are allowed. If left empty, the flag will
+                          always used.
+                          
+                          Propery sets may use value-less properties 
+                          ('<a>'  vs. '<a>value') to match absent 
+                          properties. This allows to separately match
+                          
+                             <architecture>/<address-model>64
+                             <architecture>ia64/<address-model>
+                          
+                          Where both features are optional. Without this
+                          syntax we'd be forced to define "default" value.
+
+         values:          The value to add to variable. If <feature>
+                          is specified, then the value of 'feature' 
+                          will be added.
+    """
+    if condition and not replace_grist (condition, ''):
+        # We have condition in the form '<feature>', that is, without
+        # value. That's a previous syntax:
+        #
+        #   flags gcc.link RPATH <dll-path> ;
+        # for compatibility, convert it to
+        #   flags gcc.link RPATH : <dll-path> ;                
+        values = [ condition ]
+        condition = None
+    
+    if condition:
+        property.validate_property_sets (condition)
+        condition = normalize_condition ([condition])
+    
+    __add_flag (rule_or_module, variable_name, condition, values)
+
+def set_target_variables (manager, rule_or_module, targets, properties):
+    """
+    """
+    key = rule_or_module + '.' + str (properties)
+    settings = __stv.get (key, None)
+    if not settings:
+        settings = __set_target_variables_aux  (manager, rule_or_module, properties)
+
+        __stv [key] = settings
+        
+    if settings:
+        for s in settings:
+            for target in targets:
+                manager.engine ().set_target_variable (target, s [0], s[1], True)
+
+def find_property_subset (property_sets, properties):
+    """Returns the first element of 'property-sets' which is a subset of
+    'properties', or an empty list if no such element exists."""
+    
+    prop_keys = get_grist(properties)
+
+    for s in property_sets:
+        # Handle value-less properties like '<architecture>' (compare with 
+        # '<architecture>x86').
+
+        set = feature.split(s)
+
+        # Find the set of features that
+        # - have no property specified in required property set 
+        # - are omitted in build property set
+        default_props = []
+        for i in set:       
+            # If $(i) is a value-less property it should match default 
+            # value of an optional property. See the first line in the 
+            # example below:
+            #
+            #  property set     properties     result
+            # <a> <b>foo      <b>foo           match
+            # <a> <b>foo      <a>foo <b>foo    no match
+            # <a>foo <b>foo   <b>foo           no match
+            # <a>foo <b>foo   <a>foo <b>foo    match
+            if not (get_value(i) or get_grist(i) in prop_keys):
+                default_props.append(i)
+
+        # FIXME: can this be expressed in a more pythonic way?
+        has_all = 1
+        for i in set:
+            if i not in (properties + default_props):
+                has_all = 0
+                break
+        if has_all:
+            return s
+
+    return None
+    
+
+def register (toolset):
+    """ Registers a new toolset.
+    """
+    feature.extend('toolset', [toolset])
+
+def inherit_generators (toolset, properties, base, generators_to_ignore = []):
+    if not properties:
+        properties = [replace_grist (toolset, '<toolset>')]
+        
+    base_generators = generators.generators_for_toolset(base)
+    
+    for g in base_generators:
+        id = g.id()
+        
+        if not id in generators_to_ignore:
+            # Some generator names have multiple periods in their name, so
+            # $(id:B=$(toolset)) doesn't generate the right new_id name.
+            # e.g. if id = gcc.compile.c++, $(id:B=darwin) = darwin.c++,
+            # which is not what we want. Manually parse the base and suffix
+            # (if there's a better way to do this, I'd love to see it.)
+            # See also register in module generators.
+            (base, suffix) = split_action_id(id)
+
+            new_id = toolset + '.' + suffix
+
+            generators.register(g.clone(new_id, properties))
+
+def inherit_flags(toolset, base, prohibited_properties = []):
+    """Brings all flag definitions from the 'base' toolset into the 'toolset'
+    toolset. Flag definitions whose conditions make use of properties in
+    'prohibited-properties' are ignored. Don't confuse property and feature, for
+    example <debug-symbols>on and <debug-symbols>off, so blocking one of them does
+    not block the other one.
+    
+    The flag conditions are not altered at all, so if a condition includes a name,
+    or version of a base toolset, it won't ever match the inheriting toolset. When
+    such flag settings must be inherited, define a rule in base toolset module and
+    call it as needed."""
+    for f in __module_flags.get(base, []):
+        
+        if not f.condition or set.difference(f.condition, prohibited_properties):
+            match = __re_first_group.match(f.rule)
+            rule_ = None
+            if match:
+                rule_ = match.group(1)
+
+            new_rule_or_module = ''
+
+            if rule_:
+                new_rule_or_module = toolset + '.' + rule_
+            else:
+                new_rule_or_module = toolset
+
+            __add_flag (new_rule_or_module, f.variable_name, f.condition, f.values)
+
+def inherit_rules (toolset, base):
+    pass
+    # FIXME: do something about this.
+#    base_generators = generators.generators_for_toolset (base)
+
+#    import action
+
+#    ids = []
+#    for g in base_generators:
+#        (old_toolset, id) = split_action_id (g.id ())
+#        ids.append (id) ;
+
+#    new_actions = []
+
+#    engine = get_manager().engine()
+    # FIXME: do this!
+#    for action in engine.action.values():
+#        pass
+#        (old_toolset, id) = split_action_id(action.action_name)
+#    
+#        if old_toolset == base:
+#            new_actions.append ((id, value [0], value [1]))
+#
+#    for a in new_actions:
+#        action.register (toolset + '.' + a [0], a [1], a [2])
+        
+    # TODO: how to deal with this?
+#       IMPORT $(base) : $(rules) : $(toolset) : $(rules) : localized ;
+#       # Import the rules to the global scope
+#       IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ;
+#   }
+#   
+
+######################################################################################
+# Private functions
+
+def __set_target_variables_aux (manager, rule_or_module, properties):
+    """ Given a rule name and a property set, returns a list of tuples of
+        variables names and values, which must be set on targets for that
+        rule/properties combination. 
+    """
+    result = []
+
+    for f in __flags.get(rule_or_module, []):
+           
+        if not f.condition or find_property_subset (f.condition, properties):
+            processed = []
+            for v in f.values:
+                # The value might be <feature-name> so needs special
+                # treatment.
+                processed += __handle_flag_value (manager, v, properties)
+
+            for r in processed:
+                result.append ((f.variable_name, r))
+    
+    # strip away last dot separated part and recurse.
+    next = __re_split_last_segment.match(rule_or_module)
+    
+    if next:
+        result.extend(__set_target_variables_aux(
+            manager, next.group(1), properties))
+
+    return result
+
+def __handle_flag_value (manager, value, properties):
+    result = []
+    
+    if get_grist (value):
+        matches = property.select (value, properties)
+        for p in matches:
+            att = feature.attributes (get_grist (p))
+            
+            ungristed = replace_grist (p, '')
+
+            if 'dependency' in att:
+                # the value of a dependency feature is a target
+                # and must be actualized
+                # FIXME: verify that 'find' actually works, ick!
+                result.append (manager.targets ().find (ungristed).actualize ())
+
+            elif 'path' in att or 'free' in att:
+                values = []
+                
+                # Treat features with && in the value
+                # specially -- each &&-separated element is considered
+                # separate value. This is needed to handle searched
+                # libraries, which must be in specific order.
+                if not __re_two_ampersands.search (ungristed):
+                    values.append (ungristed)
+
+                else:
+                    values.extend(value.split ('&&'))
+
+                result.extend(values)
+            else:
+                result.append (ungristed)
+    else:
+        result.append (value)
+
+    return result
+
+def __add_flag (rule_or_module, variable_name, condition, values):
+    """ Adds a new flag setting with the specified values.
+        Does no checking.
+    """
+    f = Flag(variable_name, values, condition, rule_or_module)
+    
+    # Grab the name of the module
+    m = __re_first_segment.match (rule_or_module)
+    assert m
+    module = m.group(1)
+
+    __module_flags.setdefault(m, []).append(f)
+    __flags.setdefault(rule_or_module, []).append(f)
+
+def requirements():
+    """Return the list of global 'toolset requirements'.
+    Those requirements will be automatically added to the requirements of any main target."""
+    return __requirements
+
+def add_requirements(requirements):
+    """Adds elements to the list of global 'toolset requirements'. The requirements
+    will be automatically added to the requirements for all main targets, as if
+    they were specified literally. For best results, all requirements added should
+    be conditional or indirect conditional."""
+    
+    # FIXME:
+    #if ! $(.ignore-requirements)
+    #{
+    __requirements.extend(requirements)
+    #}
+         
+# Make toolset 'toolset', defined in a module of the same name,
+# inherit from 'base'
+# 1. The 'init' rule from 'base' is imported into 'toolset' with full
+#    name. Another 'init' is called, which forwards to the base one.
+# 2. All generators from 'base' are cloned. The ids are adjusted and 
+#    <toolset> property in requires is adjusted too
+# 3. All flags are inherited
+# 4. All rules are imported.
+def inherit(toolset, base):
+    get_manager().projects().load_module(base, []);
+
+    inherit_generators(toolset, [], base)
+    inherit_flags(toolset, base)
+    inherit_rules(toolset, base)
Added: trunk/tools/build/v2/build/type.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/type.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,292 @@
+# Status: ported.
+# Base revision: 45462.
+
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+
+
+import re
+import os
+import os.path
+from b2.util.utility import replace_grist, os_name
+from b2.exceptions import *
+from b2.build import feature, property, scanner
+
+__re_hyphen = re.compile ('-')
+
+def __register_features ():
+    """ Register features need by this module.
+    """
+    # The feature is optional so that it is never implicitly added.
+    # It's used only for internal purposes, and in all cases we
+    # want to explicitly use it.
+    feature.feature ('target-type', [], ['composite', 'optional'])
+    feature.feature ('main-target-type', [], ['optional', 'incidental'])
+    feature.feature ('base-target-type', [], ['composite', 'optional', 'free'])
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+        Note that this must be called _after_ resetting the module 'feature'.
+    """    
+    global __prefixes_suffixes, __suffixes_to_types, __types, __rule_names_to_types, __target_suffixes_cache
+    
+    __register_features ()
+
+    # Stores suffixes for generated targets.
+    __prefixes_suffixes = [property.PropertyMap(), property.PropertyMap()]
+    
+    # Maps suffixes to types
+    __suffixes_to_types = {}
+    
+    # A map with all the registered types, indexed by the type name
+    # Each entry is a dictionary with following values:
+    # 'base': the name of base type or None if type has no base
+    # 'derived': a list of names of type which derive from this one
+    # 'scanner': the scanner class registered for this type, if any
+    __types = {}
+
+    # Caches suffixes for targets with certain properties.
+    __target_suffixes_cache = {}
+    
+reset ()
+
+
+def register (type, suffixes = [], base_type = None):
+    """ Registers a target type, possibly derived from a 'base-type'. 
+        If 'suffixes' are provided, they list all the suffixes that mean a file is of 'type'.
+        Also, the first element gives the suffix to be used when constructing and object of
+        'type'.
+        type: a string
+        suffixes: None or a sequence of strings
+        base_type: None or a string
+    """
+    # Type names cannot contain hyphens, because when used as
+    # feature-values they will be interpreted as composite features
+    # which need to be decomposed.
+    if __re_hyphen.search (type):
+        raise BaseException ('type name "%s" contains a hyphen' % type)
+    
+    if __types.has_key (type):
+        raise BaseException ('Type "%s" is already registered.' % type)
+
+    entry = {}
+    entry ['base'] = base_type
+    entry ['derived'] = []
+    entry ['scanner'] = None
+    __types [type] = entry
+    
+    if base_type:
+        __types [base_type]['derived'].append (type)
+
+    if len (suffixes) > 0:
+        # Generated targets of 'type' will use the first of 'suffixes'
+        # (this may be overriden)            
+        set_generated_target_suffix (type, [], suffixes [0])
+        
+        # Specify mapping from suffixes to type
+        register_suffixes (suffixes, type)
+    
+    feature.extend('target-type', [type])
+    feature.extend('main-target-type', [type])
+    feature.extend('base-target-type', [type])
+
+    if base_type:
+        feature.compose ('<target-type>' + type, replace_grist (base_type, '<base-target-type>'))
+        feature.compose ('<base-target-type>' + type, '<base-target-type>' + base_type)
+
+    # FIXME: resolving recursive dependency.
+    from b2.manager import get_manager
+    get_manager().projects().project_rules().add_rule_for_type(type)
+
+def register_suffixes (suffixes, type):
+    """ Specifies that targets with suffix from 'suffixes' have the type 'type'. 
+        If a different type is already specified for any of syffixes, issues an error.
+    """
+    for s in suffixes:
+        if __suffixes_to_types.has_key (s):
+            old_type = __suffixes_to_types [s]
+            if old_type != type:
+                raise BaseException ('Attempting to specify type for suffix "%s"\nOld type: "%s", New type "%s"' % (s, old_type, type))
+        else:
+            __suffixes_to_types [s] = type
+
+def registered (type):
+    """ Returns true iff type has been registered.
+    """
+    return __types.has_key (type)
+
+def validate (type):
+    """ Issues an error if 'type' is unknown.
+    """
+    if not registered (type):
+        raise BaseException ("Unknown target type '%s'" % type)
+
+def set_scanner (type, scanner):
+    """ Sets a scanner class that will be used for this 'type'.
+    """
+    validate (type)
+    __types [type]['scanner'] = scanner
+
+def get_scanner (type, prop_set):
+    """ Returns a scanner instance appropriate to 'type' and 'property_set'.
+    """
+    if registered (type):
+        scanner_type = __types [type]['scanner']
+        if scanner_type:
+            return scanner.get (scanner_type, prop_set.raw ())
+            pass
+            
+    return None
+
+def all_bases (type):
+    """ Returns type and all of its bases, in the order of their distance from type.
+    """
+    result = []
+    while type:
+        result.append (type)
+        type = __types [type]['base']
+
+    return result
+
+def all_derived (type):
+    """ Returns type and all classes that derive from it, in the order of their distance from type.
+    """
+    result = [type]
+    for d in __types [type]['derived']:
+        result.extend (all_derived (d))
+
+    return result
+
+def is_derived (type, base):
+    """ Returns true if 'type' is 'base' or has 'base' as its direct or indirect base.
+    """
+    # TODO: this isn't very efficient, especially for bases close to type
+    if base in all_bases (type):
+        return True
+    else: 
+        return False
+
+def is_subtype (type, base):
+    """ Same as is_derived. Should be removed.
+    """
+    # TODO: remove this method
+    return is_derived (type, base)
+
+def set_generated_target_suffix (type, properties, suffix):
+    """ Sets a target suffix that should be used when generating target 
+        of 'type' with the specified properties. Can be called with
+        empty properties if no suffix for 'type' was specified yet.
+        This does not automatically specify that files 'suffix' have
+        'type' --- two different types can use the same suffix for
+        generating, but only one type should be auto-detected for
+        a file with that suffix. User should explicitly specify which
+        one.
+
+        The 'suffix' parameter can be empty string ("") to indicate that
+        no suffix should be used.
+    """
+    set_generated_target_ps(1, type, properties, suffix)
+
+
+    
+def change_generated_target_suffix (type, properties, suffix):
+    """ Change the suffix previously registered for this type/properties 
+        combination. If suffix is not yet specified, sets it.
+    """
+    change_generated_target_ps(1, type, properties, suffix)
+
+def generated_target_suffix(type, properties):
+    return generated_target_ps(1, type, properties)
+
+# Sets a target prefix that should be used when generating targets of 'type'
+# with the specified properties. Can be called with empty properties if no
+# prefix for 'type' has been specified yet.
+#
+# The 'prefix' parameter can be empty string ("") to indicate that no prefix
+# should be used.
+#
+# Usage example: library names use the "lib" prefix on unix.
+def set_generated_target_prefix(type, properties, prefix):
+    set_generated_target_ps(0, type, properties, prefix)
+
+# Change the prefix previously registered for this type/properties combination.
+# If prefix is not yet specified, sets it.
+def change_generated_target_prefix(type, properties, prefix):
+    change_generated_target_ps(0, type, properties, prefix)
+
+def generated_target_prefix(type, properties):
+    return generated_target_ps(0, type, properties)
+
+def set_generated_target_ps(is_suffix, type, properties, val):
+    properties.append ('<target-type>' + type)
+    __prefixes_suffixes[is_suffix].insert (properties, val)
+
+def change_generated_target_ps(is_suffix, type, properties, val):
+    properties.append ('<target-type>' + type)
+    prev = __prefixes_suffixes[is_suffix].find_replace(properties, val)
+    if not prev:
+        set_generated_target_ps(is_suffix, type, properties, val)
+
+# Returns either prefix or suffix (as indicated by 'is_suffix') that should be used
+# when generating a target of 'type' with the specified properties.
+# If no prefix/suffix is specified for 'type', returns prefix/suffix for
+# base type, if any.
+def generated_target_ps_real(is_suffix, type, properties):
+
+    result = ''
+    found = False
+    while type and not found:
+        result = __prefixes_suffixes[is_suffix].find (['<target-type>' + type] + properties)
+
+        # Note that if the string is empty (""), but not null, we consider
+        # suffix found.  Setting prefix or suffix to empty string is fine.
+        if result:
+            found = True
+
+        type = __types [type]['base']
+
+    if not result:
+        result = ''
+    return result
+
+def generated_target_ps(is_suffix, type, prop_set):
+    """ Returns suffix that should be used when generating target of 'type',
+        with the specified properties. If not suffix were specified for
+        'type', returns suffix for base type, if any.
+    """
+    key = str(is_suffix) + type + str(prop_set)
+    v = __target_suffixes_cache.get (key, None)
+
+    if not v:
+        v = generated_target_ps_real(is_suffix, type, prop_set.raw())
+        __target_suffixes_cache [key] = v
+
+    return v
+
+def type(filename):
+    """ Returns file type given it's name. If there are several dots in filename,
+        tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and 
+        "so"  will be tried.
+    """
+    while 1:
+        filename, suffix = os.path.splitext (filename)
+        if not suffix: return None
+        suffix = suffix[1:]
+        
+        if __suffixes_to_types.has_key(suffix):
+            return __suffixes_to_types[suffix]
+
+# NOTE: moved from tools/types/register
+def register_type (type, suffixes, base_type = None, os = []):
+    """ Register the given type on the specified OSes, or on remaining OSes
+        if os is not specified.  This rule is injected into each of the type
+        modules for the sake of convenience.
+    """
+    if registered (type):
+        return
+
+    if not os or os_name () in os:
+        register (type, suffixes, base_type)
Added: trunk/tools/build/v2/build/virtual_target.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build/virtual_target.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,1051 @@
+# Status: being ported by Vladimir Prus
+# Essentially ported, minor fixme remain.
+# Base revision: 40480
+# 
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+#  Implements virtual targets, which correspond to actual files created during
+#  build, but are not yet targets in Jam sense. They are needed, for example,
+#  when searching for possible transormation sequences, when it's not known
+#  if particular target should be created at all.
+#
+#
+#                       +--------------------------+
+#                       | VirtualTarget           |
+#                       +==========================+
+#                       | actualize                |
+#                       +--------------------------+
+#                       | actualize_action() = 0   |
+#                       | actualize_location() = 0 |
+#                       +----------------+---------+
+#                                        |
+#                                        ^
+#                                       / \
+#                                      +-+-+
+#                                        |
+#    +---------------------+     +-------+--------------+
+#    | Action              |     | AbstractFileTarget   |
+#    +=====================|   * +======================+
+#    | action_name         |  +--+ action               |
+#    | properties          |  |  +----------------------+
+#    +---------------------+--+  | actualize_action()   |
+#    | actualize()         |0..1 +-----------+----------+
+#    | path()              |                 |
+#    | adjust_properties() | sources         |
+#    | actualize_sources() | targets         |
+#    +------+--------------+                 ^
+#           |                               / \
+#           ^                              +-+-+
+#          / \                               |
+#         +-+-+                +-------------+-------------+
+#           |                  |                           |
+#           |           +------+---------------+  +--------+-------------+
+#           |           | FileTarget           |  | SearchedLibTarget    |
+#           |           +======================+  +======================+
+#           |           | actualize-location() |  | actualize-location() |
+#           |           +----------------------+  +----------------------+
+#           |
+#         +-+------------------------------+
+#         |                                |
+#    +----+----------------+     +---------+-----------+
+#    | CompileAction       |     | LinkAction          |
+#    +=====================+     +=====================+
+#    | adjust_properties() |     | adjust_properties() |
+#    +---------------------+     | actualize_sources() |
+#                                +---------------------+
+#
+# The 'CompileAction' and 'LinkAction' classes are defined not here,
+# but in builtin.jam modules. They are shown in the diagram to give
+# the big picture.
+
+import re
+import os.path
+import string
+
+from b2.util import path, utility, set
+from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, get_value
+from b2.util.sequence import unique
+from b2.tools import common
+from b2.exceptions import *
+import b2.build.type
+import type
+
+__re_starts_with_at = re.compile ('^@(.*)')
+
+class VirtualTargetRegistry:
+    def __init__ (self, manager):
+        self.manager_ = manager
+        
+        # A cache for FileTargets
+        self.files_ = {}
+
+        # A cache for targets.
+        self.cache_ = {}
+
+        # A map of actual names to virtual targets.
+        # Used to make sure we don't associate same
+        # actual target to two virtual targets.
+        self.actual_ = {}
+
+        self.recent_targets_ = []
+        
+        # All targets ever registed
+        self.all_targets_ = []
+
+        self.next_id_ = 0
+        
+    def register (self, target):
+        """ Registers a new virtual target. Checks if there's already registered target, with the same
+            name, type, project and subvariant properties, and also with the same sources
+            and equal action. If such target is found it is retured and 'target' is not registered.
+            Otherwise, 'target' is registered and returned.
+        """
+        signature = target.path() + "-" + target.name()
+
+        result = None
+        if not self.cache_.has_key (signature):
+            self.cache_ [signature] = []
+
+        for t in self.cache_ [signature]:
+            a1 = t.action ()
+            a2 = target.action ()
+
+            # TODO: why are we checking for not result?
+            if not result:
+                if not a1 and not a2:
+                    result = t
+                else:
+                    if a1 and a2 and a1.action_name () == a2.action_name () and a1.sources () == a2.sources ():
+                        ps1 = a1.properties ()
+                        ps2 = a2.properties ()
+                        p1 = ps1.base () + ps1.free () + ps1.dependency ()
+                        p2 = ps2.base () + ps2.free () + ps2.dependency ()
+                        if p1 == p2:
+                            result = t
+        
+        if not result:
+            self.cache_ [signature].append (target)
+            result = target
+
+        # TODO: Don't append if we found pre-existing target?
+        self.recent_targets_.append(result)
+        self.all_targets_.append(result)
+
+        result.set_id(self.next_id_)
+        self.next_id_ = self.next_id_+1
+    
+        return result
+
+    def from_file (self, file, file_location, project):
+        """ Creates a virtual target with appropriate name and type from 'file'.
+            If a target with that name in that project was already created, returns that already
+            created target.
+            TODO: more correct way would be to compute path to the file, based on name and source location
+            for the project, and use that path to determine if the target was already created.
+            TODO: passing project with all virtual targets starts to be annoying.
+        """
+        # Check if we've created a target corresponding to this file.
+        path = os.path.join(os.getcwd(), file_location, file)
+        path = os.path.normpath(path)
+
+        if self.files_.has_key (path):
+            return self.files_ [path]
+
+        file_type = type.type (file)
+
+        result = FileTarget (file, False, file_type, project,
+                             None, file_location)       
+        self.files_ [path] = result
+        
+        result.set_id(self.next_id_)
+        self.next_id_ = self.next_id_+1
+        
+        return result
+
+    def recent_targets(self):
+        """Each target returned by 'register' is added to a list of
+        'recent-target', returned by this function. So, this allows
+        us to find all targets created when building a given main
+        target, even if the target."""
+
+        return self.recent_targets_
+
+    def clear_recent_targets(self):
+        self.recent_targets_ = []
+
+    def all_targets(self):
+        # Returns all virtual targets ever created
+        return self.all_targets_
+
+    # Returns all targets from 'targets' with types
+    # equal to 'type' or derived from it.
+    def select_by_type(self, type, targets):
+        return [t for t in targets if type.is_sybtype(t.type(), type)]
+
+    def register_actual_name (self, actual_name, virtual_target):
+        if self.actual_.has_key (actual_name):
+            cs1 = self.actual_ [actual_name].creating_subvariant ()
+            cs2 = virtual_target.creating_subvariant ()
+            cmt1 = cs1.main_target ()
+            cmt2 = cs2.main_target ()            
+            
+            action1 = self.actual_ [actual_name].action ()
+            action2 = virtual_target.action ()
+            
+            properties_added = []
+            properties_removed = []
+            if action1 and action2:
+                p1 = action1.properties ()
+                p1 = p1.raw ()
+                p2 = action2.properties ()
+                p2 = p2.raw ()
+                
+                properties_removed = set.difference (p1, p2)
+                if not properties_removed: properties_removed = "none"
+
+                properties_added = set.difference (p2, p1)
+                if not properties_added: properties_added = "none"
+
+            # FIXME: Revive printing of real location.
+            raise BaseException ("Duplicate name of actual target: '%s'\n" 
+              "previous virtual target '%s'\n"
+              "created from '%s'\n"
+              "another virtual target '%s'\n"
+              "created from '%s'\n"
+              "added properties: '%s'\n"
+              "removed properties: '%s'\n" % (actual_name,
+                  self.actual_ [actual_name], "loc", #cmt1.location (),
+                                              virtual_target, 
+                                              "loc", #cmt2.location (),
+                                              properties_added, properties_removed))
+
+        else:
+            self.actual_ [actual_name] = virtual_target
+    
+
+    def add_suffix (self, specified_name, file_type, prop_set):
+        """ Appends the suffix appropriate to 'type/property_set' combination
+            to the specified name and returns the result.
+        """
+        suffix = type.generated_target_suffix (file_type, prop_set)
+
+        if suffix:
+            return specified_name + '.' + suffix
+
+        else:
+            return specified_name
+    
+class VirtualTarget:
+    """ Potential target. It can be converted into jam target and used in
+        building, if needed. However, it can be also dropped, which allows
+        to search for different transformation and select only one.
+        name:    name of this target.
+        project: project to which this target belongs.
+    """
+    def __init__ (self, name, project):
+        self.name_ = name
+        self.project_ = project
+        self.dependencies_ = []
+        
+        # Caches if dapendencies for scanners have already been set.
+        self.made_ = {}
+
+    def manager(self):
+        return self.project_.manager()
+
+    def virtual_targets(self):
+        return self.manager().virtual_targets()
+
+    def name (self):
+        """ Name of this target.
+        """
+        return self.name_
+
+    def project (self):
+        """ Project of this target.
+        """
+        return self.project_
+
+    def set_id(self, id):
+        self.id_ = id
+
+    def __hash__(self):
+        return self.id_
+
+    def __cmp__(self, other):
+        return self.id_ - other.id_
+
+    def depends (self, d):
+        """ Adds additional instances of 'VirtualTarget' that this
+            one depends on.
+        """
+        self.dependencies_ = unique (self.dependencies_ + d).sort ()
+
+    def dependencies (self):
+        return self.dependencies_
+
+    def actualize (self, scanner = None):
+        """ Generates all the actual targets and sets up build actions for
+            this target.
+            
+            If 'scanner' is specified, creates an additional target
+            with the same location as actual target, which will depend on the
+            actual target and be associated with 'scanner'. That additional
+            target is returned. See the docs (#dependency_scanning) for rationale.
+            Target must correspond to a file if 'scanner' is specified.
+            
+            If scanner is not specified, then actual target is returned.
+        """
+        actual_name = self.actualize_no_scanner ()
+
+        if not scanner:
+            return actual_name
+
+        else:
+            # Add the scanner instance to the grist for name.
+            g = '-'.join ([ungrist(get_grist(actual_name)), str(id(scanner))])
+
+            name = replace_grist (actual_name, '<' + g + '>')
+
+            if not self.made_.has_key (name):
+                self.made_ [name] = True
+
+                self.project_.manager ().engine ().add_dependency (name, actual_name)
+
+                self.actualize_location (name)
+
+                self.project_.manager ().scanners ().install (scanner, name, str (self))
+
+            return name
+
+# private: (overridables)
+
+    def actualize_action (self, target):
+        """ Sets up build actions for 'target'. Should call appropriate rules
+            and set target variables.
+        """
+        raise BaseException ("method should be defined in derived classes")
+
+    def actualize_location (self, target):
+        """ Sets up variables on 'target' which specify its location.
+        """
+        raise BaseException ("method should be defined in derived classes")
+    
+    def path (self):
+        """ If the target is generated one, returns the path where it will be
+            generated. Otherwise, returns empty list.
+        """
+        raise BaseException ("method should be defined in derived classes")
+    
+    def actual_name (self):
+        """ Return that actual target name that should be used
+            (for the case where no scanner is involved)
+        """
+        raise BaseException ("method should be defined in derived classes")
+
+
+class AbstractFileTarget (VirtualTarget):
+    """ Target which correspond to a file. The exact mapping for file
+        is not yet specified in this class. (TODO: Actually, the class name
+        could be better...)
+        
+        May be a source file (when no action is specified), or
+        derived file (otherwise).
+        
+        The target's grist is concatenation of project's location,
+        properties of action (for derived files), and, optionally,
+        value identifying the main target.
+        
+        exact:  If non-empty, the name is exactly the name
+                created file should have. Otherwise, the '__init__'
+                method will add suffix obtained from 'type' by
+                calling 'type.generated-target-suffix'.
+                  
+        type:   optional type of this target.
+    """
+    def __init__ (self, name, exact, type, project, action = None):
+        VirtualTarget.__init__ (self, name, project)
+            
+        self.type_ = type
+
+        self.action_ = action
+        self.exact_ = exact
+        
+        if action:
+            action.add_targets ([self])
+ 
+            if self.type and not exact:
+                self.__adjust_name (name)
+
+        
+        self.actual_name_ = None
+        self.path_ = None
+        self.intermediate_ = False
+        self.creating_subvariant_ = None
+        
+        # True if this is a root target.
+        self.root_ = False
+
+    def type (self):
+        return self.type_
+
+    def set_path (self, path):
+        """ Sets the path. When generating target name, it will override any path
+            computation from properties.
+        """
+        self.path_ = path
+
+    def action (self):
+        """ Returns the action.
+        """
+        return self.action_
+
+    def root (self, set = None):
+        """ Sets/gets the 'root' flag. Target is root is it directly correspods to some
+            variant of a main target.
+        """
+        if set:
+            self.root_ = True
+        return self.root_
+
+    def creating_subvariant (self, s = None):
+        """ Gets or sets the subvariant which created this target. Subvariant
+        is set when target is brought into existance, and is never changed
+        after that. In particual, if target is shared by subvariant, only 
+        the first is stored.
+        s:  If specified, specified the value to set,
+                which should be instance of 'subvariant' class.
+        """
+        if s and not self.creating_subvariant ():
+            if self.creating_subvariant ():
+                raise BaseException ("Attempt to change 'dg'")
+
+            else:
+                self.creating_subvariant_ = s
+
+        return self.creating_subvariant_
+
+    def actualize_action (self, target):
+        if self.action_:
+            self.action_.actualize ()
+
+    # Return a human-readable representation of this target
+    #
+    # If this target has an action, that's:
+    #
+    #    { <action-name>-<self.name>.<self.type> <action-sources>... }
+    #
+    # otherwise, it's:
+    #
+    #    { <self.name>.<self.type> }
+    #
+    def str(self):
+        a = self.action()
+
+        name_dot_type = self.name_ + "." + self.type_
+
+        if a:
+            action_name = a.action_name()
+            ss = [ s.str() for s in a.sources()]
+                       
+            return "{ %s-%s %s}" % (action_name, name_dot_type, str(ss))
+        else:
+            return "{ " + name_dot_type + " }"
+        
+# private:
+
+    def actual_name (self):
+        if not self.actual_name_:
+            self.actual_name_ = '<' + self.grist() + '>' + self.name_
+
+        return self.actual_name_
+
+    def grist (self):
+        """Helper to 'actual_name', above. Compute unique prefix used to distinguish
+            this target from other targets with the same name which create different
+            file.
+        """
+        # Depending on target, there may be different approaches to generating
+        # unique prefixes. We'll generate prefixes in the form 
+        # <one letter approach code> <the actual prefix>
+        path = self.path ()
+        
+        if path:
+            # The target will be generated to a known path. Just use the path
+            # for identification, since path is as unique as it can get.
+            return 'p' + path
+
+        else:
+            # File is either source, which will be searched for, or is not a file at
+            # all. Use the location of project for distinguishing.
+            project_location = self.project_.get ('location')
+            path_components = b2.util.path.split(project_location)
+            location_grist = '!'.join (path_components)
+            
+            if self.action_:
+                ps = self.action_.properties ()
+                property_grist = ps.as_path ()
+                # 'property_grist' can be empty when 'ps' is an empty
+                # property set.
+                if property_grist:
+                    location_grist = location_grist + '/' + property_grist
+                        
+            return 'l' + location_grist
+
+    def __adjust_name(self, specified_name):
+        """Given the target name specified in constructor, returns the
+        name which should be really used, by looking at the <tag> properties.
+        The tag properties come in two flavour:
+          - <tag>value, 
+          - <tag>@rule-name
+        In the first case, value is just added to name
+        In the second case, the specified rule is called with specified name,
+        target type and properties and should return the new name.
+        If not <tag> property is specified, or the rule specified by
+        <tag> returns nothing, returns the result of calling 
+        virtual-target.add-suffix"""
+
+        if self.action_:
+            ps = self.action_.properties()
+        else:
+            ps = property_set.empty()
+
+        # FIXME: I'm not sure how this is used, need to check with
+        # Rene to figure out how to implement
+        #~ We add ourselves to the properties so that any tag rule can get
+        #~ more direct information about the target than just that available
+        #~ through the properties. This is useful in implementing
+        #~ name changes based on the sources of the target. For example to
+        #~ make unique names of object files based on the source file.
+        #~ --grafik
+        #ps = property_set.create(ps.raw() + ["<target>%s" % "XXXX"])
+        #ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ;
+        
+        tag = ps.get("<tag>")
+        
+        if tag:
+
+            rule_names = [t[:1] for t in tag if t[0] == '@']
+            if rule_names:
+                if len(tag) > 1:
+                    self.manager_.errors()(
+"""<tag>@rulename is present but is not the only <tag> feature""")
+
+                self.name_ = bjam.call(rule_names[0], specified_name, self.type_, ps)
+            else:
+                self.manager_.errors()(
+"""The value of the <tag> feature must be '@rule-nane'""")
+        
+        # If there's no tag or the tag rule returned nothing.
+        if not tag or not self.name_:
+            self.name_ = add_prefix_and_suffix(specified_name, self.type_, ps)
+
+    def actualize_no_scanner(self):
+        name = self.actual_name()
+
+        # Do anything only on the first invocation
+        if not self.made_:
+            self.made_[name] = True
+
+            if self.action_:  
+                # For non-derived target, we don't care if there
+                # are several virtual targets that refer to the same name.
+                # One case when this is unavoidable is when file name is
+                # main.cpp and two targets have types CPP (for compiling)
+                # and MOCCABLE_CPP (for convertion to H via Qt tools).
+                self.virtual_targets().register_actual_name(name, self)
+
+            for i in self.dependencies_:
+                self.manager_.engine().add_dependency(name, i.actualize())
+
+            self.actualize_location(name)
+            self.actualize_action(name)
+            
+        return name
+
+def add_prefix_and_suffix(specified_name, type, property_set):
+    """Appends the suffix appropriate to 'type/property-set' combination
+    to the specified name and returns the result."""
+
+    suffix = b2.build.type.generated_target_suffix(type, property_set)
+    
+    # Handle suffixes for which no leading dot is desired.  Those are
+    # specified by enclosing them in <...>.  Needed by python so it
+    # can create "_d.so" extensions, for example.
+    if get_grist(suffix):
+        suffix = ungrist(suffix)
+    elif suffix:
+        suffix = "." + suffix
+
+    prefix = b2.build.type.generated_target_prefix(type, property_set)
+
+    if specified_name.startswith(prefix):
+        prefix = ""
+
+    if not prefix:
+        prefix = ""
+    if not suffix:
+        suffix = ""
+    return prefix + specified_name + suffix
+
+
+class FileTarget (AbstractFileTarget):
+    """ File target with explicitly known location.
+
+        The file path is determined as
+           - value passed to the 'set_path' method, if any
+           - for derived files, project's build dir, joined with components
+             that describe action's properties. If the free properties
+             are not equal to the project's reference properties
+             an element with name of main target is added.
+           - for source files, project's source dir
+        
+        The file suffix is
+            - the value passed to the 'suffix' method, if any, or
+            - the suffix which correspond to the target's type.
+    """
+    def __init__ (self, name, exact, type, project, action = None, path=None):
+        AbstractFileTarget.__init__ (self, name, exact, type, project, action)
+
+        self.path_ = path
+
+    def clone_with_different_type(self, new_type):
+        return FileTarget(self.name_, 1, new_type, self.project_,
+                          self.action_, self.path_)
+        
+    def actualize_location (self, target):
+        engine = self.project_.manager_.engine ()
+
+        if self.action_:
+            # This is a derived file.
+            path = self.path ()
+            engine.set_target_variable (target, 'LOCATE', path)
+
+            # Make sure the path exists.
+            engine.add_dependency (target, path)
+            common.mkdir(engine, path)
+
+            # It's possible that the target name includes a directory
+            # too, for example when installing headers. Create that
+            # directory.
+            d = os.path.dirname(get_value(target))
+            if d:
+                d = os.path.join(path, d)
+                engine.add_dependency(target, d)
+                common.mkdir(engine, d)
+
+            # For real file target, we create a fake target that
+            # depends on the real target. This allows to run
+            #
+            #    bjam hello.o
+            #
+            # without trying to guess the name of the real target.            
+            # Note the that target has no directory name, and a special
+            # grist <e>.
+            #
+            # First, that means that "bjam hello.o" will build all
+            # known hello.o targets.
+            # Second, the <e> grist makes sure this target won't be confused
+            # with other targets, for example, if we have subdir 'test'
+            # with target 'test' in it that includes 'test.o' file,
+            # then the target for directory will be just 'test' the target
+            # for test.o will be <ptest/bin/gcc/debug>test.o and the target
+            # we create below will be <e>test.o
+            engine.add_dependency("<e>%s" % get_value(target), target)
+            
+        else:
+            # This is a source file.
+            engine.set_target_variable (target, 'SEARCH', self.project_.get ('source-location'))
+    
+
+    def path (self):
+        """ Returns the directory for this target.
+        """
+        if not self.path_:
+            if self.action_:
+                p = self.action_.properties ()
+                target_path = p.target_path ()
+                
+                if target_path [1] == True:
+                    # Indicates that the path is relative to
+                    # build dir.
+                    target_path = os.path.join (self.project_.build_dir (), target_path [0])
+                                
+                # Store the computed path, so that it's not recomputed
+                # any more
+                self.path_ = target_path
+
+        return self.path_
+
+
+class NotFileTarget(AbstractFileTarget):
+
+    def __init__(self, name, project):
+        AbstractFileTarget.__init__(name, project)
+
+    def path(self):
+        """Returns nothing, to indicate that target path is not known."""
+        return None
+
+    def actualize_location(self, target):
+        bjam.call("NOTFILE", target)
+        bjam.call("ALWAYS", taget)
+
+
+class Action:
+    """ Class which represents an action.
+        Both 'targets' and 'sources' should list instances of 'VirtualTarget'.
+        Action name should name a rule with this prototype
+            rule action_name ( targets + : sources * : properties * )
+        Targets and sources are passed as actual jam targets. The rule may
+        not establish dependency relationship, but should do everything else.
+    """
+    def __init__ (self, manager, sources, action_name, prop_set):
+        self.sources_ = sources
+        self.action_name_ = action_name
+        if not prop_set:
+            prop_set = property_set.empty()
+        self.properties_ = prop_set
+
+        self.manager_ = manager
+        self.engine_ = self.manager_.engine ()
+        self.targets_ = []
+
+        # Indicates whether this has been actualized or not.
+        self.actualized_ = False
+        
+        self.dependency_only_sources_ = []
+        self.actual_sources_ = []
+        
+        
+    def add_targets (self, targets):
+        self.targets_ += targets
+
+    def targets (self):
+        return self.targets_
+
+    def sources (self):
+        return self.sources_
+
+    def action_name (self):
+        return self.action_name_
+
+    def properties (self):
+        return self.properties_
+
+    def actualize (self):
+        """ Generates actual build instructions.
+        """
+        if self.actualized_:
+            return
+            
+        self.actualized_ = True
+
+        ps = self.properties ()
+        properties = self.adjust_properties (ps)
+        actual_targets = []
+        
+        for i in self.targets ():
+            actual_targets.append (i.actualize ())
+
+        self.actualize_sources (self.sources (), properties)
+
+        self.engine_.add_dependency (actual_targets, self.actual_sources_ + self.dependency_only_sources_)
+
+        raw_properties = properties.raw ()
+
+        # FIXME: check the comment below. Was self.action_name_ [1]
+        # Action name can include additional argument to rule, which should not
+        # be passed to 'set-target-variables'
+        # FIXME: breaking circular dependency
+        import toolset
+        toolset.set_target_variables (self.manager_, self.action_name_, actual_targets, raw_properties)
+             
+        engine = self.manager_.engine ()
+        
+        self.manager_.engine ().set_update_action (self.action_name_, actual_targets, self.actual_sources_,
+                                                   properties)
+        
+        # Since we set up creating action here, we also set up
+        # action for cleaning up
+        self.manager_.engine ().set_update_action ('common.Clean', 'clean-all',
+                                                   actual_targets, None)
+
+        return actual_targets
+
+    def actualize_source_type (self, sources, prop_set):
+        """ Helper for 'actualize_sources'.
+            For each passed source, actualizes it with the appropriate scanner.
+            Returns the actualized virtual targets.
+        """
+        result = []
+        for i in sources:
+            scanner = None
+
+# FIXME: what's this?
+#            if isinstance (i, str):
+#                i = self.manager_.get_object (i)
+                
+            if i.type ():
+                scanner = type.get_scanner (i.type (), prop_set)
+
+            result.append (i.actualize (scanner))
+        
+        return result
+    
+    def actualize_sources (self, sources, prop_set):
+        """ Creates actual jam targets for sources. Initializes two member
+            variables:
+            'self.actual_sources_' -- sources which are passed to updating action
+            'self.dependency_only_sources_' -- sources which are made dependencies, but
+            are not used otherwise.
+            
+            New values will be *appended* to the variables. They may be non-empty,
+            if caller wants it.
+        """
+        dependencies = self.properties_.get ('<dependency>')
+                
+        self.dependency_only_sources_ += self.actualize_source_type (dependencies, prop_set)
+        self.actual_sources_ += self.actualize_source_type (sources, prop_set)
+
+        # This is used to help bjam find dependencies in generated headers
+        # in other main targets.
+        # Say:
+        #
+        #   make a.h : ....... ;
+        #   exe hello : hello.cpp : <implicit-dependency>a.h ;
+        #
+        # However, for bjam to find the dependency the generated target must
+        # be actualized (i.e. have the jam target). In the above case,
+        # if we're building just hello ("bjam hello"), 'a.h' won't be
+        # actualized unless we do it here.
+        implicit = self.properties_.get("<implicit-dependency>")
+        for i in implicit:
+            i.actualize()
+
+    def adjust_properties (self, prop_set):
+        """ Determines real properties when trying building with 'properties'.
+            This is last chance to fix properties, for example to adjust includes
+            to get generated headers correctly. Default implementation returns
+            its argument.
+        """
+        return prop_set
+
+
+class NullAction (Action):
+    """ Action class which does nothing --- it produces the targets with
+        specific properties out of nowhere. It's needed to distinguish virtual
+        targets with different properties that are known to exist, and have no 
+        actions which create them.
+    """
+    def __init__ (self, manager, prop_set):
+        Action.__init__ (self, manager, None, None, prop_set)
+        
+    def actualize (self):
+        if not self.actualized_:
+            self.actualized_ = True
+
+            for i in self.targets ():
+                i.actualize ()
+
+class NonScanningAction(Action):
+    """Class which acts exactly like 'action', except that the sources
+    are not scanned for dependencies."""
+
+    def __init__(self, sources, action_name, property_set):
+        #FIXME: should the manager parameter of Action.__init__
+        #be removed? -- Steven Watanabe
+        Action.__init__(b2.manager.get_manager(), sources, action_name, property_set)
+
+    def actualize_source_type(self, sources, property_set):
+        
+        return [x for source in sources for x in i.actualize()]
+
+def traverse (target, include_roots = False, include_sources = False):
+    """ Traverses the dependency graph of 'target' and return all targets that will
+        be created before this one is created. If root of some dependency graph is
+        found during traversal, it's either included or not, dependencing of the
+        value of 'include_roots'. In either case, sources of root are not traversed.
+    """
+    result = []
+    
+    if target.action ():
+        action = target.action ()
+        
+        # This includes 'target' as well
+        result += action.targets ()
+
+        for t in action.sources ():
+
+            # FIXME:
+            # TODO: see comment in Manager.register_object ()
+            #if not isinstance (t, VirtualTarget):
+            #    t = target.project_.manager_.get_object (t)
+                
+            if not t.root ():
+                result += traverse (t, include_roots, include_sources)
+
+            elif include_roots:
+                result.append (t)
+
+    elif include_sources:
+        result.append (target)
+
+    return result
+
+def clone_action (action, new_project, new_action_name, new_properties):
+    """Takes an 'action' instances and creates new instance of it
+    and all produced target. The rule-name and properties are set
+    to 'new-rule-name' and 'new-properties', if those are specified.
+    Returns the cloned action."""
+
+    if not new_action_name:
+        new_action_name = action.action_name()
+
+    if not new_properties:
+        new_properties = action.properties()
+
+    closed_action = action.__class__(action.sources(), new_action_name,
+                                     new_properties)
+                          
+    cloned_targets = []
+    for target in action.targets():
+
+        n = target.name()
+        # Don't modify the name of the produced targets. Strip the directory f
+        cloned_target = FileTarget(n, 1, target.type(), new_project,
+                                   cloned_action)
+
+        d = target.dependencies()
+        if d:
+            cloned_target.depends(d)
+        cloned_target.root(target.root())
+        cloned_target.creating_subvariant(target.creating_subvariant())
+
+        cloned_targets.append(cloned_target)
+
+    return cloned_action
+
+class Subvariant:
+    
+    def __init__ (self, main_target, prop_set, sources, build_properties, sources_usage_requirements, created_targets):
+        """ 
+        main_target:                 The instance of MainTarget class
+        prop_set:                    Properties requested for this target
+        sources:
+        build_properties:            Actually used properties
+        sources_usage_requirements:  Properties propagated from sources
+        created_targets:             Top-level created targets
+        """
+        self.main_target_ = main_target
+        self.properties_ = prop_set
+        self.sources_ = sources
+        self.build_properties_ = build_properties
+        self.sources_usage_requirements_ = sources_usage_requirements
+        self.created_targets_ = created_targets
+
+        self.usage_requirements_ = None
+        
+        # Pre-compose the list of other dependency graphs, on which this one
+        # depends
+        deps = build_properties.get ('<implicit-dependency>')
+        
+        self.other_dg_ = []
+        for d in deps:
+            # FIXME: the property must have the actual object here, not a string.
+            value = replace_grist (d, '')
+            self.other_dg_.append (value.creating_subvariant ())
+
+        self.other_dg_ = unique (self.other_dg_)
+
+        self.implicit_includes_cache_ = {}
+        self.target_directories_ = None
+               
+    def main_target (self):
+        return self.main_target_
+    
+    def created_targets (self):
+        return self.created_targets_
+    
+    def requested_properties (self):
+        return self.properties_
+    
+    def build_properties (self):
+        return self.build_properties_
+    
+    def sources_usage_requirements (self):
+        return self.sources_usage_requirements_
+    
+    def set_usage_requirements (self, usage_requirements):
+        self.usage_requirements_ = usage_requirements
+    
+    def usage_requirements (self):
+        return self.usage_requirements_
+
+    def all_referenced_targets(self):
+        """Returns all targets referenced by this subvariant,
+        either directly or indirectly, and either as sources,
+        or as dependency properties. Targets referred with
+        dependency property are returned a properties, not targets."""
+        
+        # Find directly referenced targets.
+        deps = self.build_properties().dependency()
+        all_targets = self.sources_ + deps
+        
+        # Find other subvariants.
+        r = []
+        for t in all_targets:
+            r.append(t.creating_subvariant)
+        r = unique(r)
+
+        for s in r:
+            if s != self:
+                all_targets.extend(s.all_referenced_targets())
+
+        return all_targets
+
+    def implicit_includes (self, feature, target_type):
+        """ Returns the properties which specify implicit include paths to
+            generated headers. This traverses all targets in this subvariant,
+            and subvariants referred by <implcit-dependecy>properties.
+            For all targets which are of type 'target-type' (or for all targets,
+            if 'target_type' is not specified), the result will contain
+            <$(feature)>path-to-that-target.
+        """
+
+        if not target_type:
+            key = feature
+        else:
+            key = feature + "-" + target_type
+
+            
+        result = self.implicit_includes_cache_.get(key)
+        if not result:
+            target_paths = self.all_target_directories(target_type)
+            target_paths = unique(target_paths)
+            result = ["<%s>%s" % (feature, p) for p in target_paths]
+            self.implicit_includes_cache_[key] = result
+
+        return result
+
+    def all_target_directories(self, target_type = None):
+        # TODO: does not appear to use target_type in deciding
+        # if we've computed this already.
+        if not self.target_directories_:
+            self.target_directories_ = self.compute_target_directories(target_type)
+        return self.target_directories_
+
+    def compute_target_directories(self, target_type=None):
+        result = []
+        for t in self.created_targets():
+            if not target_type or type.is_derived(t.type(), target_type):
+                result.append(t.path())
+
+        for d in self.other_dg_:
+            result.extend(d.all_target_directories(target_type))
+
+        result = unique(result)
+        return result
Added: trunk/tools/build/v2/build_system.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/build_system.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,437 @@
+# Status: being ported by Vladimir Prus.
+
+# Copyright 2003, 2005 Dave Abrahams 
+# Copyright 2006 Rene Rivera 
+# Copyright 2003, 2004, 2005, 2006, 2007 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+from b2.build.engine import Engine
+from b2.manager import Manager
+from b2.util.path import glob
+from b2.build import feature, property_set
+import b2.build.virtual_target
+from b2.build.targets import ProjectTarget
+from b2.util.sequence import unique
+import b2.build.build_request
+from b2.build.errors import ExceptionWithUserContext
+import b2.tools.common
+
+import bjam
+
+import os
+import sys
+
+# FIXME:
+# Returns the location of the build system. The primary use case
+# is building Boost, where it's sometimes needed to get location
+# of other components (like BoostBook files), and it's convenient
+# to use location relatively to Boost.Build path.
+#rule location ( )
+#{
+#    local r = [ modules.binding build-system ] ;
+#    return $(r:P) ;
+#}
+
+# FIXME:
+
+def get_boolean_option(name):
+    match = "--" + name
+    if match in argv:
+        return 1
+    else:
+        return 0
+
+def get_string_option(name):
+    match = "--" + name + "="
+    
+    for arg in argv:
+        if arg.startswith(match):
+            return arg[len(match):]
+    return None
+
+def home_directories():
+    if os.name == "nt":
+        result = set()
+        try:
+            result.add(os.environ['HOMEDRIVE'] + os.environ['HOMEPATH'])
+            result.add(os.environ['HOME'])
+            result.add(os.environ['USERPROFILE'])
+        except KeyError:
+            pass
+        return list(result)
+    else:
+        return [os.environ['HOME']]
+
+ignore_config = 0
+debug_config = 0
+
+def load_config(manager, basename, path):
+    """Unless ignore-config is set, search configuration
+    basename.jam in path and loads it.  The jamfile module
+    for that file will be loaded 'basename'."""
+
+    if not ignore_config:
+        found = glob(path, [basename + ".jam"])
+        if found:
+            found = found[0]
+        if debug_config:
+            print "notice: searching '%s' for '%s.jam'" % (path, basename)
+            if found:
+                print "notice: loading %s.jam from %s" % (basename, found)
+
+        manager.projects().load_standalone(basename, found)
+
+def main():
+
+    global argv
+    argv = bjam.variable("ARGV")
+
+    # FIXME: document this option.
+    if "--profiling" in argv:
+        import cProfile
+        import pstats
+        cProfile.runctx('main_real()', globals(), locals(), "stones.prof")
+        
+        stats = pstats.Stats("stones.prof")
+        stats.strip_dirs()
+        stats.sort_stats('time', 'calls')
+        stats.print_callers(20)
+    else:
+        main_real()
+
+def main_real():
+
+    global ignore_config
+    global debug_config
+    
+    boost_build_path = bjam.variable("BOOST_BUILD_PATH")
+
+    engine = Engine()
+
+    global_build_dir = get_string_option("build-dir")
+    debug_config = get_boolean_option("debug-configuration")
+    
+    manager = Manager(engine, global_build_dir)
+
+    # This module defines types and generator and what not,
+    # and depends on manager's existence
+    import b2.tools.builtin
+
+
+    # Check if we can load 'test-config.jam'. If we can, load it and
+    # ignore user configs.
+    
+    test_config = glob(boost_build_path, ["test-config.jam"])
+    if test_config:
+        test_config = test_config[0]
+
+    if test_config:
+        if debug_config:
+            print "notice: loading testing-config.jam from '%s'" % test_config
+            print "notice: user-config.jam and site-config.jam will be ignored"
+
+        manager.projects().load_standalone("test-config", test_config)
+
+
+    ignore_config = test_config or get_boolean_option("ignore-config")
+    user_path = home_directories() + boost_build_path
+
+    site_path = ["/etc"] + user_path
+    if bjam.variable("OS") in ["NT", "CYGWIN"]:
+        site_path = [os.environ("SystemRoot")] + user_path
+
+    load_config(manager, "site-config", site_path)
+
+    user_config_path = get_string_option("user-config")
+    if not user_config_path:
+        user_config_path = os.environ.get("BOOST_BUILD_USER_CONFIG")
+
+    if user_config_path:
+        if debug_config:
+            print "Loading explicitly specifier user configuration file:"
+            print "    %s" % user_config_path
+            
+        manager.projects().load_standalone("user-config", user_config_path)
+
+    else:
+        load_config(manager, "user-config", user_path)
+        
+
+# FIXME:
+## #
+## # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or
+## # toolset=xx,yy,...zz in the command line
+## #
+## local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*) : $(argv) ] : "," ] ;
+## local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*) : $(argv) ] : "," ] ;
+
+## # if the user specified --toolset=..., we need to add toolset=... to
+## # the build request
+## local extra-build-request ;
+
+    extra_build_request = []
+
+## if ! $(ignore-config)
+## {
+##     for local t in $(option-toolsets) $(feature-toolsets)
+##     {
+##         # Parse toolset-version/properties
+##         local (t-v,t,v) = [ MATCH (([^-/]+)-?([^/]+)?)/?.* : $(t) ] ;
+##         local toolset-version = $((t-v,t,v)[1]) ;
+##         local toolset = $((t-v,t,v)[2]) ;
+##         local version = $((t-v,t,v)[3]) ;
+
+##         if $(debug-config)
+##         {
+##             ECHO notice: [cmdline-cfg] Detected command-line request for 
+##               $(toolset-version): toolset= \"$(toolset)\" "version= \""$(version)\" ;
+##         }
+
+##         local known ;
+
+##         # if the toolset isn't known, configure it now.
+##         if $(toolset) in [ feature.values <toolset>  ]
+##         {
+##             known = true ;
+##         }
+
+##         if $(known) && $(version) 
+##           && ! [ feature.is-subvalue toolset : $(toolset) : version : $(version) ]
+##         {
+##             known = ;
+##         }
+
+##         if ! $(known)
+##         {
+##             if $(debug-config)
+##             {
+##                 ECHO notice: [cmdline-cfg] toolset $(toolset-version) 
+##                   not previously configured; configuring now ; 
+##             }
+##             toolset.using $(toolset) : $(version) ;
+##         }
+##         else
+##         {
+##             if $(debug-config)
+##             {
+##                 ECHO notice: [cmdline-cfg] toolset $(toolset-version) already configured ;
+##             }
+##         }
+
+##         # make sure we get an appropriate property into the build request in
+##         # case the user used the "--toolset=..." form
+##         if ! $(t) in $(argv)
+##             && ! $(t) in $(feature-toolsets) 
+##         {
+##             if $(debug-config)
+##             {
+##                 ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ;
+##             }
+##             extra-build-request += toolset=$(t) ;
+##         }
+##     }
+## }
+
+
+# FIXME:
+## if USER_MODULE in [ RULENAMES ]
+## {
+##     USER_MODULE site-config user-config ;
+## }
+
+    if get_boolean_option("version"):
+        # FIXME: Move to a separate module. Include bjam
+        # verision.
+        print "Boost.Build M15 (Python port in development)"
+        sys.exit(0)
+
+    b2.tools.common.init(manager)        
+
+    # We always load project in "." so that 'use-project' directives has
+    # any chance of been seen. Otherwise, we won't be able to refer to
+    # subprojects using target ids.
+
+    current_project = None
+    projects = manager.projects()
+    if projects.find(".", "."):
+        current_project = projects.target(projects.load("."))
+
+    # FIXME: revive this logic, when loading of gcc works
+    if not feature.values("<toolset>") and not ignore_config and 0:
+        default_toolset = "gcc" ;
+        if bjam.variable("OS") == "NT":
+            default_toolset = "msvc"
+               
+        print "warning: No toolsets are configured." ;
+        print "warning: Configuring default toolset '%s'" % default_toolset
+        print "warning: If the default is wrong, you may not be able to build C++ programs."
+        print "warning: Use the \"--toolset=xxxxx\" option to override our guess."
+        print "warning: For more configuration options, please consult"
+        print "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
+
+        projects.project_rules().using([default_toolset])
+
+    (target_ids, properties) = b2.build.build_request.from_command_line(
+        argv[1:] + extra_build_request)
+
+    if properties:
+        expanded = b2.build.build_request.expand_no_defaults(properties)
+        xexpanded = []
+        for e in expanded:
+            xexpanded.append(property_set.create(feature.split(e)))
+        expanded = xexpanded
+    else:
+        expanded = [property_set.empty()]
+
+    targets = []
+    
+    clean = get_boolean_option("clean")
+    clean_all = get_boolean_option("clean-all")
+    
+
+    bjam_targets = []
+
+    # Given a target id, try to find and return corresponding target.
+    # This is only invoked when there's no Jamfile in "."
+    # This code somewhat duplicates code in project-target.find but we can't  reuse
+    # that code without project-targets instance.
+    def find_target (target_id):
+        split = target_id.split("//")
+        pm = None
+        if len(split) > 1:
+            pm = projects.find(split[0], ".")
+        else:
+            pm = projects.find(target_id, ".")
+
+        result = None
+        if pm:
+            result = projects.target(pm)
+
+        if len(split) > 1:
+            result = result.find(split[1])
+
+    if not current_project and not target_ids:
+        print "error: no Jamfile in current directory found, and no target references specified."
+        sys.exit(1)
+
+    for id in target_ids:
+        if id == "clean":
+            clean = 1
+        else:
+            t = None
+            if current_project:
+                t = current_project.find(id, no_error=1)
+            else:
+                t = find_target(id)
+
+            if not t:
+                print "notice: could not find main target '%s'" % id
+                print "notice: assuming it's a name of file to create " ;
+                bjam_targets.append(id)
+            else:
+                targets.append(t)
+
+    if not targets:
+        targets = [projects.target(projects.module_name("."))]
+    
+    virtual_targets = []
+
+    # Virtual targets obtained when building main targets references on
+    # the command line. When running
+    #
+    #   bjam --clean main_target
+    #
+    # we want to clean the files that belong only to that main target,
+    # so we need to record which targets are produced.
+    results_of_main_targets = []
+
+    for p in expanded:
+        manager.set_command_line_free_features(property_set.create(p.free()))
+        
+        for t in targets:
+            try:
+                g = t.generate(p)
+                if not isinstance(t, ProjectTarget):
+                    results_of_main_targets.extend(g.targets())
+                virtual_targets.extend(g.targets())
+            except ExceptionWithUserContext, e:
+                e.report()
+            except Exception:                
+                raise
+
+    # The cleaning is tricky. Say, if
+    # user says: 
+    #
+    #    bjam --clean foo
+    #
+    # where 'foo' is a directory, then we want to clean targets
+    # which are in 'foo' or in any children Jamfiles, but not in any
+    # unrelated Jamfiles. So, we collect the list of project under which
+    # cleaning is allowed.
+    #
+    projects_to_clean = []
+    targets_to_clean = []
+    if clean or clean_all:
+        for t in targets:
+            if isinstance(t, ProjectTarget):
+                projects_to_clean.append(t.project_module())
+                
+            for t in results_of_main_targets:
+                # Don't include roots or sources.                
+                targets_to_clean += b2.build.virtual_target.traverse(t)
+ 
+    targets_to_clean = unique(targets_to_clean)
+
+    is_child_cache_ = {}
+
+    # Returns 'true' if 'project' is a child of 'current-project',
+    # possibly indirect, or is equal to 'project'.
+    # Returns 'false' otherwise.
+    def is_child (project):
+
+        r = is_child_cache_.get(project, None)
+        if not r:
+            if project in projects_to_clean:
+                r = 1
+            else:
+                parent = manager.projects().attribute(project, "parent-module")
+                if parent and parent != "user-config":
+                    r = is_child(parent)
+                else:
+                    r = 0
+
+            is_child_cache_[project] = r
+
+        return r
+
+    actual_targets = []
+    for t in virtual_targets:
+        actual_targets.append(t.actualize())
+
+
+    bjam.call("NOTFILE", "all")
+    bjam.call("DEPENDS", "all", actual_targets)
+
+    if bjam_targets:
+        bjam.call("UPDATE", ["<e>%s" % x for x in bjam_targets])
+    elif clean_all:
+        bjam.call("UPDATE", "clean-all")
+    elif clean:
+        to_clean = []
+        for t in manager.virtual_targets().all_targets():
+            p = t.project()
+
+            # Remove only derived targets.
+            if t.action() and \
+               (t in targets_to_clean or is_child(p.project_module())):
+                to_clean.append(t)
+
+        to_clean_actual = [t.actualize() for t in to_clean]
+        manager.engine().set_update_action('common.Clean', 'clean',
+                                           to_clean_actual, None)
+
+        bjam.call("UPDATE", "clean")
+
+    else:
+        bjam.call("UPDATE", "all")        
Added: trunk/tools/build/v2/exceptions.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/exceptions.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,44 @@
+# Copyright Pedro Ferreira 2005. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+# TODO: add more exception types?
+
+class BaseException (Exception):
+    def __init__ (self, message = ''): Exception.__init__ (self, message)
+
+class UserError (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class FeatureConflict (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class InvalidSource (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class InvalidFeature (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class InvalidProperty (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class InvalidValue (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class InvalidAttribute (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class AlreadyDefined (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class IllegalOperation (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class Recursion (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class NoBestMatchingAlternative (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
+
+class NoAction (BaseException):
+    def __init__ (self, message = ''): BaseException.__init__ (self, message)
Modified: trunk/tools/build/v2/kernel/bootstrap.jam
==============================================================================
--- trunk/tools/build/v2/kernel/bootstrap.jam	(original)
+++ trunk/tools/build/v2/kernel/bootstrap.jam	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -111,6 +111,8 @@
     BOOST_BUILD_PATH += $(whereami:D)/$(subdirs) ;
 
     modules.poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ;
+    
+    modules.poke : EXTRA_PYTHONPATH : $(whereami) ;
 }
 
 # Reload the modules, to clean up things. The modules module can tolerate
@@ -129,11 +131,110 @@
 #
 if ! $(dont-build)
 {
-    # Allow users to override the build system file from the
-    # command-line (mostly for testing)
-    local build-system = [ MATCH --build-system=(.*) : $(ARGV) ] ;
-    build-system ?= build-system ;
+    if ! [ MATCH (--python) : $(ARGV) ]
+    {
+        # Allow users to override the build system file from the
+        # command-line (mostly for testing)
+        local build-system = [ MATCH --build-system=(.*) : $(ARGV) ] ;
+        build-system ?= build-system ;
 
-    # Use last element in case of multiple command-line options
-    import $(build-system[-1]) ;
+        # Use last element in case of multiple command-line options
+        import $(build-system[-1]) ;
+    }
+    else
+    {
+        ECHO "Boost.Build V2 Python port (experimental)" ;
+                
+        # Define additional interface that is exposed to Python code. Python code will
+        # also have access to select bjam builtins in the 'bjam' module, but some
+        # things are easier to define outside C.
+        module python_interface
+        {
+            rule load ( module-name : location )
+            {        
+                USER_MODULE $(module-name) ;
+	        # Make all rules in the loaded module available in
+	        # the global namespace, so that we don't have
+	        # to bother specifying "right" module when calling
+	        # from Python.
+                module $(module-name)
+                {
+                    __name__ = $(1) ;
+                    include $(2) ;
+                    local rules = [ RULENAMES $(1) ] ;
+                    IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ;
+                }
+            }    
+            
+            rule peek ( module-name ? : variables + )
+            {
+                module $(<)
+                {
+                    return $($(>)) ;
+                }
+            }
+            
+            rule set-variable ( module-name : name : value * )
+            {
+                module $(<)
+                {
+                    $(>) = $(3) ;
+                }        
+            }
+            
+            rule set-top-level-targets ( targets * )
+            {
+                DEPENDS all : $(targets) ;
+            }
+            
+            rule set-update-action ( action : targets * : sources * : properties * )
+            {
+                $(action) $(targets) : $(sources) : $(properties) ;
+            }
+            
+            rule set-target-variable ( targets + : variable : value * : append ? )
+            {
+                if $(append)
+                {            
+	            $(variable) on $(targets) += $(value) ;
+                }
+                else
+                {
+	            $(variable) on $(targets) = $(value) ;            
+                }        
+            }        
+
+            rule get-target-variable ( target : variable )
+            {
+                return [ on $(target) return $($(variable)) ] ;
+            }
+
+            rule import-rules-from-parent ( parent-module : this-module : user-rules )
+            {
+                IMPORT $(parent-module) : $(user-rules) : $(this-module) : $(user-rules) ;
+                EXPORT $(this-module) : $(user-rules) ;
+            }
+
+            rule mark-included ( targets * : includes * ) {
+                INCLUDES $(targets) : $(INCLUDES) ;
+            }
+        }
+
+        PYTHON_IMPORT_RULE bootstrap : bootstrap : PyBB : bootstrap ;
+        modules.poke PyBB : root : [ NORMALIZE_PATH $(.bootstrap-file:DT)/.. ] ;
+        
+        module PyBB
+        {
+            bootstrap $(root) ;
+        }
+        
+        
+        #PYTHON_IMPORT_RULE boost.build.build_system : main : PyBB : main ;
+
+        #module PyBB
+        #{    
+        #    main ;
+        #}
+
+    }
 }
Added: trunk/tools/build/v2/manager.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/manager.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,132 @@
+# Copyright Pedro Ferreira 2005. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+import bjam
+
+# To simplify implementation of tools level, we'll
+# have a global variable keeping the current manager.
+the_manager = None
+def get_manager():
+    return the_manager
+
+class Manager:
+    """ This class is a facade to the Boost.Build system.
+        It serves as the root to access all data structures in use.
+    """
+        
+    def __init__ (self, engine, global_build_dir):
+        """ Constructor.
+            engine: the build engine that will actually construct the targets.
+        """
+        from build.virtual_target import VirtualTargetRegistry
+        from build.targets import TargetRegistry
+        from build.project import ProjectRegistry
+        from build.scanner import ScannerRegistry
+        from build.errors import Errors
+        from b2.util.logger import NullLogger
+        from build import build_request, property_set, feature
+        
+        self.engine_ = engine
+        self.virtual_targets_ = VirtualTargetRegistry (self)
+        self.projects_ = ProjectRegistry (self, global_build_dir)
+        self.targets_ = TargetRegistry ()
+        self.logger_ = NullLogger ()
+        self.scanners_ = ScannerRegistry (self)
+        self.argv_ = bjam.variable("ARGV")
+        self.boost_build_path_ = bjam.variable("BOOST_BUILD_PATH")
+        self.errors_ = Errors()
+        self.command_line_free_features_ = property_set.empty()
+        
+        # Object Map.
+        # TODO: This is a kludge: maps object names to the actual instances.
+        # Sometimes, objects are stored in properties, along with some grist.
+        # This map is used to store the value and return an id, which can be later on used to retriev it back.
+        self.object_map_ = {}
+
+        global the_manager
+        the_manager = self
+        
+    def scanners (self):
+        return self.scanners_
+
+    def engine (self):
+        return self.engine_
+        
+    def virtual_targets (self):
+        return self.virtual_targets_
+
+    def targets (self):
+        return self.targets_
+
+    def projects (self):
+        return self.projects_
+
+    def argv (self):
+        return self.argv_
+    
+    def logger (self):
+        return self.logger_
+
+    def set_logger (self, logger):
+        self.logger_ = logger
+
+    def errors (self):
+        return self.errors_
+
+    def getenv(self, name):
+        return bjam.variable(name)
+
+    def boost_build_path(self):
+        return self.boost_build_path_
+
+    def command_line_free_features(self):
+        return self.command_line_free_features_
+
+    def set_command_line_free_features(self, v):
+        self.command_line_free_features_ = v
+
+    def register_object (self, value):
+        """ Stores an object in a map and returns a key that can be used to retrieve it.
+        """
+        key = 'object_registry_' + str (value)
+        self.object_map_ [key] = value
+        return key
+    
+    def get_object (self, key):
+        """ Returns a previously registered object.
+        """
+        if not isinstance (key, str):
+            # Probably it's the object itself.
+            return key
+            
+        return self.object_map_ [key]
+
+    def construct (self, properties = [], targets = []):
+        """ Constructs the dependency graph.
+            properties:  the build properties.
+            targets:     the targets to consider. If none is specified, uses all.
+        """
+        if not targets:
+            for name, project in self.projects ().projects ():
+                targets.append (project.target ())
+            
+        property_groups = build_request.expand_no_defaults (properties)
+
+        virtual_targets = []
+        build_prop_sets = []
+        for p in property_groups:
+            build_prop_sets.append (property_set.create (feature.split (p)))
+
+        if not build_prop_sets:
+            build_prop_sets = [property_set.empty ()]
+
+        for build_properties in build_prop_sets:
+            for target in targets:
+                result = target.generate (build_properties)
+                virtual_targets.extend (result.targets ())
+
+        actual_targets = []
+        for virtual_target in virtual_targets:
+            actual_targets.extend (virtual_target.actualize ())
+    
Added: trunk/tools/build/v2/tools/__init__.py
==============================================================================
Added: trunk/tools/build/v2/tools/builtin.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/builtin.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,722 @@
+# Status: minor updates by Steven Watanabe to make gcc work
+#
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+""" Defines standard features and rules.
+"""
+
+import sys
+from b2.build import feature, property, virtual_target, generators, type, property_set, scanner
+from b2.util.utility import *
+from b2.util import path, regex
+import b2.tools.types
+from b2.manager import get_manager
+
+# Records explicit properties for a variant.
+# The key is the variant name.
+__variant_explicit_properties = {}
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+    """
+    global __variant_explicit_properties
+
+    __variant_explicit_properties = {}
+
+def variant (name, parents_or_properties, explicit_properties = []):
+    """ Declares a new variant.
+        First determines explicit properties for this variant, by
+        refining parents' explicit properties with the passed explicit
+        properties. The result is remembered and will be used if
+        this variant is used as parent.
+        
+        Second, determines the full property set for this variant by
+        adding to the explicit properties default values for all properties 
+        which neither present nor are symmetric.
+        
+        Lastly, makes appropriate value of 'variant' property expand
+        to the full property set.
+        name:                   Name of the variant
+        parents_or_properties:  Specifies parent variants, if 
+                                'explicit_properties' are given,
+                                and explicit_properties otherwise.
+        explicit_properties:    Explicit properties.
+    """
+    parents = []
+    if not explicit_properties:
+        if get_grist (parents_or_properties [0]):
+            explicit_properties = parents_or_properties
+
+        else:
+            parents = parents_or_properties
+
+    else:
+        parents = parents_or_properties
+
+    # The problem is that we have to check for conflicts
+    # between base variants.
+    if len (parents) > 1:
+        raise BaseException ("Multiple base variants are not yet supported")
+    
+    inherited = []
+    # Add explicitly specified properties for parents
+    for p in parents:
+        # TODO: the check may be stricter
+        if not feature.is_implicit_value (p):
+            raise BaseException ("Invalid base varaint '%s'" % p)
+        
+        inherited += __variant_explicit_properties [p]
+
+    property.validate (explicit_properties)
+    explicit_properties = property.refine (inherited, explicit_properties)
+    
+    # Record explicitly specified properties for this variant
+    # We do this after inheriting parents' properties, so that
+    # they affect other variants, derived from this one.
+    __variant_explicit_properties [name] = explicit_properties
+           
+    feature.extend('variant', [name])
+    feature.compose (replace_grist (name, '<variant>'), explicit_properties)
+
+__os_names = """
+    amiga aix bsd cygwin darwin dos emx freebsd hpux iphone linux netbsd
+    openbsd osf qnx qnxnto sgi solaris sun sunos svr4 sysv ultrix unix unixware
+    vms windows
+""".split()
+
+# Translates from bjam current OS to the os tags used in host-os and target-os,
+# i.e. returns the running host-os.
+#
+def default_host_os():
+    host_os = os_name()
+    if host_os not in (x.upper() for x in __os_names):
+        if host_os == 'NT': host_os = 'windows'
+        elif host_os == 'AS400': host_os = 'unix'
+        elif host_os == 'MINGW': host_os = 'windows'
+        elif host_os == 'BSDI': host_os = 'bsd'
+        elif host_os == 'COHERENT': host_os = 'unix'
+        elif host_os == 'DRAGONFLYBSD': host_os = 'bsd'
+        elif host_os == 'IRIX': host_os = 'sgi'
+        elif host_os == 'MACOSX': host_os = 'darwin'
+        elif host_os == 'KFREEBSD': host_os = 'freebsd'
+        elif host_os == 'LINUX': host_os = 'linux'
+        else: host_os = 'unix'
+    return host_os.lower()
+
+def register_globals ():
+    """ Registers all features and variants declared by this module.
+    """
+
+    # This feature is used to determine which OS we're on.
+    # In future, this may become <target-os> and <host-os>
+    # TODO: check this. Compatibility with bjam names? Subfeature for version?
+    os = sys.platform
+    feature.feature ('os', [os], ['propagated', 'link-incompatible'])
+
+
+    # The two OS features define a known set of abstract OS names. The host-os is
+    # the OS under which bjam is running. Even though this should really be a fixed
+    # property we need to list all the values to prevent unknown value errors. Both
+    # set the default value to the current OS to account for the default use case of
+    # building on the target OS.
+    feature.feature('host-os', __os_names)
+    feature.set_default('host-os', default_host_os())
+
+    feature.feature('target-os', __os_names, ['propagated', 'link-incompatible'])
+    feature.set_default('target-os', default_host_os())
+    
+    feature.feature ('toolset', [], ['implicit', 'propagated' ,'symmetric'])
+    
+    feature.feature ('stdlib', ['native'], ['propagated', 'composite'])
+    
+    feature.feature ('link', ['shared', 'static'], ['propagated'])
+    feature.feature ('runtime-link', ['shared', 'static'], ['propagated'])
+    feature.feature ('runtime-debugging', ['on', 'off'], ['propagated'])
+    
+    
+    feature.feature ('optimization',  ['off', 'speed', 'space'], ['propagated'])
+    feature.feature ('profiling', ['off', 'on'], ['propagated'])
+    feature.feature ('inlining', ['off', 'on', 'full'], ['propagated'])
+    
+    feature.feature ('threading', ['single', 'multi'], ['propagated'])
+    feature.feature ('rtti', ['on', 'off'], ['propagated'])
+    feature.feature ('exception-handling', ['on', 'off'], ['propagated'])
+    feature.feature ('debug-symbols', ['on', 'off'], ['propagated'])
+    feature.feature ('define', [], ['free'])
+    feature.feature ('include', [], ['free', 'path']) #order-sensitive
+    feature.feature ('cflags', [], ['free'])
+    feature.feature ('cxxflags', [], ['free'])
+    feature.feature ('linkflags', [], ['free'])
+    feature.feature ('archiveflags', [], ['free'])
+    feature.feature ('version', [], ['free'])
+    
+    feature.feature ('location-prefix', [], ['free'])
+
+    feature.feature ('action', [], ['free'])
+
+    
+    # The following features are incidental, since
+    # in themself they have no effect on build products.
+    # Not making them incidental will result in problems in corner
+    # cases, for example:
+    # 
+    #    unit-test a : a.cpp : <use>b ;
+    #    lib b : a.cpp b ;
+    # 
+    # Here, if <use> is not incidental, we'll decide we have two 
+    # targets for a.obj with different properties, and will complain.
+    #
+    # Note that making feature incidental does not mean it's ignored. It may
+    # be ignored when creating the virtual target, but the rest of build process
+    # will use them.
+    feature.feature ('use', [], ['free', 'dependency', 'incidental'])
+    feature.feature ('dependency', [], ['free', 'dependency', 'incidental'])
+    feature.feature ('implicit-dependency', [], ['free', 'dependency', 'incidental'])
+
+    feature.feature('warnings', [
+        'on',         # Enable default/"reasonable" warning level for the tool.
+        'all',        # Enable all possible warnings issued by the tool.
+        'off'],       # Disable all warnings issued by the tool.
+        ['incidental', 'propagated'])
+
+    feature.feature('warnings-as-errors', [
+        'off',        # Do not fail the compilation if there are warnings.
+        'on'],        # Fail the compilation if there are warnings.
+        ['incidental', 'propagated'])
+    
+    feature.feature ('source', [], ['free', 'dependency', 'incidental'])
+    feature.feature ('library', [], ['free', 'dependency', 'incidental'])
+    feature.feature ('file', [], ['free', 'dependency', 'incidental'])
+    feature.feature ('find-shared-library', [], ['free']) #order-sensitive ;
+    feature.feature ('find-static-library', [], ['free']) #order-sensitive ;
+    feature.feature ('library-path', [], ['free', 'path']) #order-sensitive ;
+    # Internal feature.
+    feature.feature ('library-file', [], ['free', 'dependency'])
+    
+    feature.feature ('name', [], ['free'])
+    feature.feature ('tag', [], ['free'])
+    feature.feature ('search', [], ['free', 'path']) #order-sensitive ;
+    feature.feature ('location', [], ['free', 'path'])
+    
+    feature.feature ('dll-path', [], ['free', 'path'])
+    feature.feature ('hardcode-dll-paths', ['true', 'false'], ['incidental'])
+    
+    
+    # This is internal feature which holds the paths of all dependency
+    # dynamic libraries. On Windows, it's needed so that we can all
+    # those paths to PATH, when running applications.
+    # On Linux, it's needed to add proper -rpath-link command line options.
+    feature.feature ('xdll-path', [], ['free', 'path'])
+    
+    #provides means to specify def-file for windows dlls.
+    feature.feature ('def-file', [], ['free', 'dependency'])
+    
+    # This feature is used to allow specific generators to run.
+    # For example, QT tools can only be invoked when QT library
+    # is used. In that case, <allow>qt will be in usage requirement
+    # of the library.
+    feature.feature ('allow', [], ['free'])
+    
+    # The addressing model to generate code for. Currently a limited set only
+    # specifying the bit size of pointers.
+    feature.feature('address-model', ['16', '32', '64'], ['propagated', 'optional'])
+
+    # Type of CPU architecture to compile for.
+    feature.feature('architecture', [
+        # x86 and x86-64
+        'x86',
+
+        # ia64
+        'ia64',
+
+        # Sparc
+        'sparc',
+
+        # RS/6000 & PowerPC
+        'power',
+
+        # MIPS/SGI
+        'mips1', 'mips2', 'mips3', 'mips4', 'mips32', 'mips32r2', 'mips64',
+
+        # HP/PA-RISC
+        'parisc',
+        
+        # Advanced RISC Machines
+        'arm',
+
+        # Combined architectures for platforms/toolsets that support building for
+        # multiple architectures at once. "combined" would be the default multi-arch
+        # for the toolset.
+        'combined',
+        'combined-x86-power'],
+
+        ['propagated', 'optional'])
+
+    # The specific instruction set in an architecture to compile.
+    feature.feature('instruction-set', [
+        # x86 and x86-64
+        'i386', 'i486', 'i586', 'i686', 'pentium', 'pentium-mmx', 'pentiumpro', 'pentium2', 'pentium3',
+        'pentium3m', 'pentium-m', 'pentium4', 'pentium4m', 'prescott', 'nocona', 'conroe', 'conroe-xe',
+        'conroe-l', 'allendale', 'mermon', 'mermon-xe', 'kentsfield', 'kentsfield-xe', 'penryn', 'wolfdale',
+        'yorksfield', 'nehalem', 'k6', 'k6-2', 'k6-3', 'athlon', 'athlon-tbird', 'athlon-4', 'athlon-xp',
+        'athlon-mp', 'k8', 'opteron', 'athlon64', 'athlon-fx', 'winchip-c6', 'winchip2', 'c3', 'c3-2',
+
+        # ia64
+        'itanium', 'itanium1', 'merced', 'itanium2', 'mckinley',
+
+        # Sparc
+        'v7', 'cypress', 'v8', 'supersparc', 'sparclite', 'hypersparc', 'sparclite86x', 'f930', 'f934',
+        'sparclet', 'tsc701', 'v9', 'ultrasparc', 'ultrasparc3',
+
+        # RS/6000 & PowerPC
+        '401', '403', '405', '405fp', '440', '440fp', '505', '601', '602',
+        '603', '603e', '604', '604e', '620', '630', '740', '7400',
+        '7450', '750', '801', '821', '823', '860', '970', '8540',
+        'power-common', 'ec603e', 'g3', 'g4', 'g5', 'power', 'power2',
+        'power3', 'power4', 'power5', 'powerpc', 'powerpc64', 'rios',
+        'rios1', 'rsc', 'rios2', 'rs64a',
+
+        # MIPS
+        '4kc', '4kp', '5kc', '20kc', 'm4k', 'r2000', 'r3000', 'r3900', 'r4000',
+        'r4100', 'r4300', 'r4400', 'r4600', 'r4650',
+        'r6000', 'r8000', 'rm7000', 'rm9000', 'orion', 'sb1', 'vr4100',
+        'vr4111', 'vr4120', 'vr4130', 'vr4300',
+        'vr5000', 'vr5400', 'vr5500',
+
+        # HP/PA-RISC
+        '700', '7100', '7100lc', '7200', '7300', '8000',
+        
+        # Advanced RISC Machines
+        'armv2', 'armv2a', 'armv3', 'armv3m', 'armv4', 'armv4t', 'armv5',
+        'armv5t', 'armv5te', 'armv6', 'armv6j', 'iwmmxt', 'ep9312'],
+
+        ['propagated', 'optional'])
+    
+    # Windows-specific features
+    feature.feature ('user-interface', ['console', 'gui', 'wince', 'native', 'auto'], [])
+    feature.feature ('variant', [], ['implicit', 'composite', 'propagated', 'symmetric'])
+
+
+    variant ('debug', ['<optimization>off', '<debug-symbols>on', '<inlining>off', '<runtime-debugging>on'])
+    variant ('release', ['<optimization>speed', '<debug-symbols>off', '<inlining>full', 
+                         '<runtime-debugging>off', '<define>NDEBUG'])
+    variant ('profile', ['release'], ['<profiling>on', '<debug-symbols>on'])
+
+    type.register ('H', ['h'])
+    type.register ('HPP', ['hpp'], 'H')
+    type.register ('C', ['c'])
+    
+
+reset ()
+register_globals ()
+
+class SearchedLibTarget (virtual_target.AbstractFileTarget):
+    def __init__ (self, name, project, shared, real_name, search, action):
+        virtual_target.AbstractFileTarget.__init__ (self, name, False, 'SEARCHED_LIB', project, action)
+        
+        self.shared_ = shared
+        self.real_name_ = real_name
+        if not self.real_name_:
+            self.real_name_ = name
+        self.search_ = search
+
+    def shared (self):
+        return self.shared_
+    
+    def real_name (self):
+        return self.real_name_
+    
+    def search (self):
+        return self.search_
+        
+    def actualize_location (self, target):
+        project.manager ().engine ().add_not_file_target (target)
+    
+    def path (self):
+        #FIXME: several functions rely on this not being None
+        return ""
+
+
+class CScanner (scanner.Scanner):
+    def __init__ (self, includes):
+        scanner.Scanner.__init__ (self)
+    
+        self.includes_ = includes
+
+    def pattern (self):
+        return r'#[ \t]*include[ ]*(<(.*)>|"(.*)")'
+
+    def process (self, target, matches, binding):
+       
+        angle = regex.transform (matches, "<(.*)>")
+        quoted = regex.transform (matches, '"(.*)"')
+
+        g = str(id(self))
+        b = os.path.normpath(os.path.dirname(binding[0]))
+        
+        # Attach binding of including file to included targets.
+        # When target is directly created from virtual target
+        # this extra information is unnecessary. But in other
+        # cases, it allows to distinguish between two headers of the 
+        # same name included from different places.      
+        # We don't need this extra information for angle includes,
+        # since they should not depend on including file (we can't
+        # get literal "." in include path).
+        g2 = g + "#" + b
+
+        g = "<" + g + ">"
+        g2 = "<" + g2 + ">"
+        angle = [g + x for x in angle]
+        quoted = [g2 + x for x in quoted]
+
+        all = angle + quoted
+        bjam.call("mark-included", target, all)
+
+        engine = get_manager().engine()
+        engine.set_target_variable(angle, "SEARCH", self.includes_)
+        engine.set_target_variable(quoted, "SEARCH", self.includes_)
+        
+        # Just propagate current scanner to includes, in a hope
+        # that includes do not change scanners. 
+        get_manager().scanners().propagate(self, angle + quoted)
+        
+scanner.register (CScanner, 'include')
+type.set_scanner ('CPP', CScanner)
+
+# Ported to trunk_at_47077
+class LibGenerator (generators.Generator):
+    """ The generator class for libraries (target type LIB). Depending on properties it will
+        request building of the approapriate specific type -- SHARED_LIB, STATIC_LIB or 
+        SHARED_LIB.
+    """
+
+    def __init__(self, id = 'LibGenerator', composing = True, source_types = [], target_types_and_names = ['LIB'], requirements = []):
+        generators.Generator.__init__(self, id, composing, source_types, target_types_and_names, requirements)
+    
+    def run(self, project, name, prop_set, sources):
+        # The lib generator is composing, and can be only invoked with
+        # explicit name. This check is present in generator.run (and so in
+        # builtin.LinkingGenerator), but duplicate it here to avoid doing
+        # extra work.
+        if name:
+            properties = prop_set.raw()
+            # Determine the needed target type
+            actual_type = None
+            properties_grist = get_grist(properties)
+            if '<source>' not in properties_grist  and \
+               ('<search>' in properties_grist or '<name>' in properties_grist):
+                actual_type = 'SEARCHED_LIB'
+            elif '<file>' in properties_grist:
+                # The generator for 
+                actual_type = 'LIB'
+            elif '<link>shared' in properties:
+                actual_type = 'SHARED_LIB'
+            else:
+                actual_type = 'STATIC_LIB'
+
+            prop_set = prop_set.add_raw(['<main-target-type>LIB'])
+
+            # Construct the target.
+            return generators.construct(project, name, actual_type, prop_set, sources)
+
+    def viable_source_types(self):
+        return ['*']
+
+generators.register(LibGenerator())
+
+### # The implementation of the 'lib' rule. Beyond standard syntax that rule allows
+### # simplified:
+### #    lib a b c ;
+### # so we need to write code to handle that syntax. 
+### rule lib ( names + : sources * : requirements * : default-build * 
+###     : usage-requirements * )
+### {
+###     local project = [ project.current ] ;
+###     
+###     # This is a circular module dependency, so it must be imported here
+###     import targets ;
+### 
+###     local result ;
+###     if ! $(sources) && ! $(requirements) 
+###       && ! $(default-build) && ! $(usage-requirements)
+###     {
+###         for local name in $(names)
+###         {    
+###             result += [ 
+###             targets.main-target-alternative
+###               [ new typed-target $(name) : $(project) : LIB 
+###                 : 
+###                 : [ targets.main-target-requirements $(requirements) <name>$(name)  :
+###                     $(project) ] 
+###                 : [ targets.main-target-default-build $(default-build) : $(project) ]
+###                 : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
+###              ] ] ;        
+###         }        
+###     }
+###     else
+###     {
+###         if $(names[2])
+###         {
+###             errors.user-error "When several names are given to the 'lib' rule" :
+###               "it's not allowed to specify sources or requirements. " ;
+###         }
+###                 
+###         local name = $(names[1]) ;
+###         result = [ targets.main-target-alternative
+###           [ new typed-target $(name) : $(project) : LIB
+###             : [ targets.main-target-sources $(sources) : $(name) ] 
+###             : [ targets.main-target-requirements $(requirements) : $(project) ] 
+###             : [ targets.main-target-default-build $(default-build) : $(project) ]
+###             : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
+###          ] ] ;
+###     }    
+###     return $(result) ;
+### }
+### IMPORT $(__name__) : lib : : lib ;
+
+# Updated to trunk_at_47077
+class SearchedLibGenerator (generators.Generator):
+    def __init__ (self, id = 'SearchedLibGenerator', composing = False, source_types = [], target_types_and_names = ['SEARCHED_LIB'], requirements = []):
+        # TODO: the comment below looks strange. There are no requirements!
+        # The requirements cause the generators to be tried *only* when we're building
+        # lib target and there's 'search' feature. This seems ugly --- all we want
+        # is make sure SearchedLibGenerator is not invoked deep in transformation
+        # search.
+        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+    
+    def run(self, project, name, prop_set, sources):
+        if not name:
+            return None
+
+        # If name is empty, it means we're called not from top-level.
+        # In this case, we just fail immediately, because SearchedLibGenerator
+        # cannot be used to produce intermediate targets.
+        
+        properties = prop_set.raw ()
+        shared = '<link>shared' in properties
+
+        a = virtual_target.NullAction (project.manager(), prop_set)
+        
+        real_name = feature.get_values ('<name>', properties)
+        if real_name:
+            real_name = real_name[0]
+        else:
+            real_nake = name
+        search = feature.get_values('<search>', properties)
+        usage_requirements = property_set.create(['<xdll-path>' + p for p in search])
+        t = SearchedLibTarget(name, project, shared, real_name, search, a)
+
+        # We return sources for a simple reason. If there's
+        #    lib png : z : <name>png ; 
+        # the 'z' target should be returned, so that apps linking to
+        # 'png' will link to 'z', too.
+        return(usage_requirements, [b2.manager.get_manager().virtual_targets().register(t)] + sources)
+
+generators.register (SearchedLibGenerator ())
+
+### class prebuilt-lib-generator : generator
+### {
+###     rule __init__ ( * : * )
+###     {
+###         generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+###     }
+### 
+###     rule run ( project name ? : prop_set : sources * : multiple ? )
+###     {
+###         local f = [ $(prop_set).get <file> ] ;
+###         return $(f) $(sources) ;
+###     }    
+### }
+### 
+### generators.register 
+###   [ new prebuilt-lib-generator builtin.prebuilt : : LIB : <file> ] ;
+
+
+class CompileAction (virtual_target.Action):
+    def __init__ (self, manager, sources, action_name, prop_set):
+        virtual_target.Action.__init__ (self, manager, sources, action_name, prop_set)
+
+    def adjust_properties (self, prop_set):
+        """ For all virtual targets for the same dependency graph as self, 
+            i.e. which belong to the same main target, add their directories
+            to include path.
+        """
+        s = self.targets () [0].creating_subvariant ()
+
+        return prop_set.add_raw (s.implicit_includes ('include', 'H'))
+
+class CCompilingGenerator (generators.Generator):
+    """ Declare a special compiler generator.
+        The only thing it does is changing the type used to represent
+        'action' in the constructed dependency graph to 'CompileAction'.
+        That class in turn adds additional include paths to handle a case
+        when a source file includes headers which are generated themselfs.
+    """
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        # TODO: (PF) What to do with optional_properties? It seemed that, in the bjam version, the arguments are wrong.
+        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+            
+    def action_class (self):
+        return CompileAction
+
+def register_c_compiler (id, source_types, target_types, requirements, optional_properties = []):
+    g = CCompilingGenerator (id, False, source_types, target_types, requirements + optional_properties)
+    return generators.register (g)
+
+
+class LinkingGenerator (generators.Generator):
+    """ The generator class for handling EXE and SHARED_LIB creation.
+    """
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+        
+    def run (self, project, name, prop_set, sources):
+        lib_sources = prop_set.get('<library>')
+        [ sources.append (project.manager().get_object(x)) for x in lib_sources ]
+        
+        # Add <library-path> properties for all searched libraries
+        extra = []
+        for s in sources:
+            if s.type () == 'SEARCHED_LIB':
+                search = s.search()
+                extra.append(replace_grist(search, '<library-path>'))
+
+        orig_xdll_path = []
+                   
+        if prop_set.get('<hardcode-dll-paths>') == ['true'] and type.is_derived(self.target_types_ [0], 'EXE'):
+            xdll_path = prop_set.get('<xdll-path>')
+            orig_xdll_path = [ replace_grist(x, '<dll-path>') for x in xdll_path ]
+            # It's possible that we have libraries in sources which did not came
+            # from 'lib' target. For example, libraries which are specified
+            # just as filenames as sources. We don't have xdll-path properties
+            # for such target, but still need to add proper dll-path properties.
+            for s in sources:
+                if type.is_derived (s.type (), 'SHARED_LIB') and not s.action ():
+                    # Unfortunately, we don't have a good way to find the path
+                    # to a file, so use this nasty approach.
+                    p = s.project()
+                    location = path.root(s.name(), p.get('source-location'))
+                    xdll_path.append(path.parent(location))
+                          
+            extra += [ replace_grist(x, '<dll-path>') for x in xdll_path ]
+        
+        if extra:
+            prop_set = prop_set.add_raw (extra)
+                        
+        result = generators.Generator.run(self, project, name, prop_set, sources)
+
+        if result:
+            ur = self.extra_usage_requirements(result, prop_set)
+            ur = ur.add(property_set.create(orig_xdll_path))
+        else:
+            return None
+        
+        return(ur, result)
+    
+    def extra_usage_requirements (self, created_targets, prop_set):
+        
+        result = property_set.empty ()
+        extra = []
+                        
+        # Add appropriate <xdll-path> usage requirements.
+        raw = prop_set.raw ()
+        if '<link>shared' in raw:
+            paths = []
+            
+            # TODO: is it safe to use the current directory? I think we should use 
+            # another mechanism to allow this to be run from anywhere.
+            pwd = os.getcwd()
+            
+            for t in created_targets:
+                if type.is_derived(t.type(), 'SHARED_LIB'):
+                    paths.append(path.root(path.make(t.path()), pwd))
+
+            extra += replace_grist(paths, '<xdll-path>')
+        
+        # We need to pass <xdll-path> features that we've got from sources,
+        # because if shared library is built, exe which uses it must know paths
+        # to other shared libraries this one depends on, to be able to find them
+        # all at runtime.
+                        
+        # Just pass all features in property_set, it's theorically possible
+        # that we'll propagate <xdll-path> features explicitly specified by
+        # the user, but then the user's to blaim for using internal feature.                
+        values = prop_set.get('<xdll-path>')
+        extra += replace_grist(values, '<xdll-path>')
+        
+        if extra:
+            result = property_set.create(extra)
+
+        return result
+
+    def generated_targets (self, sources, prop_set, project, name):
+
+        # sources to pass to inherited rule
+        sources2 = []
+        # properties to pass to inherited rule
+        properties2  = []
+        # sources which are libraries
+        libraries  = []
+        
+        # Searched libraries are not passed as argument to linker
+        # but via some option. So, we pass them to the action
+        # via property. 
+        properties2 = prop_set.raw()
+        fsa = []
+        fst = []
+        for s in sources:
+            if type.is_derived(s.type(), 'SEARCHED_LIB'):
+                name = s.real_name()
+                if s.shared():
+                    fsa.append(name)
+
+                else:
+                    fst.append(name)
+
+            else:
+                sources2.append(s)
+
+        if fsa:
+            properties2 += [replace_grist('&&'.join(fsa), '<find-shared-library>')]
+        if fst:
+            properties2 += [replace_grist('&&'.join(fst), '<find-static-library>')]
+                
+        spawn = generators.Generator.generated_targets(self, sources2, property_set.create(properties2), project, name)
+        
+        return spawn
+
+
+def register_linker(id, source_types, target_types, requirements):
+    g = LinkingGenerator(id, True, source_types, target_types, requirements)
+    generators.register(g)
+
+class ArchiveGenerator (generators.Generator):
+    """ The generator class for handling STATIC_LIB creation.
+    """
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+        
+    def run (self, project, name, prop_set, sources):
+        sources += prop_set.get ('<library>')
+        
+        result = generators.Generator.run (self, project, name, prop_set, sources)
+             
+        return result
+
+### rule register-archiver ( id composing ? : source_types + : target_types + :
+###                             requirements * )
+### {
+###     local g = [ new ArchiveGenerator $(id) $(composing) : $(source_types) 
+###                 : $(target_types) : $(requirements) ] ;
+###     generators.register $(g) ;
+### }
+### 
+### 
+### IMPORT $(__name__) : register-linker register-archiver 
+###   : : generators.register-linker generators.register-archiver ;
+### 
+### 
+### 
Added: trunk/tools/build/v2/tools/common.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/common.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,817 @@
+#  Status: being ported by Steven Watanabe
+#  Base revision: 47174
+#
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+""" Provides actions common to all toolsets, such as creating directories and
+    removing files.
+"""
+
+import re
+import bjam
+import os
+import os.path
+
+from b2.build import feature
+from b2.util.utility import *
+from b2.util import path
+
+__re__before_first_dash = re.compile ('([^-]*)-')
+
+def reset ():
+    """ Clear the module state. This is mainly for testing purposes.
+        Note that this must be called _after_ resetting the module 'feature'.
+    """    
+    global __had_unspecified_value, __had_value, __declared_subfeature
+    global __init_loc
+    global __all_signatures, __debug_configuration, __show_configuration
+    
+    # Stores toolsets without specified initialization values.
+    __had_unspecified_value = {}
+
+    # Stores toolsets with specified initialization values.
+    __had_value = {}
+    
+    # Stores toolsets with declared subfeatures.
+    __declared_subfeature = {}
+    
+    # Stores all signatures of the toolsets.
+    __all_signatures = {}
+
+    # Stores the initialization locations of each toolset
+    __init_loc = {}
+
+    __debug_configuration = '--debug-configuration' in bjam.variable('ARGV')
+    __show_configuration = '--show-configuration' in bjam.variable('ARGV')
+    
+reset()
+
+# ported from trunk_at_47174
+class Configurations(object):
+    """
+        This class helps to manage toolset configurations. Each configuration
+        has a unique ID and one or more parameters. A typical example of a unique ID
+        is a condition generated by 'common.check-init-parameters' rule. Other kinds
+        of IDs can be used. Parameters may include any details about the configuration
+        like 'command', 'path', etc.
+
+        A toolset configuration may be in one of the following states:
+
+        - registered
+              Configuration has been registered (e.g. by autodetection code) but has
+              not yet been marked as used, i.e. 'toolset.using' rule has not yet been
+              called for it.
+          - used
+              Once called 'toolset.using' rule marks the configuration as 'used'.
+
+        The main difference between the states above is that while a configuration is
+        'registered' its options can be freely changed. This is useful in particular
+        for autodetection code - all detected configurations may be safely overwritten
+        by user code.
+    """
+
+    def __init__(self):
+        self.used_ = set()
+        self.all_ = set()
+        self.params = {}
+
+    def register(self, id):
+        """
+            Registers a configuration.
+
+            Returns True if the configuration has been added and False if
+            it already exists. Reports an error if the configuration is 'used'.
+        """
+        if id in self.used_:
+            #FIXME
+            errors.error("common: the configuration '$(id)' is in use")
+
+        if id not in self.all_:
+            self.all_ += [id]
+
+            # Indicate that a new configuration has been added.
+            return True
+        else:
+            return False
+
+    def use(self, id):
+        """
+            Mark a configuration as 'used'.
+
+            Returns True if the state of the configuration has been changed to
+            'used' and False if it the state wasn't changed. Reports an error
+            if the configuration isn't known.
+        """
+        if id not in self.all_:
+            #FIXME:
+            errors.error("common: the configuration '$(id)' is not known")
+
+        if id not in self.used_:
+            self.used_ += [id]
+
+            # indicate that the configuration has been marked as 'used'
+            return True
+        else:
+            return False
+
+    def all(self):
+        """ Return all registered configurations. """
+        return self.all_
+
+    def used(self):
+        """ Return all used configurations. """
+        return self.used_
+
+    def get(self, id, param):
+        """ Returns the value of a configuration parameter. """
+        self.params_.getdefault(param, {}).getdefault(id, None)
+
+    def set (self, id, param, value):
+        """ Sets the value of a configuration parameter. """
+        self.params_.setdefault(param, {})[id] = value
+
+# Ported from trunk_at_47174
+def check_init_parameters(toolset, requirement, *args):
+    """ The rule for checking toolset parameters. Trailing parameters should all be
+        parameter name/value pairs. The rule will check that each parameter either has
+        a value in each invocation or has no value in each invocation. Also, the rule
+        will check that the combination of all parameter values is unique in all
+        invocations.
+
+        Each parameter name corresponds to a subfeature. This rule will declare a
+        subfeature the first time a non-empty parameter value is passed and will
+        extend it with all the values.
+
+        The return value from this rule is a condition to be used for flags settings.
+    """
+    # The type checking here is my best guess about
+    # what the types should be.
+    assert(isinstance(toolset, str))
+    assert(isinstance(requirement, str) or requirement is None)
+    sig = toolset
+    condition = replace_grist(toolset, '<toolset>')
+    subcondition = []
+    
+    for arg in args:
+        assert(isinstance(arg, tuple))
+        assert(len(arg) == 2)
+        name = arg[0]
+        value = arg[1]
+        assert(isinstance(name, str))
+        assert(isinstance(value, str) or value is None)
+        
+        str_toolset_name = str((toolset, name))
+
+        # FIXME: is this the correct translation?
+        ### if $(value)-is-not-empty
+        if value is not None:
+            condition = condition + '-' + value
+            if __had_unspecified_value.has_key(str_toolset_name):
+                raise BaseException("'%s' initialization: parameter '%s' inconsistent\n" \
+                "no value was specified in earlier initialization\n" \
+                "an explicit value is specified now" % (toolset, name))
+
+            # The logic below is for intel compiler. It calls this rule
+            # with 'intel-linux' and 'intel-win' as toolset, so we need to
+            # get the base part of toolset name.
+            # We can't pass 'intel' as toolset, because it that case it will
+            # be impossible to register versionles intel-linux and
+            # intel-win of specific version.
+            t = toolset
+            m = __re__before_first_dash.match(toolset)
+            if m:
+                t = m.group(1)
+
+            if not __had_value.has_key(str_toolset_name):
+                if not __declared_subfeature.has_key(str((t, name))):
+                    feature.subfeature('toolset', t, name, [], ['propagated'])
+                    __declared_subfeature[str((t, name))] = True
+
+                __had_value[str_toolset_name] = True
+
+            feature.extend_subfeature('toolset', t, name, [value])
+            subcondition += ['<toolset-' + t + ':' + name + '>' + value ]
+
+        else:
+            if __had_value.has_key(str_toolset_name):
+                raise BaseException ("'%s' initialization: parameter '%s' inconsistent\n" \
+                "an explicit value was specified in an earlier initialization\n" \
+                "no value is specified now" % (toolset, name))
+
+            __had_unspecified_value[str_toolset_name] = True
+
+        if value == None: value = ''
+        
+        sig = sig + value + '-'
+
+    if __all_signatures.has_key(sig):
+        message = "duplicate initialization of '%s' with the following parameters: " % toolset
+        
+        for arg in args:
+            name = arg[0]
+            value = arg[1]
+            if value == None: value = '<unspecified>'
+            
+            message += "'%s' = '%s'\n" % (name, value)
+
+        raise BaseException(message)
+
+    __all_signatures[sig] = True
+    # FIXME
+    __init_loc[sig] = "User location unknown" #[ errors.nearest-user-location ] ;
+
+    # If we have a requirement, this version should only be applied under that
+    # condition. To accomplish this we add a toolset requirement that imposes
+    # the toolset subcondition, which encodes the version.
+    if requirement:
+        r = ['<toolset>' + toolset, requirement]
+        r = ','.join(r)
+        toolset.add_requirements([r + ':' + c for c in subcondition])
+
+    # We add the requirements, if any, to the condition to scope the toolset
+    # variables and options to this specific version.
+    condition = [condition]
+    if requirement:
+        condition += [requirement]
+
+    if __show_configuration:
+        print "notice:", condition
+    return ['/'.join(condition)]
+
+# Ported from trunk_at_47077
+def get_invocation_command_nodefault(
+    toolset, tool, user_provided_command=[], additional_paths=[], path_last=False):
+    """
+        A helper rule to get the command to invoke some tool. If
+        'user-provided-command' is not given, tries to find binary named 'tool' in
+        PATH and in the passed 'additional-path'. Otherwise, verifies that the first
+        element of 'user-provided-command' is an existing program.
+        
+        This rule returns the command to be used when invoking the tool. If we can't
+        find the tool, a warning is issued. If 'path-last' is specified, PATH is
+        checked after 'additional-paths' when searching for 'tool'.
+    """
+    assert(isinstance(toolset, str))
+    assert(isinstance(tool, str))
+    assert(isinstance(user_provided_command, list))
+    if additional_paths is not None:
+        assert(isinstance(additional_paths, list))
+        assert(all([isinstance(path, str) for path in additional_paths]))
+    assert(all(isinstance(path, str) for path in additional_paths))
+    assert(isinstance(path_last, bool))
+    
+    if not user_provided_command:
+        command = find_tool(tool, additional_paths, path_last) 
+        if not command and __debug_configuration:
+            print "warning: toolset", toolset, "initialization: can't find tool, tool"
+            #FIXME
+            #print "warning: initialized from" [ errors.nearest-user-location ] ;
+    else:
+        command = check_tool(user_provided_command)
+        if not command and __debug_configuration:
+            print "warning: toolset", toolset, "initialization:"
+            print "warning: can't find user-provided command", user_provided_command
+            #FIXME
+            #ECHO "warning: initialized from" [ errors.nearest-user-location ]
+
+    assert(isinstance(command, str))
+    
+    return command
+
+# ported from trunk_at_47174
+def get_invocation_command(toolset, tool, user_provided_command = [],
+                           additional_paths = [], path_last = False):
+    """ Same as get_invocation_command_nodefault, except that if no tool is found,
+        returns either the user-provided-command, if present, or the 'tool' parameter.
+    """
+
+    assert(isinstance(toolset, str))
+    assert(isinstance(tool, str))
+    assert(isinstance(user_provided_command, list))
+    if additional_paths is not None:
+        assert(isinstance(additional_paths, list))
+        assert(all([isinstance(path, str) for path in additional_paths]))
+    assert(isinstance(path_last, bool))
+
+    result = get_invocation_command_nodefault(toolset, tool,
+                                              user_provided_command,
+                                              additional_paths,
+                                              path_last)
+
+    if not result:
+        if user_provided_command:
+            result = user_provided_command[0]
+        else:
+            result = tool
+
+    assert(isinstance(result, str))
+    
+    return result
+
+# ported from trunk_at_47281
+def get_absolute_tool_path(command):
+    """
+        Given an invocation command,
+        return the absolute path to the command. This works even if commnad
+        has not path element and is present in PATH.
+    """
+    if os.path.dirname(command):
+        return os.path.dirname(command)
+    else:
+        programs = path.programs_path()
+        m = path.glob(programs, [command, command + '.exe' ])
+        if not len(m):
+            print "Could not find:", command, "in", programs
+        return os.path.dirname(m[0])
+
+# ported from trunk_at_47174
+def find_tool(name, additional_paths = [], path_last = False):
+    """ Attempts to find tool (binary) named 'name' in PATH and in
+        'additional-paths'.  If found in path, returns 'name'.  If
+        found in additional paths, returns full name.  If the tool
+        is found in several directories, returns the first path found.
+        Otherwise, returns the empty string.  If 'path_last' is specified,
+        path is checked after 'additional_paths'.
+    """
+    assert(isinstance(name, str))
+    assert(isinstance(additional_paths, list))
+    assert(isinstance(path_last, bool))
+
+    programs = path.programs_path()
+    match = path.glob(programs, [name, name + '.exe'])
+    additional_match = path.glob(additional_paths, [name, name + '.exe'])
+
+    result = []
+    if path_last:
+        result = additional_match
+        if not result and match:
+            result = match
+
+    else:
+        if match:
+            result = match
+
+        elif additional_match:
+            result = additional_match
+
+    if result:
+        return path.native(result[0])
+    else:
+        return ''
+
+#ported from trunk_at_47281
+def check_tool_aux(command):
+    """ Checks if 'command' can be found either in path
+        or is a full name to an existing file.
+    """
+    assert(isinstance(command, str))
+    dirname = os.path.dirname(command)
+    if dirname:
+        if os.path.exists(command):
+            return command
+        # Both NT and Cygwin will run .exe files by their unqualified names.
+        elif on_windows() and os.path.exists(command + '.exe'):
+            return command
+        # Only NT will run .bat files by their unqualified names.
+        elif os_name() == 'NT' and os.path.exists(command + '.bat'):
+            return command
+    else:
+        paths = path.programs_path()
+        if path.glob(paths, [command]):
+            return command
+
+# ported from trunk_at_47281
+def check_tool(command):
+    """ Checks that a tool can be invoked by 'command'. 
+        If command is not an absolute path, checks if it can be found in 'path'.
+        If comand is absolute path, check that it exists. Returns 'command'
+        if ok and empty string otherwise.
+    """
+    assert(isinstance(command, list))
+    assert(all(isinstance(c, str) for c in command))
+    #FIXME: why do we check the first and last elements????
+    if check_tool_aux(command[0]) or check_tool_aux(command[-1]):
+        return command
+
+# ported from trunk_at_47281
+def handle_options(tool, condition, command, options):
+    """ Handle common options for toolset, specifically sets the following
+        flag variables:
+        - CONFIG_COMMAND to 'command'
+        - OPTIOns for compile to the value of <compileflags> in options
+        - OPTIONS for compile.c to the value of <cflags> in options
+        - OPTIONS for compile.c++ to the value of <cxxflags> in options
+        - OPTIONS for compile.fortran to the value of <fflags> in options
+        - OPTIONs for link to the value of <linkflags> in options
+    """
+    from b2.build import toolset
+
+    assert(isinstance(tool, str))
+    assert(isinstance(condition, list))
+    assert(isinstance(command, str))
+    assert(isinstance(options, list))
+    assert(command)
+    toolset.flags(tool, 'CONFIG_COMMAND', condition, [command])
+    toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('<compileflags>', options))
+    toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('<cflags>', options))
+    toolset.flags(tool + '.compile.c++', 'OPTIONS', condition, feature.get_values('<cxxflags>', options))
+    toolset.flags(tool + '.compile.fortran', 'OPTIONS', condition, feature.get_values('<fflags>', options))
+    toolset.flags(tool + '.link', 'OPTIONS', condition, feature.get_values('<linkflags>', options))
+
+# ported from trunk_at_47281
+def get_program_files_dir():
+    """ returns the location of the "program files" directory on a windows
+        platform
+    """
+    ProgramFiles = bjam.variable("ProgramFiles")
+    if ProgramFiles:
+        ProgramFiles = ' '.join(ProgramFiles)
+    else:
+        ProgramFiles = "c:\\Program Files"
+    return ProgramFiles
+
+# ported from trunk_at_47281
+def rm_command():
+    return __RM
+
+# ported from trunk_at_47281
+def copy_command():
+    return __CP
+
+# ported from trunk_at_47281
+def variable_setting_command(variable, value):
+    """
+        Returns the command needed to set an environment variable on the current
+        platform. The variable setting persists through all following commands and is
+        visible in the environment seen by subsequently executed commands. In other
+        words, on Unix systems, the variable is exported, which is consistent with the
+        only possible behavior on Windows systems.
+    """
+    assert(isinstance(variable, str))
+    assert(isinstance(value, str))
+
+    if os_name() == 'NT':
+        return "set " + variable + "=" + value + os.linesep
+    else:
+        # (todo)
+        #   The following does not work on CYGWIN and needs to be fixed. On
+        # CYGWIN the $(nl) variable holds a Windows new-line \r\n sequence that
+        # messes up the executed export command which then reports that the
+        # passed variable name is incorrect. This is most likely due to the
+        # extra \r character getting interpreted as a part of the variable name.
+        #
+        #   Several ideas pop to mind on how to fix this:
+        #     * One way would be to separate the commands using the ; shell
+        #       command separator. This seems like the quickest possible
+        #       solution but I do not know whether this would break code on any
+        #       platforms I I have no access to.
+        #     * Another would be to not use the terminating $(nl) but that would
+        #       require updating all the using code so it does not simply
+        #       prepend this variable to its own commands.
+        #     * I guess the cleanest solution would be to update Boost Jam to
+        #       allow explicitly specifying \n & \r characters in its scripts
+        #       instead of always relying only on the 'current OS native newline
+        #       sequence'.
+        #
+        #   Some code found to depend on this behaviour:
+        #     * This Boost Build module.
+        #         * __test__ rule.
+        #         * path-variable-setting-command rule.
+        #     * python.jam toolset.
+        #     * xsltproc.jam toolset.
+        #     * fop.jam toolset.
+        #                                     (todo) (07.07.2008.) (Jurko)
+        #
+        # I think that this works correctly in python -- Steven Watanabe
+        return variable + "=" + value + os.linesep + "export " + variable + os.linesep
+
+def path_variable_setting_command(variable, paths):
+    """
+        Returns a command to sets a named shell path variable to the given NATIVE
+        paths on the current platform.
+    """
+    assert(isinstance(variable, str))
+    assert(isinstance(paths, list))
+    sep = os.path.pathsep
+    return variable_setting_command(variable, sep.join(paths))
+
+def prepend_path_variable_command(variable, paths):
+    """
+        Returns a command that prepends the given paths to the named path variable on
+        the current platform.
+    """
+    return path_variable_setting_command(variable,
+        paths + os.environ(variable).split(os.pathsep))
+
+def file_creation_command():
+    """
+        Return a command which can create a file. If 'r' is result of invocation, then
+        'r foobar' will create foobar with unspecified content. What happens if file
+        already exists is unspecified.
+    """
+    if os_name() == 'NT':
+        return "echo. > "
+    else:
+        return "touch "
+
+#FIXME: global variable
+__mkdir_set = set()
+__re_windows_drive = re.compile(r'^.*:\$')
+
+def mkdir(engine, target):
+    # If dir exists, do not update it. Do this even for $(DOT).
+    bjam.call('NOUPDATE', target)
+
+    global __mkdir_set
+
+    # FIXME: Where is DOT defined?
+    #if $(<) != $(DOT) && ! $($(<)-mkdir):
+    if target != '.' and target not in __mkdir_set:
+        # Cheesy gate to prevent multiple invocations on same dir.
+        __mkdir_set.add(target)
+
+        # Schedule the mkdir build action.
+        if os_name() == 'NT':
+            engine.set_update_action("common.MkDir1-quick-fix-for-windows", target, [], None)
+        else:
+            engine.set_update_action("common.MkDir1-quick-fix-for-unix", target, [], None)
+
+        # Prepare a Jam 'dirs' target that can be used to make the build only
+        # construct all the target directories.
+        engine.add_dependency('dirs', target)
+
+        # Recursively create parent directories. $(<:P) = $(<)'s parent & we
+        # recurse until root.
+
+        s = os.path.dirname(target)
+        if os_name() == 'NT':
+            if(__re_windows_drive.match(s)):
+                s = ''
+                
+        if s:
+            if s != target:
+                engine.add_dependency(target, s)
+                mkdir(engine, s)
+            else:
+                bjam.call('NOTFILE', s)
+
+__re_version = re.compile(r'^([^.]+)[.]([^.]+)[.]?([^.]*)')
+
+def format_name(format, name, target_type, prop_set):
+    """ Given a target, as given to a custom tag rule, returns a string formatted
+        according to the passed format. Format is a list of properties that is
+        represented in the result. For each element of format the corresponding target
+        information is obtained and added to the result string. For all, but the
+        literal, the format value is taken as the as string to prepend to the output
+        to join the item to the rest of the result. If not given "-" is used as a
+        joiner.
+
+        The format options can be:
+
+          <base>[joiner]
+              ::  The basename of the target name.
+          <toolset>[joiner]
+              ::  The abbreviated toolset tag being used to build the target.
+          <threading>[joiner]
+              ::  Indication of a multi-threaded build.
+          <runtime>[joiner]
+              ::  Collective tag of the build runtime.
+          <version:/version-feature | X.Y[.Z]/>[joiner]
+              ::  Short version tag taken from the given "version-feature"
+                  in the build properties. Or if not present, the literal
+                  value as the version number.
+          <property:/property-name/>[joiner]
+              ::  Direct lookup of the given property-name value in the
+                  build properties. /property-name/ is a regular expression.
+                  e.g. <property:toolset-.*:flavor> will match every toolset.
+          /otherwise/
+              ::  The literal value of the format argument.
+
+        For example this format:
+
+          boost_ <base> <toolset> <threading> <runtime> <version:boost-version>
+
+        Might return:
+
+          boost_thread-vc80-mt-gd-1_33.dll, or
+          boost_regex-vc80-gd-1_33.dll
+
+        The returned name also has the target type specific prefix and suffix which
+        puts it in a ready form to use as the value from a custom tag rule.
+    """
+    assert(isinstance(format, list))
+    assert(isinstance(name, str))
+    assert(isinstance(target_type, str) or not type)
+    # assert(isinstance(prop_set, property_set.PropertySet))
+    if type.is_derived(target_type, 'LIB'):
+        result = "" ;
+        for f in format:
+            grist = get_grist(f)
+            if grist == '<base>':
+                result += os.path.basename(name)
+            elif grist == '<toolset>':
+                result += join_tag(ungrist(f), 
+                    toolset_tag(name, target_type, prop_set))
+            elif grist == '<threading>':
+                result += join_tag(ungrist(f),
+                    threading_tag(name, target_type, prop_set))
+            elif grist == '<runtime>':
+                result += join_tag(ungrist(f),
+                    runtime_tag(name, target_type, prop_set))
+            elif grist.startswith('<version:'):
+                key = grist[len('<version:'):-1]
+                version = prop_set.get('<' + key + '>')
+                if not version:
+                    version = key
+                version = __re_version.match(version)
+                result += join_tag(ungrist(f), version[1] + '_' + version[2])
+            elif grist.startswith('<property:'):
+                key = grist[len('<property:'):-1]
+                property_re = re.compile('<(' + key + ')>')
+                p0 = None
+                for prop in prop_set.raw():
+                    match = property_re.match(prop)
+                    if match:
+                        p0 = match[1]
+                        break
+                if p0:
+                    p = prop_set.get('<' + p0 + '>')
+                    if p:
+                        assert(len(p) == 1)
+                        result += join_tag(ungrist(f), p)
+            else:
+                result += ungrist(f)
+
+        result = virtual_target.add_prefix_and_suffix(
+            ''.join(result), target_type, prop_set)
+        return result
+
+def join_tag(joiner, tag):
+    if not joiner: joiner = '-'
+    return joiner + tag
+
+__re_toolset_version = re.compile(r"<toolset.*version>(\d+)[.](\d*)")
+
+def toolset_tag(name, target_type, prop_set):
+    tag = ''
+
+    properties = prop_set.raw()
+    tools = prop_set.get('<toolset>')
+    assert(len(tools) == 0)
+    tools = tools[0]
+    if tools.startswith('borland'): tag += 'bcb'
+    elif tools.startswith('como'): tag += 'como'
+    elif tools.startswith('cw'): tag += 'cw'
+    elif tools.startswith('darwin'): tag += 'xgcc'
+    elif tools.startswith('edg'): tag += edg
+    elif tools.startswith('gcc'):
+        flavor = prop_set.get('<toolset-gcc:flavor>')
+        ''.find
+        if flavor.find('mingw') != -1:
+            tag += 'mgw'
+        else:
+            tag += 'gcc'
+    elif tools == 'intel':
+        if prop_set.get('<toolset-intel:platform>') == ['win']:
+            tag += 'iw'
+        else:
+            tag += 'il'
+    elif tools.startswith('kcc'): tag += 'kcc'
+    elif tools.startswith('kylix'): tag += 'bck'
+    #case metrowerks* : tag += cw ;
+    #case mingw* : tag += mgw ;
+    elif tools.startswith('mipspro'): tag += 'mp'
+    elif tools.startswith('msvc'): tag += 'vc'
+    elif tools.startswith('sun'): tag += 'sw'
+    elif tools.startswith('tru64cxx'): tag += 'tru'
+    elif tools.startswith('vacpp'): tag += 'xlc'
+
+    for prop in properties:
+        match = __re_toolset_version.match(prop)
+        if(match):
+            version = match
+            break
+    version_string = None
+    # For historical reasons, vc6.0 and vc7.0 use different naming.
+    if tag == 'vc':
+        if version.group(1) == '6':
+            # Cancel minor version.
+            version_string = '6'
+        elif version.group(1) == '7' and version.group(2) == '0':
+            version_string = '7'
+
+    # On intel, version is not added, because it does not matter and it's the
+    # version of vc used as backend that matters. Ideally, we'd encode the
+    # backend version but that would break compatibility with V1.
+    elif tag == 'iw':
+        version_string = ''
+
+    # On borland, version is not added for compatibility with V1.
+    elif tag == 'bcb':
+        version_string = ''
+
+    if version_string is None:
+        version = version.group(1) + version.group(2)
+
+    tag += version
+
+    return tag
+
+
+def threading_tag(name, target_type, prop_set):
+    tag = ''
+    properties = prop_set.raw()
+    if '<threading>multi' in properties: tag = 'mt'
+
+    return tag
+
+
+def runtime_tag(name, target_type, prop_set ):
+    tag = ''
+
+    properties = prop_set.raw()
+    if '<runtime-link>static' in properties: tag += 's'
+
+    # This is an ugly thing. In V1, there's a code to automatically detect which
+    # properties affect a target. So, if <runtime-debugging> does not affect gcc
+    # toolset, the tag rules won't even see <runtime-debugging>. Similar
+    # functionality in V2 is not implemented yet, so we just check for toolsets
+    # which are known to care about runtime debug.
+    if '<toolset>msvc' in properties \
+       or '<stdlib>stlport' in properties \
+       or '<toolset-intel:platform>win' in properties:
+        if '<runtime-debugging>on' in properties: tag += 'g'
+
+    if '<python-debugging>on' in properties: tag += 'y'
+    if '<variant>debug' in properties: tag += 'd'
+    if '<stdlib>stlport' in properties: tag += 'p'
+    if '<stdlib-stlport:iostream>hostios' in properties: tag += 'n'
+
+    return tag
+
+
+## TODO:
+##rule __test__ ( )
+##{
+##    import assert ;
+##
+##    local nl = "
+##" ;
+##
+##    local save-os = [ modules.peek os : .name ] ;
+##
+##    modules.poke os : .name : LINUX ;
+##
+##    assert.result "PATH=foo:bar:baz$(nl)export PATH$(nl)"
+##        : path-variable-setting-command PATH : foo bar baz ;
+##
+##    assert.result "PATH=foo:bar:$PATH$(nl)export PATH$(nl)"
+##        : prepend-path-variable-command PATH : foo bar ;
+##
+##    modules.poke os : .name : NT ;
+##
+##    assert.result "set PATH=foo;bar;baz$(nl)"
+##        : path-variable-setting-command PATH : foo bar baz ;
+##
+##    assert.result "set PATH=foo;bar;%PATH%$(nl)"
+##        : prepend-path-variable-command PATH : foo bar ;
+##
+##    modules.poke os : .name : $(save-os) ;
+##}
+
+def init(manager):
+    engine = manager.engine()
+
+    engine.register_action("common.MkDir1-quick-fix-for-unix", 'mkdir -p "$(<)"')
+    engine.register_action("common.MkDir1-quick-fix-for-windows", 'if not exist "$(<)\\" mkdir "$(<)"')
+
+    import b2.tools.make
+    import b2.build.alias
+
+    global __RM, __CP, __IGNORE, __LN
+    # ported from trunk_at_47281
+    if os_name() == 'NT':
+        __RM = 'del /f /q'
+        __CP = 'copy'
+        __IGNORE = '2>nul >nul & setlocal'
+        __LN = __CP
+        #if not __LN:
+        #    __LN = CP
+    else:
+        __RM = 'rm -f'
+        __CP = 'cp'
+        __IGNORE = ''
+        __LN = 'ln'
+        
+    engine.register_action("common.Clean", __RM + ' "$(>)"',
+                           flags=['piecemeal', 'together', 'existing'])
+    engine.register_action("common.copy", __CP + ' "$(>)" "$(<)"')
+    engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE,
+                           flags=['quietly', 'updated', 'piecemeal', 'together'])
+
+    engine.register_action("common.hard-link", 
+        __RM + ' "$(<)" 2$(NULL_OUT) $(NULL_OUT)' + os.linesep +
+        __LN + ' "$(>)" "$(<)" $(NULL_OUT)')
Added: trunk/tools/build/v2/tools/darwin.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/darwin.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,57 @@
+#  Copyright (C) Christopher Currie 2003. Permission to copy, use,
+#  modify, sell and distribute this software is granted provided this
+#  copyright notice appears in all copies. This software is provided
+# "as is" without express or implied warranty, and with no claim as to
+#  its suitability for any purpose.
+
+#  Please see http://article.gmane.org/gmane.comp.lib.boost.build/3389/
+#  for explanation why it's a separate toolset.
+
+import common, gcc, builtin
+from b2.build import feature, toolset, type, action, generators
+from b2.util.utility import *
+
+toolset.register ('darwin')
+
+toolset.inherit_generators ('darwin', [], 'gcc')
+toolset.inherit_flags ('darwin', 'gcc')
+toolset.inherit_rules ('darwin', 'gcc')
+
+def init (version = None, command = None, options = None):
+    options = to_seq (options)
+
+    condition = common.check_init_parameters ('darwin', None, ('version', version))
+    
+    command = common.get_invocation_command ('darwin', 'g++', command)
+
+    common.handle_options ('darwin', condition, command, options)
+    
+    gcc.init_link_flags ('darwin', 'darwin', condition)
+
+# Darwin has a different shared library suffix
+type.set_generated_target_suffix ('SHARED_LIB', ['<toolset>darwin'], 'dylib')
+
+# we need to be able to tell the type of .dylib files
+type.register_suffixes ('dylib', 'SHARED_LIB')
+
+feature.feature ('framework', [], ['free'])
+
+toolset.flags ('darwin.compile', 'OPTIONS', '<link>shared', ['-dynamic'])
+toolset.flags ('darwin.compile', 'OPTIONS', None, ['-Wno-long-double', '-no-cpp-precomp'])
+toolset.flags ('darwin.compile.c++', 'OPTIONS', None, ['-fcoalesce-templates'])
+
+toolset.flags ('darwin.link', 'FRAMEWORK', '<framework>')
+
+# This is flag is useful for debugging the link step
+# uncomment to see what libtool is doing under the hood
+# toolset.flags ('darwin.link.dll', 'OPTIONS', None, '[-Wl,-v'])
+
+action.register ('darwin.compile.cpp', None, ['$(CONFIG_COMMAND) $(ST_OPTIONS) -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) -framework$(_)$(FRAMEWORK) $(OPTIONS)'])
+
+# TODO: how to set 'bind LIBRARIES'?
+action.register ('darwin.link.dll', None, ['$(CONFIG_COMMAND) -dynamiclib -L"$(LINKPATH)" -o "$(<)" "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-SA) -l$(FINDLIBS-ST) -framework$(_)$(FRAMEWORK) $(OPTIONS)'])
+
+def darwin_archive (manager, targets, sources, properties):
+    pass
+
+action.register ('darwin.archive', darwin_archive, ['ar -c -r -s $(ARFLAGS) "$(<:T)" "$(>:T)"'])
Added: trunk/tools/build/v2/tools/gcc.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/gcc.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,796 @@
+# Status: being ported by Steven Watanabe
+# Base revision: 47077
+# TODO: common.jam needs to be ported
+# TODO: generators.jam needs to have register_c_compiler.
+#
+# Copyright 2001 David Abrahams.
+# Copyright 2002-2006 Rene Rivera.
+# Copyright 2002-2003 Vladimir Prus.
+#  Copyright (c) 2005 Reece H. Dunn.
+# Copyright 2006 Ilya Sokolov.
+# Copyright 2007 Roland Schwarz
+# Copyright 2007 Boris Gubenko.
+# Copyright 2008 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+#    (See accompanying file LICENSE_1_0.txt or copy at
+#          http://www.boost.org/LICENSE_1_0.txt)
+
+import os
+import subprocess
+import re
+
+import bjam
+
+from b2.tools import unix, common, rc, pch, builtin
+from b2.build import feature, type, toolset, generators
+from b2.util.utility import os_name, on_windows
+from b2.manager import get_manager
+from b2.build.generators import Generator
+from b2.build.toolset import flags
+from b2.util.utility import to_seq
+
+__debug = None
+
+def debug():
+    global __debug
+    if __debug is None:
+        __debug = "--debug-configuration" in bjam.variable("ARGV")        
+    return __debug
+
+feature.extend('toolset', ['gcc'])
+
+
+toolset.inherit_generators('gcc', [], 'unix', ['unix.link', 'unix.link.dll'])
+toolset.inherit_flags('gcc', 'unix')
+toolset.inherit_rules('gcc', 'unix')
+
+generators.override('gcc.prebuilt', 'builtin.prebuilt')
+generators.override('gcc.searched-lib-generator', 'searched-lib-generator')
+
+# Target naming is determined by types/lib.jam and the settings below this
+# comment.
+#
+# On *nix:
+#     libxxx.a     static library
+#     libxxx.so    shared library
+#
+# On windows (mingw):
+#     libxxx.lib   static library
+#     xxx.dll      DLL
+#     xxx.lib      import library
+#
+# On windows (cygwin) i.e. <target-os>cygwin
+#     libxxx.a     static library
+#     xxx.dll      DLL
+#     libxxx.dll.a import library
+#
+# Note: user can always override by using the <tag>@rule
+#       This settings have been choosen, so that mingw
+#       is in line with msvc naming conventions. For
+#       cygwin the cygwin naming convention has been choosen.
+
+# Make the "o" suffix used for gcc toolset on all
+# platforms
+type.set_generated_target_suffix('OBJ', ['<toolset>gcc'], 'o')
+type.set_generated_target_suffix('STATIC_LIB', ['<toolset>gcc', '<target-os>cygwin'], 'a')
+
+type.set_generated_target_suffix('IMPORT_LIB', ['<toolset>gcc', '<target-os>cygwin'], 'dll.a')
+type.set_generated_target_prefix('IMPORT_LIB', ['<toolset>gcc', '<target-os>cygwin'], 'lib')
+
+__machine_match = re.compile('^([^ ]+)')
+__version_match = re.compile('^([0-9.]+)')
+
+def init(version = None, command = None, options = None):
+    """
+        Initializes the gcc toolset for the given version. If necessary, command may
+        be used to specify where the compiler is located. The parameter 'options' is a
+        space-delimited list of options, each one specified as
+        <option-name>option-value. Valid option names are: cxxflags, linkflags and
+        linker-type. Accepted linker-type values are gnu, darwin, osf, hpux or sun
+        and the default value will be selected based on the current OS.
+        Example:
+          using gcc : 3.4 : : <cxxflags>foo <linkflags>bar <linker-type>sun ;
+    """
+
+    options = to_seq(options)
+    command = to_seq(command)
+
+    # Information about the gcc command...
+    #   The command.
+    command = to_seq(common.get_invocation_command('gcc', 'g++', command))
+    #   The root directory of the tool install.
+    root = feature.get_values('<root>', options) ;
+    #   The bin directory where to find the command to execute.
+    bin = None
+    #   The flavor of compiler.
+    flavor = feature.get_values('<flavor>', options)
+    #   Autodetect the root and bin dir if not given.
+    if command:
+        if not bin:
+            bin = common.get_absolute_tool_path(command[-1])
+        if not root:
+            root = os.path.dirname(bin)
+    #   Autodetect the version and flavor if not given.
+    if command:
+        machine_info = subprocess.Popen(command + ['-dumpmachine'], stdout=subprocess.PIPE).communicate()[0]
+        machine = __machine_match.search(machine_info).group(1)
+
+        version_info = subprocess.Popen(command + ['-dumpversion'], stdout=subprocess.PIPE).communicate()[0]
+        version = __version_match.search(version_info).group(1)
+        if not flavor and machine.find('mingw') != -1:
+            flavor = 'mingw'
+
+    condition = None
+    if flavor:
+        condition = common.check_init_parameters('gcc', None,
+            ('version', version),
+            ('flavor', flavor))
+    else:
+        condition = common.check_init_parameters('gcc', None,
+            ('version', version))
+
+    if command:
+        command = command[0]
+
+    common.handle_options('gcc', condition, command, options)
+
+    linker = feature.get_values('<linker-type>', options)
+    if not linker:
+        if os_name() == 'OSF':
+            linker = 'osf'
+        elif os_name() == 'HPUX':
+            linker = 'hpux' ;
+        else:
+            linker = 'gnu'
+
+    init_link_flags('gcc', linker, condition)
+
+    # If gcc is installed in non-standard location, we'd need to add
+    # LD_LIBRARY_PATH when running programs created with it (for unit-test/run
+    # rules).
+    if command:
+        # On multilib 64-bit boxes, there are both 32-bit and 64-bit libraries
+        # and all must be added to LD_LIBRARY_PATH. The linker will pick the
+        # right onces. Note that we don't provide a clean way to build 32-bit
+        # binary with 64-bit compiler, but user can always pass -m32 manually.
+        lib_path = [os.path.join(root, 'bin'),
+                    os.path.join(root, 'lib'),
+                    os.path.join(root, 'lib32'),
+                    os.path.join(root, 'lib64')]
+        if debug():
+            print 'notice: using gcc libraries ::', condition, '::', lib_path
+        toolset.flags('gcc.link', 'RUN_PATH', condition, lib_path)
+
+    # If it's not a system gcc install we should adjust the various programs as
+    # needed to prefer using the install specific versions. This is essential
+    # for correct use of MinGW and for cross-compiling.
+
+    # - The archive builder.
+    archiver = common.get_invocation_command('gcc',
+            'ar', feature.get_values('<archiver>', options), [bin], path_last=True)
+    toolset.flags('gcc.archive', '.AR', condition, [archiver])
+    if debug():
+        print 'notice: using gcc archiver ::', condition, '::', archiver
+
+    # - The resource compiler.
+    rc_command = common.get_invocation_command_nodefault('gcc',
+            'windres', feature.get_values('<rc>', options), [bin], path_last=True)
+    rc_type = feature.get_values('<rc-type>', options)
+
+    if not rc_type:
+        rc_type = 'windres'
+
+    if not rc_command:
+        # If we can't find an RC compiler we fallback to a null RC compiler that
+        # creates empty object files. This allows the same Jamfiles to work
+        # across the board. The null RC uses the assembler to create the empty
+        # objects, so configure that.
+        rc_command = common.get_invocation_command('gcc', 'as', [], [bin], path_last=True)
+        rc_type = 'null'
+    rc.configure(rc_command, condition, '<rc-type>' + rc_type)
+
+###if [ os.name ] = NT
+###{
+###    # This causes single-line command invocation to not go through .bat files,
+###    # thus avoiding command-line length limitations.
+###    JAMSHELL = % ;
+###}
+
+#FIXME: when register_c_compiler is moved to
+# generators, these should be updated
+builtin.register_c_compiler('gcc.compile.c++', ['CPP'], ['OBJ'], ['<toolset>gcc'])
+builtin.register_c_compiler('gcc.compile.c', ['C'], ['OBJ'], ['<toolset>gcc'])
+builtin.register_c_compiler('gcc.compile.asm', ['ASM'], ['OBJ'], ['<toolset>gcc'])
+
+# pch support
+
+# The compiler looks for a precompiled header in each directory just before it
+# looks for the include file in that directory. The name searched for is the
+# name specified in the #include directive with ".gch" suffix appended. The
+# logic in gcc-pch-generator will make sure that BASE_PCH suffix is appended to
+# full name of the header.
+
+type.set_generated_target_suffix('PCH', ['<toolset>gcc'], 'gch')
+
+# GCC-specific pch generator.
+class GccPchGenerator(pch.PchGenerator):
+
+    # Inherit the __init__ method
+
+    def run_pch(self, project, name, prop_set, sources):
+        # Find the header in sources. Ignore any CPP sources.
+        header = None
+        for s in sources:
+            if type.is_derived(s.type, 'H'):
+                header = s
+
+        # Error handling: Base header file name should be the same as the base
+        # precompiled header name.
+        header_name = header.name
+        header_basename = os.path.basename(header_name).rsplit('.', 1)[0]
+        if header_basename != name:
+            location = project.project_module
+            ###FIXME:
+            raise Exception()
+            ### errors.user-error "in" $(location)": pch target name `"$(name)"' should be the same as the base name of header file `"$(header-name)"'" ;
+
+        pch_file = Generator.run(self, project, name, prop_set, [header])
+
+        # return result of base class and pch-file property as usage-requirements
+        # FIXME: what about multiple results from generator.run?
+        return (property_set.create('<pch-file>' + pch_file[0], '<cflags>-Winvalid-pch'),
+                pch_file)
+
+    # Calls the base version specifying source's name as the name of the created
+    # target. As result, the PCH will be named whatever.hpp.gch, and not
+    # whatever.gch.
+    def generated_targets(self, sources, prop_set, project, name = None):
+        name = sources[0].name
+        return Generator.generated_targets(self, sources,
+            prop_set, project, name)
+
+# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The
+# latter have HPP type, but HPP type is derived from H. The type of compilation
+# is determined entirely by the destination type.
+generators.register(GccPchGenerator('gcc.compile.c.pch', False, ['H'], ['C_PCH'], ['<pch>on', '<toolset>gcc' ]))
+generators.register(GccPchGenerator('gcc.compile.c++.pch', False, ['H'], ['CPP_PCH'], ['<pch>on', '<toolset>gcc' ]))
+
+# Override default do-nothing generators.
+generators.override('gcc.compile.c.pch', 'pch.default-c-pch-generator')
+generators.override('gcc.compile.c++.pch', 'pch.default-cpp-pch-generator')
+
+flags('gcc.compile', 'PCH_FILE', ['<pch>on'], ['<pch-file>'])
+
+# Declare flags and action for compilation
+flags('gcc.compile', 'OPTIONS', ['<optimization>off'], ['-O0'])
+flags('gcc.compile', 'OPTIONS', ['<optimization>speed'], ['-O3'])
+flags('gcc.compile', 'OPTIONS', ['<optimization>space'], ['-Os'])
+
+flags('gcc.compile', 'OPTIONS', ['<inlining>off'], ['-fno-inline'])
+flags('gcc.compile', 'OPTIONS', ['<inlining>on'], ['-Wno-inline'])
+flags('gcc.compile', 'OPTIONS', ['<inlining>full'], ['-finline-functions', '-Wno-inline'])
+
+flags('gcc.compile', 'OPTIONS', ['<warnings>off'], ['-w'])
+flags('gcc.compile', 'OPTIONS', ['<warnings>on'], ['-Wall'])
+flags('gcc.compile', 'OPTIONS', ['<warnings>all'], ['-Wall', '-pedantic'])
+flags('gcc.compile', 'OPTIONS', ['<warnings-as-errors>on'], ['-Werror'])
+
+flags('gcc.compile', 'OPTIONS', ['<debug-symbols>on'], ['-g'])
+flags('gcc.compile', 'OPTIONS', ['<profiling>on'], ['-pg'])
+flags('gcc.compile', 'OPTIONS', ['<rtti>off'], ['-fno-rtti'])
+
+# On cygwin and mingw, gcc generates position independent code by default, and
+# warns if -fPIC is specified. This might not be the right way of checking if
+# we're using cygwin. For example, it's possible to run cygwin gcc from NT
+# shell, or using crosscompiling. But we'll solve that problem when it's time.
+# In that case we'll just add another parameter to 'init' and move this login
+# inside 'init'.
+if not os_name () in ['CYGWIN', 'NT']:
+    print "osname:", os_name()
+    # This logic will add -fPIC for all compilations:
+    #
+    # lib a : a.cpp b ;
+    # obj b : b.cpp ;
+    # exe c : c.cpp a d ;
+    # obj d : d.cpp ;
+    #
+    # This all is fine, except that 'd' will be compiled with -fPIC even though
+    # it's not needed, as 'd' is used only in exe. However, it's hard to detect
+    # where a target is going to be used. Alternative, we can set -fPIC only
+    # when main target type is LIB but than 'b' will be compiled without -fPIC.
+    # In x86-64 that will lead to link errors. So, compile everything with
+    # -fPIC.
+    #
+    # Yet another alternative would be to create propagated <sharedable>
+    # feature, and set it when building shared libraries, but that's hard to
+    # implement and will increase target path length even more.
+    flags('gcc.compile', 'OPTIONS', ['<link>shared'], ['-fPIC'])
+
+if os_name() != 'NT' and os_name() != 'OSF' and os_name() != 'HPUX':
+    # OSF does have an option called -soname but it doesn't seem to work as
+    # expected, therefore it has been disabled.
+    HAVE_SONAME   = ''
+    SONAME_OPTION = '-h'
+
+
+flags('gcc.compile', 'USER_OPTIONS', [], ['<cflags>'])
+flags('gcc.compile.c++', 'USER_OPTIONS',[], ['<cxxflags>'])
+flags('gcc.compile', 'DEFINES', [], ['<define>'])
+flags('gcc.compile', 'INCLUDES', [], ['<include>'])
+
+engine = get_manager().engine()
+
+engine.register_action('gcc.compile.c++.pch', 
+    '"$(CONFIG_COMMAND)" -x c++-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"')
+
+engine.register_action('gcc.compile.c.pch',
+    '"$(CONFIG_COMMAND)" -x c-header $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"')
+
+
+def gcc_compile_cpp(targets, sources, properties):
+    # Some extensions are compiled as C++ by default. For others, we need to
+    # pass -x c++. We could always pass -x c++ but distcc does not work with it.
+    extension = os.path.splitext (sources [0]) [1]
+    lang = ''
+    if not extension in ['.cc', '.cp', '.cxx', '.cpp', '.c++', '.C']:
+        lang = '-x c++'
+    get_manager().engine().set_target_variable (targets, 'LANG', lang)
+    engine.add_dependency(targets, bjam.call('get-target-variable', targets, 'PCH_FILE'))
+
+def gcc_compile_c(targets, sources, properties):
+    engine = get_manager().engine()
+    # If we use the name g++ then default file suffix -> language mapping does
+    # not work. So have to pass -x option. Maybe, we can work around this by
+    # allowing the user to specify both C and C++ compiler names.
+    #if $(>:S) != .c
+    #{
+    engine.set_target_variable (targets, 'LANG', '-x c')
+    #}
+    engine.add_dependency(targets, bjam.call('get-target-variable', targets, 'PCH_FILE'))
+    
+engine.register_action(
+    'gcc.compile.c++',
+    '"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-128 $(OPTIONS) ' +
+        '$(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" ' +
+        '-c -o "$(<:W)" "$(>:W)"',
+    function=gcc_compile_cpp,
+    bound_list=['PCH_FILE'])
+
+engine.register_action(
+    'gcc.compile.c',
+    '"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) ' +
+        '-I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)"',
+    function=gcc_compile_c,
+    bound_list=['PCH_FILE'])
+
+def gcc_compile_asm(targets, sources, properties):
+    get_manager().engine().set_target_variable(targets, 'LANG', '-x assembler-with-cpp')
+
+engine.register_action(
+    'gcc.compile.asm',
+    '"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"',
+    function=gcc_compile_asm)
+
+
+class GccLinkingGenerator(unix.UnixLinkingGenerator):
+    """
+        The class which check that we don't try to use the <runtime-link>static
+        property while creating or using shared library, since it's not supported by
+        gcc/libc.
+    """
+    def run(self, project, name, prop_set, sources):
+        # TODO: Replace this with the use of a target-os property.
+
+        no_static_link = False
+        if bjam.variable('UNIX'):
+            no_static_link = True;
+        ##FIXME: what does this mean?
+##        {
+##            switch [ modules.peek : JAMUNAME ]
+##            {
+##                case * : no-static-link = true ;
+##            }
+##        }
+
+        properties = prop_set.raw()
+        reason = None
+        if no_static_link and '<runtime-link>static' in properties:
+            if '<link>shared' in properties:
+                reason = "On gcc, DLL can't be build with '<runtime-link>static'."
+            elif type.is_derived(self.target_types[0], 'EXE'):
+                for s in sources:
+                    source_type = s.type()
+                    if source_type and type.is_derived(source_type, 'SHARED_LIB'):
+                        reason = "On gcc, using DLLS together with the " +\
+                                 "<runtime-link>static options is not possible "
+        if reason:
+            print 'warning:', reason
+            print 'warning:',\
+                "It is suggested to use '<runtime-link>static' together",\
+                "with '<link>static'." ;
+            return
+        else:
+            generated_targets = unix.UnixLinkingGenerator.run(self, project,
+                name, prop_set, sources)
+            return generated_targets
+
+if on_windows():
+    flags('gcc.link.dll', '.IMPLIB-COMMAND', [], ['-Wl,--out-implib,'])
+    generators.register(
+        GccLinkingGenerator('gcc.link', True,
+            ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'],
+            [ 'EXE' ],
+            [ '<toolset>gcc' ]))
+    generators.register(
+        GccLinkingGenerator('gcc.link.dll', True,
+            ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'],
+            ['IMPORT_LIB', 'SHARED_LIB'],
+            ['<toolset>gcc']))
+else:
+    generators.register(
+        GccLinkingGenerator('gcc.link', True,
+            ['LIB', 'OBJ'],
+            ['EXE'],
+            ['<toolset>gcc']))
+    generators.register(
+        GccLinkingGenerator('gcc.link.dll', True,
+            ['LIB', 'OBJ'],
+            ['SHARED_LIB'],
+            ['<toolset>gcc']))
+
+# Declare flags for linking.
+# First, the common flags.
+flags('gcc.link', 'OPTIONS', ['<debug-symbols>on'], ['-g'])
+flags('gcc.link', 'OPTIONS', ['<profiling>on'], ['-pg'])
+flags('gcc.link', 'USER_OPTIONS', [], ['<linkflags>'])
+flags('gcc.link', 'LINKPATH', [], ['<library-path>'])
+flags('gcc.link', 'FINDLIBS-ST', [], ['<find-static-library>'])
+flags('gcc.link', 'FINDLIBS-SA', [], ['<find-shared-library>'])
+flags('gcc.link', 'LIBRARIES', [], ['<library-file>'])
+
+# For <runtime-link>static we made sure there are no dynamic libraries in the
+# link. On HP-UX not all system libraries exist as archived libraries (for
+# example, there is no libunwind.a), so, on this platform, the -static option
+# cannot be specified.
+if os_name() != 'HPUX':
+    flags('gcc.link', 'OPTIONS', ['<runtime-link>static'], ['-static'])
+
+# Now, the vendor specific flags.
+# The parameter linker can be either gnu, darwin, osf, hpux or sun.
+def init_link_flags(toolset, linker, condition):
+    """
+        Now, the vendor specific flags.
+        The parameter linker can be either gnu, darwin, osf, hpux or sun.
+    """
+    toolset_link = toolset + '.link'
+    if linker == 'gnu':
+        # Strip the binary when no debugging is needed. We use --strip-all flag
+        # as opposed to -s since icc (intel's compiler) is generally
+        # option-compatible with and inherits from the gcc toolset, but does not
+        # support -s.
+
+        # FIXME: what does unchecked translate to?
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<debug-symbols>off', condition), ['-Wl,--strip-all'])  # : unchecked ;
+        flags(toolset_link, 'RPATH',       condition,                      ['<dll-path>'])       # : unchecked ;
+        flags(toolset_link, 'RPATH_LINK',  condition,                      ['<xdll-path>'])      # : unchecked ;
+        flags(toolset_link, 'START-GROUP', condition,                      ['-Wl,--start-group'])# : unchecked ;
+        flags(toolset_link, 'END-GROUP',   condition,                      ['-Wl,--end-group'])  # : unchecked ;
+
+        # gnu ld has the ability to change the search behaviour for libraries
+        # referenced by -l switch. These modifiers are -Bstatic and -Bdynamic
+        # and change search for -l switches that follow them. The following list
+        # shows the tried variants.
+        # The search stops at the first variant that has a match.
+        # *nix: -Bstatic -lxxx
+        #    libxxx.a
+        #
+        # *nix: -Bdynamic -lxxx
+        #    libxxx.so
+        #    libxxx.a
+        #
+        # windows (mingw,cygwin) -Bstatic -lxxx
+        #    libxxx.a
+        #    xxx.lib
+        #
+        # windows (mingw,cygwin) -Bdynamic -lxxx
+        #    libxxx.dll.a
+        #    xxx.dll.a
+        #    libxxx.a
+        #    xxx.lib
+        #    cygxxx.dll (*)
+        #    libxxx.dll
+        #    xxx.dll
+        #    libxxx.a
+        #
+        # (*) This is for cygwin
+        # Please note that -Bstatic and -Bdynamic are not a guarantee that a
+        # static or dynamic lib indeed gets linked in. The switches only change
+        # search patterns!
+
+        # On *nix mixing shared libs with static runtime is not a good idea.
+        flags(toolset_link, 'FINDLIBS-ST-PFX',
+              map(lambda x: x + '/<runtime-link>shared', condition),
+            ['-Wl,-Bstatic']) # : unchecked ;
+        flags(toolset_link, 'FINDLIBS-SA-PFX',
+              map(lambda x: x + '/<runtime-link>shared', condition),
+            ['-Wl,-Bdynamic']) # : unchecked ;
+
+        # On windows allow mixing of static and dynamic libs with static
+        # runtime.
+        flags(toolset_link, 'FINDLIBS-ST-PFX',
+              map(lambda x: x + '/<runtime-link>static/<target-os>windows', condition),
+              ['-Wl,-Bstatic']) # : unchecked ;
+        flags(toolset_link, 'FINDLIBS-SA-PFX',
+              map(lambda x: x + '/<runtime-link>static/<target-os>windows', condition),
+              ['-Wl,-Bdynamic']) # : unchecked ;
+        flags(toolset_link, 'OPTIONS',
+              map(lambda x: x + '/<runtime-link>static/<target-os>windows', condition),
+              ['-Wl,-Bstatic']) # : unchecked ;
+
+    elif linker == 'darwin':
+        # On Darwin, the -s option to ld does not work unless we pass -static,
+        # and passing -static unconditionally is a bad idea. So, don't pass -s.
+        # at all, darwin.jam will use separate 'strip' invocation.
+        flags(toolset_link, 'RPATH', condition, ['<dll-path>']) # : unchecked ;
+        flags(toolset_link, 'RPATH_LINK', condition, ['<xdll-path>']) # : unchecked ;
+
+    elif linker == 'osf':
+        # No --strip-all, just -s.
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<debug-symbols>off', condition), ['-Wl,-s'])
+            # : unchecked ;
+        flags(toolset_link, 'RPATH', condition, ['<dll-path>']) # : unchecked ;
+        # This does not supports -R.
+        flags(toolset_link, 'RPATH_OPTION', condition, ['-rpath']) # : unchecked ;
+        # -rpath-link is not supported at all.
+
+    elif linker == 'sun':
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<debug-symbols>off', condition), ['-Wl,-s'])
+            # : unchecked ;
+        flags(toolset_link, 'RPATH', condition, ['<dll-path>']) # : unchecked ;
+        # Solaris linker does not have a separate -rpath-link, but allows to use
+        # -L for the same purpose.
+        flags(toolset_link, 'LINKPATH', condition, ['<xdll-path>']) # : unchecked ;
+
+        # This permits shared libraries with non-PIC code on Solaris.
+        # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the
+        # following is not needed. Whether -fPIC should be hardcoded, is a
+        # separate question.
+        # AH, 2004/10/16: it is still necessary because some tests link against
+        # static libraries that were compiled without PIC.
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<link>shared', condition), ['-mimpure-text'])
+            # : unchecked ;
+
+    elif linker == 'hpux':
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<debug-symbols>off', condition),
+            ['-Wl,-s']) # : unchecked ;
+        flags(toolset_link, 'OPTIONS', map(lambda x: x + '/<link>shared', condition),
+            ['-fPIC']) # : unchecked ;
+
+    else:
+        # FIXME:
+        errors.user_error(
+        "$(toolset) initialization: invalid linker '$(linker)' " +
+        "The value '$(linker)' specified for <linker> is not recognized. " +
+        "Possible values are 'gnu', 'darwin', 'osf', 'hpux' or 'sun'")
+
+# Declare actions for linking.
+def gcc_link(targets, sources, properties):
+    engine = get_manager().engine()
+    engine.set_target_variable(targets, 'SPACE', ' ')
+    # Serialize execution of the 'link' action, since running N links in
+    # parallel is just slower. For now, serialize only gcc links, it might be a
+    # good idea to serialize all links.
+    engine.set_target_variable(targets, 'JAM_SEMAPHORE', '<s>gcc-link-semaphore')
+
+engine.register_action(
+    'gcc.link',
+    '"$(CONFIG_COMMAND)" -L"$(LINKPATH)" ' +
+        '-Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" ' +
+        '-Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" ' +
+        '$(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) ' +
+        '-l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) ' +
+        '$(OPTIONS) $(USER_OPTIONS)',
+    function=gcc_link,
+    bound_list=['LIBRARIES'])
+
+# Default value. Mostly for the sake of intel-linux that inherits from gcc, but
+# does not have the same logic to set the .AR variable. We can put the same
+# logic in intel-linux, but that's hardly worth the trouble as on Linux, 'ar' is
+# always available.
+__AR = 'ar'
+
+flags('gcc.archive', 'AROPTIONS', [], ['<archiveflags>'])
+
+def gcc_archive(targets, sources, properties):
+    # Always remove archive and start again. Here's rationale from
+    #
+    # Andre Hentz:
+    #
+    # I had a file, say a1.c, that was included into liba.a. I moved a1.c to
+    # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd
+    # errors. After some debugging I traced it back to the fact that a1.o was
+    # *still* in liba.a
+    #
+    # Rene Rivera:
+    #
+    # Originally removing the archive was done by splicing an RM onto the
+    # archive action. That makes archives fail to build on NT when they have
+    # many files because it will no longer execute the action directly and blow
+    # the line length limit. Instead we remove the file in a different action,
+    # just before building the archive.
+    clean = targets[0] + '(clean)'
+    bjam.call('TEMPORARY', clean)
+    bjam.call('NOCARE', clean)
+    engine = get_manager().engine()
+    engine.set_target_variable('LOCATE', clean, bjam.call('get-target-variable', targets, 'LOCATE'))
+    engine.add_dependency(clean, sources)
+    engine.add_dependency(targets, clean)
+    engine.set_update_action('common.RmTemps', clean, targets, None)
+
+# Declare action for creating static libraries.
+# The letter 'r' means to add files to the archive with replacement. Since we
+# remove archive, we don't care about replacement, but there's no option "add
+# without replacement".
+# The letter 'c' suppresses the warning in case the archive does not exists yet.
+# That warning is produced only on some platforms, for whatever reasons.
+engine.register_action('gcc.archive',
+                       '"$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)"',
+                       function=gcc_archive,
+                       flags=['piecemeal'])
+
+def gcc_link_dll(targets, sources, properties):
+    engine = get_manager().engine()
+    engine.set_target_variable(targets, 'SPACE', ' ')
+    engine.set_target_variable(targets, 'JAM_SEMAPHORE', '<s>gcc-link-semaphore')
+
+engine.register_action(
+    'gcc.link.dll',
+    # Differ from 'link' above only by -shared.
+    '"$(CONFIG_COMMAND)" -L"$(LINKPATH)" ' +
+        '-Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,"$(RPATH)" ' +
+        '"$(.IMPLIB-COMMAND)$(<[1])" -o "$(<[-1])" ' +
+        '$(HAVE_SONAME)-Wl,$(SONAME_OPTION)$(SPACE)-Wl,$(<[-1]:D=) ' +
+        '-shared $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) ' +
+        '-l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) ' +
+        '$(OPTIONS) $(USER_OPTIONS)',
+    function = gcc_link_dll,
+    bound_list=['LIBRARIES'])
+
+# Set up threading support. It's somewhat contrived, so perform it at the end,
+# to avoid cluttering other code.
+
+if on_windows():
+    flags('gcc', 'OPTIONS', ['<threading>multi'], ['-mthreads'])
+elif bjam.variable('UNIX'):
+    jamuname = bjam.variable('JAMUNAME')
+    host_os_name = jamuname[0]
+    if host_os_name.startswith('SunOS'):
+        flags('gcc', 'OPTIONS', ['<threading>multi'], ['-pthreads'])
+        flags('gcc', 'FINDLIBS-SA', [], ['rt'])
+    elif host_os_name == 'BeOS':
+        # BeOS has no threading options, don't set anything here.
+        pass
+    elif host_os_name.endswith('BSD'):
+        flags('gcc', 'OPTIONS', ['<threading>multi'], ['-pthread'])
+        # there is no -lrt on BSD
+    elif host_os_name == 'DragonFly':
+        flags('gcc', 'OPTIONS', ['<threading>multi'], ['-pthread'])
+        # there is no -lrt on BSD - DragonFly is a FreeBSD variant,
+        # which anoyingly doesn't say it's a *BSD.
+    elif host_os_name == 'IRIX':
+        # gcc on IRIX does not support multi-threading, don't set anything here.
+        pass
+    elif host_os_name == 'Darwin':
+        # Darwin has no threading options, don't set anything here.
+        pass
+    else:
+        flags('gcc', 'OPTIONS', ['<threading>multi'], ['-pthread'])
+        flags('gcc', 'FINDLIBS-SA', [], ['rt'])
+
+def cpu_flags(toolset, variable, architecture, instruction_set, values, default=None):
+    #FIXME: for some reason this fails.  Probably out of date feature code
+##    if default:
+##        flags(toolset, variable,
+##              ['<architecture>' + architecture + '/<instruction-set>'],
+##              values)
+    flags(toolset, variable,
+          #FIXME: same as above
+          [##'<architecture>/<instruction-set>' + instruction_set,
+           '<architecture>' + architecture + '/<instruction-set>' + instruction_set],
+          values)
+
+# Set architecture/instruction-set options.
+#
+# x86 and compatible
+flags('gcc', 'OPTIONS', ['<architecture>x86/<address-model>32'], ['-m32'])
+flags('gcc', 'OPTIONS', ['<architecture>x86/<address-model>64'], ['-m64'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'i386', ['-march=i386'], default=True)
+cpu_flags('gcc', 'OPTIONS', 'x86', 'i486', ['-march=i486'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'i586', ['-march=i586'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'i686', ['-march=i686'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium', ['-march=pentium'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium-mmx', ['-march=pentium-mmx'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentiumpro', ['-march=pentiumpro'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium2', ['-march=pentium2'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium3', ['-march=pentium3'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium3m', ['-march=pentium3m'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium-m', ['-march=pentium-m'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium4', ['-march=pentium4'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'pentium4m', ['-march=pentium4m'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'prescott', ['-march=prescott'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'nocona', ['-march=nocona'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'k6', ['-march=k6'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'k6-2', ['-march=k6-2'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'k6-3', ['-march=k6-3'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon', ['-march=athlon'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-tbird', ['-march=athlon-tbird'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-4', ['-march=athlon-4'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-xp', ['-march=athlon-xp'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-mp', ['-march=athlon-mp'])
+##
+cpu_flags('gcc', 'OPTIONS', 'x86', 'k8', ['-march=k8'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'opteron', ['-march=opteron'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon64', ['-march=athlon64'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'athlon-fx', ['-march=athlon-fx'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'winchip-c6', ['-march=winchip-c6'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'winchip2', ['-march=winchip2'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'c3', ['-march=c3'])
+cpu_flags('gcc', 'OPTIONS', 'x86', 'c3-2', ['-march=c3-2'])
+# Sparc
+flags('gcc', 'OPTIONS', ['<architecture>sparc/<address-model>32'], ['-m32'])
+flags('gcc', 'OPTIONS', ['<architecture>sparc/<address-model>64'], ['-m64'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'c3', ['-mcpu=c3'], default=True)
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'v7', ['-mcpu=v7'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'cypress', ['-mcpu=cypress'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'v8', ['-mcpu=v8'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'supersparc', ['-mcpu=supersparc'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclite', ['-mcpu=sparclite'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'hypersparc', ['-mcpu=hypersparc'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclite86x', ['-mcpu=sparclite86x'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'f930', ['-mcpu=f930'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'f934', ['-mcpu=f934'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'sparclet', ['-mcpu=sparclet'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'tsc701', ['-mcpu=tsc701'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'v9', ['-mcpu=v9'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'ultrasparc', ['-mcpu=ultrasparc'])
+cpu_flags('gcc', 'OPTIONS', 'sparc', 'ultrasparc3', ['-mcpu=ultrasparc3'])
+# RS/6000 & PowerPC
+flags('gcc', 'OPTIONS', ['<architecture>power/<address-model>32'], ['-m32'])
+flags('gcc', 'OPTIONS', ['<architecture>power/<address-model>64'], ['-m64'])
+cpu_flags('gcc', 'OPTIONS', 'power', '403', ['-mcpu=403'])
+cpu_flags('gcc', 'OPTIONS', 'power', '505', ['-mcpu=505'])
+cpu_flags('gcc', 'OPTIONS', 'power', '601', ['-mcpu=601'])
+cpu_flags('gcc', 'OPTIONS', 'power', '602', ['-mcpu=602'])
+cpu_flags('gcc', 'OPTIONS', 'power', '603', ['-mcpu=603'])
+cpu_flags('gcc', 'OPTIONS', 'power', '603e', ['-mcpu=603e'])
+cpu_flags('gcc', 'OPTIONS', 'power', '604', ['-mcpu=604'])
+cpu_flags('gcc', 'OPTIONS', 'power', '604e', ['-mcpu=604e'])
+cpu_flags('gcc', 'OPTIONS', 'power', '620', ['-mcpu=620'])
+cpu_flags('gcc', 'OPTIONS', 'power', '630', ['-mcpu=630'])
+cpu_flags('gcc', 'OPTIONS', 'power', '740', ['-mcpu=740'])
+cpu_flags('gcc', 'OPTIONS', 'power', '7400', ['-mcpu=7400'])
+cpu_flags('gcc', 'OPTIONS', 'power', '7450', ['-mcpu=7450'])
+cpu_flags('gcc', 'OPTIONS', 'power', '750', ['-mcpu=750'])
+cpu_flags('gcc', 'OPTIONS', 'power', '801', ['-mcpu=801'])
+cpu_flags('gcc', 'OPTIONS', 'power', '821', ['-mcpu=821'])
+cpu_flags('gcc', 'OPTIONS', 'power', '823', ['-mcpu=823'])
+cpu_flags('gcc', 'OPTIONS', 'power', '860', ['-mcpu=860'])
+cpu_flags('gcc', 'OPTIONS', 'power', '970', ['-mcpu=970'])
+cpu_flags('gcc', 'OPTIONS', 'power', '8540', ['-mcpu=8540'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'power', ['-mcpu=power'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'power2', ['-mcpu=power2'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'power3', ['-mcpu=power3'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'power4', ['-mcpu=power4'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'power5', ['-mcpu=power5'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'powerpc', ['-mcpu=powerpc'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'powerpc64', ['-mcpu=powerpc64'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'rios', ['-mcpu=rios'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'rios1', ['-mcpu=rios1'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'rios2', ['-mcpu=rios2'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'rsc', ['-mcpu=rsc'])
+cpu_flags('gcc', 'OPTIONS', 'power', 'rs64a', ['-mcpu=rs64'])
+# AIX variant of RS/6000 & PowerPC
+flags('gcc', 'OPTIONS', ['<architecture>power/<address-model>32/<target-os>aix'], ['-maix32'])
+flags('gcc', 'OPTIONS', ['<architecture>power/<address-model>64/<target-os>aix'], ['-maix64'])
+flags('gcc', 'AROPTIONS', ['<architecture>power/<address-model>64/<target-os>aix'], ['-X 64'])
Added: trunk/tools/build/v2/tools/make.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/make.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,55 @@
+# Status: being ported by Vladimir Prus
+
+# Copyright 2003 Dave Abrahams 
+# Copyright 2003 Douglas Gregor 
+# Copyright 2006 Rene Rivera 
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 
+# Distributed under the Boost Software License, Version 1.0. 
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 
+
+#  This module defines the 'make' main target rule.
+
+from b2.build.targets import BasicTarget
+from b2.build.virtual_target import Action, FileTarget
+from b2.build import type
+from b2.manager import get_manager
+import b2.build.property_set
+
+class MakeTarget(BasicTarget):
+  
+    def construct(self, name, source_targets, property_set):
+
+        action_name = property_set.get("<action>")[0]
+
+        action = Action(get_manager(), source_targets, action_name, property_set)
+        # FIXME: type.type uses global data.
+        target = FileTarget(self.name(), 1, type.type(self.name()),
+                            self.project(), action)    
+        return [ b2.build.property_set.empty(),
+                 [self.project().manager().virtual_targets().register(target)]]
+
+def make (target_name, sources, generating_rule,
+          requirements=None, usage_requirements=None):
+
+    target_name = target_name[0]
+    generating_rule = generating_rule[0]
+
+    if not requirements:
+        requirements = []
+
+    requirements.append("<action>%s" % generating_rule)
+    m = get_manager()
+    targets = m.targets()
+    project = m.projects().current()
+    engine = m.engine()
+    engine.register_bjam_action(generating_rule)
+
+    targets.main_target_alternative(MakeTarget(
+        target_name, project,
+        targets.main_target_sources(sources, target_name),
+        targets.main_target_requirements(requirements, project),
+        targets.main_target_default_build([], project),
+        targets.main_target_usage_requirements(usage_requirements or [], project)))
+
+get_manager().projects().add_rule("make", make)
+
Added: trunk/tools/build/v2/tools/pch.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/pch.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,83 @@
+# Status: Being ported by Steven Watanabe
+# Base revision: 47077
+#
+# Copyright (c) 2005 Reece H. Dunn.
+# Copyright 2006 Ilya Sokolov
+# Copyright (c) 2008 Steven Watanabe
+#
+# Use, modification and distribution is subject to the Boost Software
+# License Version 1.0. (See accompanying file LICENSE_1_0.txt or
+# http://www.boost.org/LICENSE_1_0.txt)
+
+##### Using Precompiled Headers (Quick Guide) #####
+#
+# Make precompiled mypch.hpp:
+#
+#    import pch ;
+#
+#    cpp-pch mypch
+#      : # sources
+#        mypch.hpp
+#      : # requiremnts
+#        <toolset>msvc:<source>mypch.cpp
+#      ;
+#
+# Add cpp-pch to sources:
+#
+#    exe hello
+#      : main.cpp hello.cpp mypch
+#      ;
+
+from b2.build import type, feature, generators
+
+type.register('PCH', ['pch'])
+type.register('C_PCH', [], 'PCH')
+type.register('CPP_PCH', [], 'PCH')
+
+# Control precompiled header (PCH) generation.
+feature.feature('pch',
+                ['on', 'off'],
+                ['propagated'])
+
+feature.feature('pch-header', [], ['free', 'dependency'])
+feature.feature('pch-file', [], ['free', 'dependency'])
+
+class PchGenerator(generators.Generator):
+    """
+        Base PCH generator. The 'run' method has the logic to prevent this generator
+        from being run unless it's being used for a top-level PCH target.
+    """
+    def action_class(self):
+        return 'compile-action'
+
+    def run(self, project, name, prop_set, sources):
+        if not name:
+            # Unless this generator is invoked as the top-most generator for a
+            # main target, fail. This allows using 'H' type as input type for
+            # this generator, while preventing Boost.Build to try this generator
+            # when not explicitly asked for.
+            #
+            # One bad example is msvc, where pch generator produces both PCH
+            # target and OBJ target, so if there's any header generated (like by
+            # bison, or by msidl), we'd try to use pch generator to get OBJ from
+            # that H, which is completely wrong. By restricting this generator
+            # only to pch main target, such problem is solved.
+            pass
+        else:
+            r = self.run_pch(project, name,
+                 prop_set.add_raw('<define>BOOST_BUILD_PCH_ENABLED'),
+                 sources)
+            return generators.add_usage_requirements(
+                r, ['<define>BOOST_BUILD_PCH_ENABLED'])
+
+    # This rule must be overridden by the derived classes.
+    def run_pch(self, project, name, prop_set, sources):
+        pass
+
+#FIXME: dummy-generator in builtins.jam needs to be ported.
+# NOTE: requirements are empty, default pch generator can be applied when
+# pch=off.
+###generators.register(
+###    [ new dummy-generator pch.default-c-pch-generator   : :   C_PCH ] ;
+###generators.register
+###    [ new dummy-generator pch.default-cpp-pch-generator : : CPP_PCH ] ;
Added: trunk/tools/build/v2/tools/rc.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/rc.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,189 @@
+# Status: being ported by Steven Watanabe
+# Base revision: 47077
+#
+#  Copyright (C) Andre Hentz 2003. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+#  
+#  Copyright (c) 2006 Rene Rivera.
+#
+#  Copyright (c) 2008 Steven Watanabe
+#
+#  Use, modification and distribution is subject to the Boost Software
+#  License Version 1.0. (See accompanying file LICENSE_1_0.txt or
+#  http://www.boost.org/LICENSE_1_0.txt)
+
+##import type ;
+##import generators ;
+##import feature ;
+##import errors ;
+##import scanner ;
+##import toolset : flags ;
+
+from b2.build import type, toolset, generators, scanner, feature
+from b2.tools import builtin
+from b2.util import regex
+from b2.build.toolset import flags
+from b2.manager import get_manager
+
+__debug = None
+
+def debug():
+    global __debug
+    if __debug is None:
+        __debug = "--debug-configuration" in bjam.variable("ARGV")        
+    return __debug
+
+type.register('RC', ['rc'])
+
+def init():
+    pass
+
+def configure (command = None, condition = None, options = None):
+    """
+        Configures a new resource compilation command specific to a condition,
+        usually a toolset selection condition. The possible options are:
+        
+            * <rc-type>(rc|windres) - Indicates the type of options the command
+              accepts.
+        
+        Even though the arguments are all optional, only when a command, condition,
+        and at minimum the rc-type option are given will the command be configured.
+        This is so that callers don't have to check auto-configuration values
+        before calling this. And still get the functionality of build failures when
+        the resource compiler can't be found.
+    """
+    rc_type = feature.get_values('<rc-type>', options)
+    if rc_type:
+        assert(len(rc_type) == 1)
+        rc_type = rc_type[0]
+
+    if command and condition and rc_type:
+        flags('rc.compile.resource', '.RC', condition, command)
+        flags('rc.compile.resource', '.RC_TYPE', condition, rc_type.lower())
+        flags('rc.compile.resource', 'DEFINES', [], ['<define>'])
+        flags('rc.compile.resource', 'INCLUDES', [], ['<include>'])
+        if debug():
+            print 'notice: using rc compiler ::', condition, '::', command
+
+engine = get_manager().engine()
+
+class RCAction:
+    """Class representing bjam action defined from Python.
+    The function must register the action to execute."""
+    
+    def __init__(self, action_name, function):
+        self.action_name = action_name
+        self.function = function
+            
+    def __call__(self, targets, sources, property_set):
+        if self.function:
+            self.function(targets, sources, property_set)
+
+# FIXME: What is the proper way to dispatch actions?
+def rc_register_action(action_name, function = None):
+    global engine
+    if engine.actions.has_key(action_name):
+        raise "Bjam action %s is already defined" % action_name
+    engine.actions[action_name] = RCAction(action_name, function)
+
+def rc_compile_resource(targets, sources, properties):
+    rc_type = bjam.call('get-target-variable', targets, '.RC_TYPE')
+    global engine
+    engine.set_update_action('rc.compile.resource.' + rc_type, targets, sources, properties)
+
+rc_register_action('rc.compile.resource', rc_compile_resource)
+
+
+engine.register_action(
+    'rc.compile.resource.rc',
+    '"$(.RC)" -l 0x409 "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -fo "$(<)" "$(>)"')
+
+engine.register_action(
+    'rc.compile.resource.windres',
+    '"$(.RC)" "-U$(UNDEFS)" "-D$(DEFINES)" -I"$(>:D)" -I"$(<:D)" -I"$(INCLUDES)" -o "$(<)" -i "$(>)"')
+
+# FIXME: this was originally declared quietly
+engine.register_action(
+    'compile.resource.null',
+    'as /dev/null -o "$(<)"')
+
+# Since it's a common practice to write
+# exe hello : hello.cpp hello.rc
+# we change the name of object created from RC file, to
+# avoid conflict with hello.cpp.
+# The reason we generate OBJ and not RES, is that gcc does not
+# seem to like RES files, but works OK with OBJ.
+# See http://article.gmane.org/gmane.comp.lib.boost.build/5643/
+#
+# Using 'register-c-compiler' adds the build directory to INCLUDES
+# FIXME: switch to generators
+builtin.register_c_compiler('rc.compile.resource', ['RC'], ['OBJ(%_res)'], [])
+
+__angle_include_re = "#include[ ]*<([^<]+)>"
+
+# Register scanner for resources
+class ResScanner(scanner.Scanner):
+    
+    def __init__(self, includes):
+        scanner.__init__ ;
+        self.includes = includes
+
+    def pattern(self):
+        return "(([^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\
+               "[ ]+([^ \"]+|\"[^\"]+\"))|(#include[ ]*(<[^<]+>|\"[^\"]+\")))" ;
+
+    def process(self, target, matches, binding):
+
+        angle = regex.transform(matches, "#include[ ]*<([^<]+)>")
+        quoted = regex.transform(matches, "#include[ ]*\"([^\"]+)\"")
+        res = regex.transform(matches,
+                              "[^ ]+[ ]+(BITMAP|CURSOR|FONT|ICON|MESSAGETABLE|RT_MANIFEST)" +\
+                              "[ ]+(([^ \"]+)|\"([^\"]+)\")", [3, 4])
+
+        # Icons and other includes may referenced as 
+        #
+        # IDR_MAINFRAME ICON "res\\icon.ico"
+        #
+        # so we have to replace double backslashes to single ones.
+        res = [ re.sub(r'\\\\', '/', match) for match in res ]
+
+        # CONSIDER: the new scoping rule seem to defeat "on target" variables.
+        g = bjam.call('get-target-variable', target, 'HDRGRIST')
+        b = os.path.normalize_path(os.path.dirname(binding))
+
+        # Attach binding of including file to included targets.
+        # When target is directly created from virtual target
+        # this extra information is unnecessary. But in other
+        # cases, it allows to distinguish between two headers of the 
+        # same name included from different places.      
+        # We don't need this extra information for angle includes,
+        # since they should not depend on including file (we can't
+        # get literal "." in include path).
+        g2 = g + "#" + b
+       
+        g = "<" + g + ">"
+        g2 = "<" + g2 + ">"
+        angle = [g + x for x in angle]
+        quoted = [g2 + x for x in quoted]
+        res = [g2 + x for x in res]
+        
+        all = angle + quoted
+
+        bjam.call('mark-included', target, all)
+
+        engine = get_manager().engine()
+
+        engine.add_dependency(target, res)
+        bjam.call('NOCARE', all + res)
+        engine.set_target_variable(angle, 'SEARCH', ungrist(self.includes))
+        engine.set_target_variable(quoted, 'SEARCH', b + ungrist(self.includes))
+        engine.set_target_variable(res, 'SEARCH', b + ungrist(self.includes)) ;
+        
+        # Just propagate current scanner to includes, in a hope
+        # that includes do not change scanners.
+        get_manager().scanners().propagate(self, angle + quoted)
+
+scanner.register(ResScanner, 'include')
+type.set_scanner('RC', ResScanner)
Added: trunk/tools/build/v2/tools/types/__init__.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/__init__.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,18 @@
+__all__ = [
+    'asm',
+    'cpp',
+    'exe',
+    'html',
+    'lib',
+    'obj',
+    'rsp',
+]
+
+def register_all ():
+    for i in __all__:
+        m = __import__ (__name__ + '.' + i)
+        reg = i + '.register ()'
+        #exec (reg)
+
+# TODO: (PF) I thought these would be imported automatically. Anyone knows why they aren't?
+register_all ()
Added: trunk/tools/build/v2/tools/types/asm.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/asm.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,13 @@
+# Copyright Craig Rodrigues 2005.
+# Copyright (c) 2008 Steven Watanabe
+#
+# Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register():
+    type.register_type('ASM', ['s', 'S', 'asm'])
+
+register()
Added: trunk/tools/build/v2/tools/types/cpp.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/cpp.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,10 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    type.register_type ('CPP', ['cpp', 'cxx', 'cc'])
+
+register ()
Added: trunk/tools/build/v2/tools/types/exe.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/exe.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,11 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    type.register_type ('EXE', ['exe'], None, ['NT', 'CYGWIN'])
+    type.register_type ('EXE', [], None, [])
+
+register ()
Added: trunk/tools/build/v2/tools/types/html.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/html.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,10 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    type.register_type ('HTML', ['html'])
+
+register ()
Added: trunk/tools/build/v2/tools/types/lib.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/lib.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,23 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    
+    if not type.registered ('LIB'):
+        type.register ('LIB')
+    
+    type.register_type ('STATIC_LIB', ['lib', 'a'], 'LIB', ['NT', 'CYGWIN'])
+    type.register_type ('STATIC_LIB', ['a'], 'LIB')
+    
+    type.register_type ('IMPORT_LIB', [], 'STATIC_LIB')
+    type.set_generated_target_suffix ('IMPORT_LIB', [], 'lib')
+    
+    type.register_type ('SHARED_LIB', ['dll'], 'LIB', ['NT', 'CYGWIN'])
+    type.register_type ('SHARED_LIB', ['so'], 'LIB')
+    
+    type.register_type ('SEARCHED_LIB', [], 'LIB')
+
+register ()
Added: trunk/tools/build/v2/tools/types/obj.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/obj.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,11 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    type.register_type ('OBJ', ['obj'], None, ['NT', 'CYGWIN'])
+    type.register_type ('OBJ', ['o'])
+
+register ()
Added: trunk/tools/build/v2/tools/types/rsp.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/types/rsp.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,10 @@
+# Copyright David Abrahams 2004. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+from b2.build import type
+
+def register ():
+    type.register_type ('RSP', ['rsp'])
+
+register ()
Added: trunk/tools/build/v2/tools/unix.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/unix.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,150 @@
+#  Copyright (c) 2004 Vladimir Prus.
+#
+#  Use, modification and distribution is subject to the Boost Software
+#  License Version 1.0. (See accompanying file LICENSE_1_0.txt or
+#  http://www.boost.org/LICENSE_1_0.txt)
+
+""" This file implements linking semantics common to all unixes. On unix, static
+    libraries must be specified in a fixed order on the linker command line. Generators
+    declared there store information about the order and use it properly.
+"""
+
+import builtin
+from b2.build import generators, type
+from b2.util.utility import *
+from b2.util import set, sequence
+
+class UnixLinkingGenerator (builtin.LinkingGenerator):
+    
+    def __init__ (self, id, composing, source_types, target_types, requirements):
+        builtin.LinkingGenerator.__init__ (self, id, composing, source_types, target_types, requirements)
+    
+    def run (self, project, name, prop_set, sources):
+        result = builtin.LinkingGenerator.run (self, project, name, prop_set, sources)
+        if result:
+            set_library_order (project.manager (), sources, prop_set, result [1])
+                                
+        return result
+    
+    def generated_targets (self, sources, prop_set, project, name):
+        sources2 = []
+        libraries = []
+        for l in sources:
+            if type.is_derived (l.type (), 'LIB'):
+                libraries.append (l)
+
+            else:
+                sources2.append (l)
+        
+        sources = sources2 + order_libraries (libraries)
+        
+        return builtin.LinkingGenerator.generated_targets (self, sources, prop_set, project, name)
+
+
+class UnixArchiveGenerator (builtin.ArchiveGenerator):
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        builtin.ArchiveGenerator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+        
+    def run (self, project, name, prop_set, sources):
+        result = builtin.ArchiveGenerator.run(self, project, name, prop_set, sources)
+        set_library_order(project.manager(), sources, prop_set, result)
+        return result
+
+class UnixSearchedLibGenerator (builtin.SearchedLibGenerator):
+    
+    def __init__ (self):
+        builtin.SearchedLibGenerator.__init__ (self)
+    
+    def optional_properties (self):
+        return self.requirements ()
+              
+    def run (self, project, name, prop_set, sources, multiple):
+        result = SearchedLibGenerator.run (project, name, prop_set, sources, multiple)
+        
+        set_library_order (sources, prop_set, result)
+        
+        return result
+
+class UnixPrebuiltLibGenerator (generators.Generator):
+    def __init__ (self, id, composing, source_types, target_types_and_names, requirements):
+        generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
+
+    def run (self, project, name, prop_set, sources, multiple):
+        f = prop_set.get ('<file>')
+        set_library_order_aux (f, sources)
+        return (f, sources)
+
+### # The derived toolset must specify their own rules and actions.
+# FIXME: restore?
+# action.register ('unix.prebuilt', None, None)
+
+
+generators.register (UnixPrebuiltLibGenerator ('unix.prebuilt', False, [], ['LIB'], ['<file>', '<toolset>unix']))
+
+
+
+
+
+### # Declare generators
+### generators.register [ new UnixLinkingGenerator unix.link : LIB OBJ : EXE 
+###     : <toolset>unix ] ;
+generators.register (UnixArchiveGenerator ('unix.archive', True, ['OBJ'], ['STATIC_LIB'], ['<toolset>unix']))
+
+### generators.register [ new UnixLinkingGenerator unix.link.dll : LIB OBJ : SHARED_LIB 
+###     : <toolset>unix ] ;
+### 
+### generators.register [ new UnixSearchedLibGenerator 
+###    unix.SearchedLibGenerator : : SEARCHED_LIB : <toolset>unix ] ;
+### 
+### 
+### # The derived toolset must specify their own actions.
+### actions link {
+### }
+### 
+### actions link.dll {
+### }
+
+def unix_archive (manager, targets, sources, properties):
+    pass
+
+# FIXME: restore?
+#action.register ('unix.archive', unix_archive, [''])
+
+### actions searched-lib-generator {    
+### }
+### 
+### actions prebuilt {
+### }
+
+
+from b2.util.order import Order
+__order = Order ()
+
+def set_library_order_aux (from_libs, to_libs):
+    for f in from_libs:
+        for t in to_libs:
+            if f != t:
+                __order.add_pair (f, t)
+
+def set_library_order (manager, sources, prop_set, result):
+    used_libraries = []
+    deps = prop_set.dependency ()
+    
+    [ sources.append (manager.get_object (get_value (x))) for x in deps ]
+    sources = sequence.unique (sources)
+
+    for l in sources:
+        if l.type () and type.is_derived (l.type (), 'LIB'):
+            used_libraries.append (l)
+
+    created_libraries = []
+    for l in result:
+        if l.type () and type.is_derived (l.type (), 'LIB'):
+            created_libraries.append (l)
+    
+    created_libraries = set.difference (created_libraries, used_libraries)
+    set_library_order_aux (created_libraries, used_libraries)
+
+def order_libraries (libraries):
+    return __order.order (libraries)
+     
Added: trunk/tools/build/v2/util/__init__.py
==============================================================================
Added: trunk/tools/build/v2/util/logger.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/logger.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,46 @@
+# Copyright Pedro Ferreira 2005. Distributed under the Boost
+# Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+import sys
+
+class NullLogger:
+    def __init__ (self):
+        self.indent_ = ''
+    
+    def log (self, source_name, *args):
+        if self.on () and self.interesting (source_name):
+            self.do_log (self.indent_)
+            for i in args:
+                self.do_log (i)
+            self.do_log ('\n')
+    
+    def increase_indent (self):
+        if self.on ():
+            self.indent_ += '    '
+        
+    def decrease_indent (self):
+        if self.on () and len (self.indent_) > 4:
+            self.indent_ = self.indent_ [-4:]
+
+    def do_log (self, *args):
+        pass
+    
+    def interesting (self, source_name):
+        return False
+
+    def on (self):
+        return False
+
+class TextLogger (NullLogger):
+    def __init__ (self):
+        NullLogger.__init__ (self)
+    
+    def do_log (self, arg):
+            sys.stdout.write (str (arg))
+    
+    def interesting (self, source_name):
+        return True
+
+    def on (self):
+        return True
Added: trunk/tools/build/v2/util/order.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/order.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,121 @@
+#  Copyright (C) 2003 Vladimir Prus
+#  Use, modification, and distribution is subject to the Boost Software
+#  License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+#  at http://www.boost.org/LICENSE_1_0.txt)
+
+class Order:
+    """Allows ordering arbitrary objects with regard to arbitrary binary relation.
+
+        The primary use case is the gcc toolset, which is sensitive to
+        library order: if library 'a' uses symbols from library 'b',
+        then 'a' must be present before 'b' on the linker's command line.
+        
+        This requirement can be lifted for gcc with GNU ld, but for gcc with
+        Solaris LD (and for Solaris toolset as well), the order always matters.
+        
+        So, we need to store order requirements and then order libraries
+        according to them. It it not possible to use dependency graph as
+        order requirements. What we need is "use symbols" relationship
+        while dependency graph provides "needs to be updated" relationship.
+        
+        For example::
+          lib a : a.cpp b;
+          lib b ;
+        
+        For static linking, the 'a' library need not depend on 'b'. However, it
+        still should come before 'b' on the command line.
+    """
+
+    def __init__ (self):
+        self.constraints_ = []
+        
+    def add_pair (self, first, second):
+        """ Adds the constraint that 'first' should precede 'second'.
+        """
+        self.constraints_.append ((first, second))
+
+    def order (self, objects):
+        """ Given a list of objects, reorder them so that the constains specified
+            by 'add_pair' are satisfied.
+            
+            The algorithm was adopted from an awk script by Nikita Youshchenko
+            (yoush at cs dot msu dot su)
+        """
+        # The algorithm used is the same is standard transitive closure,
+        # except that we're not keeping in-degree for all vertices, but
+        # rather removing edges.
+        result = []
+
+        if not objects: 
+            return result
+
+        constraints = self.__eliminate_unused_constraits (objects)
+        
+        # Find some library that nobody depends upon and add it to
+        # the 'result' array.
+        obj = None
+        while objects:
+            new_objects = []
+            while objects:
+                obj = objects [0]
+
+                if self.__has_no_dependents (obj, constraints):
+                    # Emulate break ;
+                    new_objects.extend (objects [1:])
+                    objects = []
+
+                else:
+                    new_objects.append (obj)
+                    obj = None
+                    objects = objects [1:]
+            
+            if not obj:
+                raise BaseException ("Circular order dependencies")
+
+            # No problem with placing first.
+            result.append (obj)
+
+            # Remove all containts where 'obj' comes first,
+            # since they are already satisfied.
+            constraints = self.__remove_satisfied (constraints, obj)
+
+            # Add the remaining objects for further processing
+            # on the next iteration
+            objects = new_objects
+            
+        return result
+
+    def __eliminate_unused_constraits (self, objects):
+        """ Eliminate constraints which mention objects not in 'objects'.
+            In graph-theory terms, this is finding subgraph induced by
+            ordered vertices.
+        """
+        result = []
+        for c in self.constraints_:
+            if c [0] in objects and c [1] in objects:
+                result.append (c)
+
+        return result
+    
+    def __has_no_dependents (self, obj, constraints):
+        """ Returns true if there's no constraint in 'constraints' where 
+            'obj' comes second.
+        """
+        failed = False
+        while constraints and not failed:
+            c = constraints [0]
+
+            if c [1] == obj:
+                failed = True
+
+            constraints = constraints [1:]
+
+        return not failed
+    
+    def __remove_satisfied (self, constraints, obj):
+        result = []
+        for c in constraints:
+            if c [0] != obj:
+                result.append (c)
+
+        return result
Added: trunk/tools/build/v2/util/path.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/path.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,922 @@
+# Status: this module is ported on demand by however needs something
+# from it.  Functionality that is not needed by Python port will
+# be dropped.
+
+#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+#  Performs various path manipulations. Path are always in a 'normilized' 
+#  representation. In it, a path may be either:
+#
+#     - '.', or
+#
+#     - ['/'] [ ( '..' '/' )*  (token '/')* token ]
+# 
+#   In plain english, path can be rooted, '..' elements are allowed only
+#   at the beginning, and it never ends in slash, except for path consisting
+#   of slash only.
+
+import os.path
+from utility import to_seq
+from glob import glob as builtin_glob
+
+def root (path, root):
+    """ If 'path' is relative, it is rooted at 'root'. Otherwise, it's unchanged.
+    """
+    if os.path.isabs (path):
+        return path
+    else:
+        return os.path.join (root, path)
+
+def make (native):
+    """ Converts the native path into normalized form.
+    """
+    # TODO: make os selection here.
+    return make_UNIX (native)
+
+def make_UNIX (native):
+
+    # VP: I have no idea now 'native' can be empty here! But it can!
+    assert (native)
+
+    return os.path.normpath (native)
+
+def native (path):
+    """ Builds a native representation of the path.
+    """
+    # TODO: make os selection here.
+    return native_UNIX (path)
+
+def native_UNIX (path):
+    return path
+
+
+def pwd ():
+    """ Returns the current working directory.
+        # TODO: is it a good idea to use the current dir? Some use-cases 
+                may not allow us to depend on the current dir.
+    """
+    return make (os.getcwd ())
+
+def is_rooted (path):
+    """ Tests if a path is rooted.
+    """
+    return path and path [0] == '/'
+
+
+###################################################################
+# Still to port.
+# Original lines are prefixed with "#   "
+#
+#   #  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
+#   #  distribute this software is granted provided this copyright notice appears in
+#   #  all copies. This software is provided "as is" without express or implied
+#   #  warranty, and with no claim as to its suitability for any purpose.
+#   
+#   #  Performs various path manipulations. Path are always in a 'normilized' 
+#   #  representation. In it, a path may be either:
+#   #
+#   #     - '.', or
+#   #
+#   #     - ['/'] [ ( '..' '/' )*  (token '/')* token ]
+#   # 
+#   #   In plain english, path can be rooted, '..' elements are allowed only
+#   #   at the beginning, and it never ends in slash, except for path consisting
+#   #   of slash only.
+#   
+#   import modules ;
+#   import sequence ;
+#   import regex ;
+#   import errors : error ;
+#   
+#   
+#   os = [ modules.peek : OS ] ;
+#   if [ modules.peek : UNIX ] 
+#   {    
+#       local uname = [ modules.peek : JAMUNAME ] ;
+#       switch $(uname)
+#       {
+#           case CYGWIN* :
+#             os = CYGWIN ;
+#           
+#           case * :
+#             os = UNIX ;
+#       }        
+#   }
+#   
+#   #
+#   #    Tests if a path is rooted.
+#   #
+#   rule is-rooted ( path )
+#   {
+#       return [ MATCH "^(/)" : $(path) ] ;
+#   }
+#   
+#   #
+#   #    Tests if a path has a parent.
+#   #
+#   rule has-parent ( path )
+#   {
+#       if $(path) != / {
+#           return 1 ;
+#       } else {
+#           return ;
+#       }
+#   }
+#   
+#   #
+#   #    Returns the path without any directory components.
+#   #
+#   rule basename ( path )
+#   {
+#       return [ MATCH "([^/]+)$" : $(path) ] ;
+#   }
+#   
+#   #
+#   #    Returns parent directory of the path. If no parent exists, error is issued.
+#   #
+#   rule parent ( path )
+#   {
+#       if [ has-parent $(path) ] {
+#   
+#           if $(path) = . {
+#               return .. ;
+#           } else {
+#   
+#               # Strip everything at the end of path up to and including
+#               # the last slash
+#               local result = [ regex.match "((.*)/)?([^/]+)" : $(path) : 2 3 ] ;
+#   
+#               # Did we strip what we shouldn't?
+#               if $(result[2]) = ".." {
+#                   return $(path)/.. ;
+#               } else {
+#                   if ! $(result[1]) {
+#                       if [ is-rooted $(path) ] {
+#                           result = / ;
+#                       } else {
+#                           result = . ;
+#                       }
+#                   }
+#                   return $(result[1]) ;
+#               }
+#           }
+#       } else {
+#           error "Path '$(path)' has no parent" ;
+#       }
+#   }
+#   
+#   #
+#   #    Returns path2 such that "[ join path path2 ] = .".
+#   #    The path may not contain ".." element or be rooted.
+#   #
+#   rule reverse ( path )
+#   {
+#       if $(path) = .
+#       {
+#           return $(path) ;
+#       }
+#       else
+#       {
+#           local tokens = [ regex.split $(path) "/" ] ;
+#           local tokens2 ;
+#           for local i in $(tokens) {
+#               tokens2 += .. ;
+#           }
+#           return [ sequence.join $(tokens2) : "/" ] ;
+#       }
+#   }
+#   
+#   #
+#   # Auxillary rule: does all the semantic of 'join', except for error cheching.
+#   # The error checking is separated because this rule is recursive, and I don't
+#   # like the idea of checking the same input over and over.
+#   #
+#   local rule join-imp ( elements + )
+#   {
+#       return [ NORMALIZE_PATH $(elements:J="/") ] ;
+#   }
+#   
+#   #
+#   #    Contanenates the passed path elements. Generates an error if
+#   #    any element other than the first one is rooted.
+#   #
+#   rule join ( elements + )
+#   {
+#       if ! $(elements[2]) 
+#       {
+#           return $(elements[1]) ;
+#       }
+#       else
+#       {        
+#           for local e in $(elements[2-])
+#           {
+#               if [ is-rooted $(e) ]
+#               {
+#                   error only first element may be rooted ;
+#               }
+#           }
+#           return [ join-imp $(elements) ] ;
+#       }    
+#   }
+
+
+def glob (dirs, patterns):
+    """ Returns the list of files matching the given pattern in the
+    specified directory.  Both directories and patterns are 
+    supplied as portable paths. Each pattern should be non-absolute
+    path, and can't contain "." or ".." elements. Each slash separated
+    element of pattern can contain the following special characters:
+    -  '?', which match any character
+    -  '*', which matches arbitrary number of characters.
+    A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3
+    if and only if e1 matches p1, e2 matches p2 and so on.
+    
+    For example: 
+        [ glob . : *.cpp ] 
+        [ glob . : */build/Jamfile ] 
+    """
+#   {
+#       local result ;
+#       if $(patterns:D)
+#       {
+#           # When a pattern has a directory element, we first glob for
+#           # directory, and then glob for file name is the found directories.
+#           for local p in $(patterns)
+#           {
+#               # First glob for directory part.
+#               local globbed-dirs = [ glob $(dirs) : $(p:D) ] ;
+#               result += [ glob $(globbed-dirs) : $(p:D="") ] ;
+#           }        
+#       }
+#       else
+#       {        
+#           # When a pattern has not directory, we glob directly.
+#           # Take care of special ".." value. The "GLOB" rule simply ignores
+#           # the ".." element (and ".") element in directory listings. This is
+#           # needed so that 
+#           #
+#           #    [ glob libs/*/Jamfile ]
+#           #
+#           # don't return 
+#           #
+#           #    libs/../Jamfile (which is the same as ./Jamfile)
+#           #
+#           # On the other hand, when ".." is explicitly present in the pattern
+#           # we need to return it.
+#           # 
+#           for local dir in $(dirs)
+#           {
+#               for local p in $(patterns)
+#               {                
+#                   if $(p) != ".."
+#                   {                
+#                       result += [ sequence.transform make 
+#                           : [ GLOB [ native $(dir) ] : $(p) ] ] ;
+#                   }            
+#                   else
+#                   {
+#                       result += [ path.join $(dir) .. ] ;
+#                   }            
+#               }            
+#           }
+#       }    
+#       return $(result) ;
+#   }
+#   
+
+# TODO: (PF) I replaced the code above by this. I think it should work but needs to be tested.
+    result = []
+    dirs = to_seq (dirs)
+    patterns = to_seq (patterns)
+
+    splitdirs = []
+    for dir in dirs:
+        splitdirs += dir.split (os.pathsep)
+
+    for dir in splitdirs:
+        for pattern in patterns:
+            p = os.path.join (dir, pattern)
+            import glob
+            result.extend (glob.glob (p))
+    return result
+    
+#   #
+#   #    Returns true is the specified file exists.
+#   #
+#   rule exists ( file )
+#   {
+#       return [ path.glob $(file:D) : $(file:D=) ] ;
+#   }
+#   NATIVE_RULE path : exists ;
+#   
+#   
+#   
+#   #
+#   #   Find out the absolute name of path and returns the list of all the parents,
+#   #   starting with the immediate one. Parents are returned as relative names.
+#   #   If 'upper_limit' is specified, directories above it will be pruned.
+#   #
+#   rule all-parents ( path : upper_limit ? : cwd ? )
+#   {
+#       cwd ?= [ pwd ] ;
+#       local path_ele = [ regex.split [ root $(path) $(cwd) ] "/" ] ;
+#   
+#       if ! $(upper_limit) {
+#           upper_limit = / ;
+#       }
+#       local upper_ele = [ regex.split [ root $(upper_limit) $(cwd) ] "/" ] ;
+#   
+#       # Leave only elements in 'path_ele' below 'upper_ele'
+#       while $(path_ele) && $(upper_ele[1]) = $(path_ele[1]) {
+#           upper_ele = $(upper_ele[2-]) ;
+#           path_ele = $(path_ele[2-]) ;
+#       }
+#       
+#       # All upper elements removed ?
+#       if ! $(upper_ele) {
+#           # Create the relative paths to parents, number of elements in 'path_ele'
+#           local result ;
+#           for local i in $(path_ele) {
+#               path = [ parent $(path) ] ;
+#               result += $(path) ;
+#           }
+#           return $(result) ;
+#       }
+#       else {
+#           error "$(upper_limit) is not prefix of $(path)" ;
+#       }
+#   }
+#   
+#   
+#   #
+#   #  Search for 'pattern' in parent directories of 'dir', up till and including
+#   #  'upper_limit', if it is specified, or till the filesystem root otherwise.
+#   #
+#   rule glob-in-parents ( dir : patterns + : upper-limit ? )
+#   {
+#       local result ;
+#       local parent-dirs = [ all-parents $(dir) : $(upper-limit) ] ;
+#   
+#       while $(parent-dirs) && ! $(result)
+#       {
+#           result = [ glob $(parent-dirs[1]) : $(patterns) ] ;
+#           parent-dirs = $(parent-dirs[2-]) ;
+#       }
+#       return $(result) ;    
+#   }
+#   
+#   #
+#   # Assuming 'child' is a subdirectory of 'parent', return the relative
+#   # path from 'parent' to 'child'
+#   #
+#   rule relative ( child parent )
+#   {
+#       if $(parent) = "." 
+#       {
+#           return $(child) ;
+#       }
+#       else 
+#       {       
+#           local split1 = [ regex.split $(parent) / ] ;
+#           local split2 = [ regex.split $(child) / ] ;
+#       
+#           while $(split1)
+#           {
+#               if $(split1[1]) = $(split2[1])
+#               {
+#                   split1 = $(split1[2-]) ;
+#                   split2 = $(split2[2-]) ;
+#               }
+#               else
+#               {
+#                   errors.error $(child) is not a subdir of $(parent) ;
+#               }                
+#           }    
+#           return [ join $(split2) ] ;    
+#       }    
+#   }
+#   
+#   # Returns the minimal path to path2 that is relative path1.
+#   #
+#   rule relative-to ( path1 path2 )
+#   {
+#       local root_1 = [ regex.split [ reverse $(path1) ] / ] ;
+#       local split1 = [ regex.split $(path1) / ] ;
+#       local split2 = [ regex.split $(path2) / ] ;
+#   
+#       while $(split1) && $(root_1)
+#       {
+#           if $(split1[1]) = $(split2[1])
+#           {
+#               root_1 = $(root_1[2-]) ;
+#               split1 = $(split1[2-]) ;
+#               split2 = $(split2[2-]) ;
+#           }
+#           else
+#           {
+#               split1 = ;
+#           }
+#       }
+#       return [ join . $(root_1) $(split2) ] ;
+#   }
+
+# Returns the list of paths which are used by the operating system
+# for looking up programs
+def programs_path ():
+    raw = []
+    names = ['PATH', 'Path', 'path']
+    
+    for name in names:
+        raw.append(os.environ.get (name, ''))
+    
+    result = []
+    for elem in raw:
+        if elem:
+            for p in elem.split(os.path.pathsep):
+                result.append(make(p))
+
+    return result
+
+#   rule make-NT ( native )
+#   {
+#       local tokens = [ regex.split $(native) "[/\\]" ] ;
+#       local result ;
+#   
+#       # Handle paths ending with slashes
+#       if $(tokens[-1]) = ""
+#       {
+#           tokens = $(tokens[1--2]) ; # discard the empty element
+#       }
+#   
+#       result = [ path.join $(tokens) ] ;
+#   
+#       if [ regex.match "(^.:)" : $(native)  ]
+#       {
+#           result = /$(result) ;
+#       }
+#       
+#       if $(native) = ""
+#       {
+#           result = "." ;
+#       }
+#           
+#       return $(result) ;
+#   }
+#   
+#   rule native-NT ( path )
+#   {
+#       local result = [ MATCH "^/?(.*)" : $(path) ] ;
+#       result = [ sequence.join [ regex.split $(result) "/" ] : "\\" ] ;
+#       return $(result) ;
+#   }
+#   
+#   rule make-CYGWIN ( path )
+#   {
+#       return [ make-NT $(path) ] ;
+#   }
+#   
+#   rule native-CYGWIN ( path )
+#   {
+#       local result = $(path) ;
+#       if [ regex.match "(^/.:)" : $(path)  ] # win absolute
+#       {
+#           result = [ MATCH "^/?(.*)" : $(path) ] ; # remove leading '/'
+#       }
+#       return [ native-UNIX $(result) ] ;
+#   }
+#   
+#   #
+#   # split-VMS: splits input native path into
+#   # device dir file (each part is optional),
+#   # example:
+#   #
+#   # dev:[dir]file.c => dev: [dir] file.c
+#   #
+#   rule split-path-VMS ( native )
+#   {
+#       local matches = [ MATCH ([a-zA-Z0-9_-]+:)?(\\[[^\]]*\\])?(.*)?$   : $(native) ] ;
+#       local device = $(matches[1]) ;
+#       local dir = $(matches[2]) ;
+#       local file = $(matches[3]) ;
+#   
+#       return $(device) $(dir) $(file) ;
+#   }
+#   
+#   #
+#   # Converts a native VMS path into a portable path spec.
+#   #
+#   # Does not handle current-device absolute paths such
+#   # as "[dir]File.c" as it is not clear how to represent
+#   # them in the portable path notation.
+#   #
+#   # Adds a trailing dot (".") to the file part if no extension
+#   # is present (helps when converting it back into native path).
+#   #
+#   rule make-VMS ( native )
+#   {
+#       if [ MATCH ^(\\[[a-zA-Z0-9]) : $(native) ]
+#       {
+#           errors.error "Can't handle default-device absolute paths: " $(native) ;
+#       }
+#           
+#       local parts = [ split-path-VMS $(native) ] ;
+#       local device = $(parts[1]) ;
+#       local dir = $(parts[2]) ;
+#       local file = $(parts[3]) ;
+#       local elems ;
+#       
+#       if $(device)
+#       {
+#           #
+#           # rooted
+#           #
+#           elems = /$(device) ;
+#       }
+#       
+#       if $(dir) = "[]"
+#       {
+#           #
+#           # Special case: current directory
+#           #
+#           elems = $(elems) "." ;
+#       }
+#       else if $(dir)
+#       {
+#           dir = [ regex.replace $(dir) "\\[|\\]" "" ] ;
+#           local dir_parts = [ regex.split $(dir) \\. ]  ;
+#       
+#           if $(dir_parts[1]) = ""
+#           {
+#               #
+#               # Relative path
+#               #
+#               dir_parts = $(dir_parts[2--1]) ;
+#           }
+#           
+#           #
+#           # replace "parent-directory" parts (- => ..)
+#           #
+#           dir_parts = [ regex.replace-list $(dir_parts) : - : .. ] ;
+#           
+#           elems = $(elems) $(dir_parts) ;
+#       }
+#       
+#       if $(file)
+#       {
+#           if ! [ MATCH (\\.) : $(file) ]
+#           {
+#               #
+#               # Always add "." to end of non-extension file
+#               #
+#               file = $(file). ;
+#           }
+#           elems = $(elems) $(file) ;
+#       }
+#   
+#       local portable = [ path.join $(elems) ] ;
+#   
+#       return $(portable) ;
+#   }
+#   
+#   #
+#   # Converts a portable path spec into a native VMS path.
+#   #
+#   # Relies on having at least one dot (".") included in the file
+#   # name to be able to differentiate it ftom the directory part.
+#   #
+#   rule native-VMS ( path )
+#   {
+#       local device = "" ;
+#       local dir = $(path) ;
+#       local file = "" ;
+#       local native ;
+#       local split ;
+#   
+#       #
+#       # Has device ?
+#       #
+#       if [ is-rooted $(dir) ]
+#       {
+#           split = [ MATCH ^/([^:]+:)/?(.*) : $(dir) ] ;
+#           device = $(split[1]) ;
+#           dir = $(split[2]) ;
+#       }
+#   
+#       #
+#       # Has file ?
+#       #
+#       # This is no exact science, just guess work:
+#       #
+#       # If the last part of the current path spec
+#       # includes some chars, followed by a dot,
+#       # optionally followed by more chars -
+#       # then it is a file (keep your fingers crossed).
+#       #
+#       split = [ regex.split $(dir) / ] ;
+#       local maybe_file = $(split[-1]) ;
+#   
+#       if [ MATCH ^([^.]+\\..*) : $(maybe_file) ]
+#       {
+#           file = $(maybe_file) ;
+#           dir = [ sequence.join $(split[1--2]) : / ] ;
+#       }
+#       
+#       #
+#       # Has dir spec ?
+#       #
+#       if $(dir) = "."
+#       {
+#           dir = "[]" ;
+#       }
+#       else if $(dir)
+#       {
+#           dir = [ regex.replace $(dir) \\.\\. - ] ;
+#           dir = [ regex.replace $(dir) / . ] ;
+#   
+#           if $(device) = ""
+#           {
+#               #
+#               # Relative directory
+#               # 
+#               dir = "."$(dir) ;
+#           }
+#           dir = "["$(dir)"]" ;
+#       }
+#       
+#       native = [ sequence.join $(device) $(dir) $(file) ] ;
+#   
+#       return $(native) ;
+#   }
+#   
+#   
+#   rule __test__ ( ) {
+#   
+#       import assert ;
+#       import errors : try catch ;
+#   
+#       assert.true is-rooted "/" ;
+#       assert.true is-rooted "/foo" ;
+#       assert.true is-rooted "/foo/bar" ;
+#       assert.result : is-rooted "." ;
+#       assert.result : is-rooted "foo" ;
+#       assert.result : is-rooted "foo/bar" ;
+#   
+#       assert.true has-parent "foo" ;
+#       assert.true has-parent "foo/bar" ;
+#       assert.true has-parent "." ;
+#       assert.result : has-parent "/" ;
+#   
+#       assert.result "." : basename "." ;
+#       assert.result ".." : basename ".." ;
+#       assert.result "foo" : basename "foo" ;
+#       assert.result "foo" : basename "bar/foo" ;
+#       assert.result "foo" : basename "gaz/bar/foo" ;
+#       assert.result "foo" : basename "/gaz/bar/foo" ;
+#   
+#       assert.result "." : parent "foo" ;
+#       assert.result "/" : parent "/foo" ;
+#       assert.result "foo/bar" : parent "foo/bar/giz" ;
+#       assert.result ".." : parent "." ;
+#       assert.result ".." : parent "../foo" ;
+#       assert.result "../../foo" : parent "../../foo/bar" ;
+#   
+#   
+#       assert.result "." : reverse "." ;
+#       assert.result ".." : reverse "foo" ;
+#       assert.result "../../.." : reverse "foo/bar/giz" ;
+#   
+#       assert.result "foo" : join "foo" ;
+#       assert.result "/foo" : join "/" "foo" ;
+#       assert.result "foo/bar" : join "foo" "bar" ;
+#       assert.result "foo/bar" : join "foo/giz" "../bar" ;
+#       assert.result "foo/giz" : join "foo/bar/baz" "../../giz" ;
+#       assert.result ".." : join "." ".." ;
+#       assert.result ".." : join "foo" "../.." ;
+#       assert.result "../.." : join "../foo" "../.." ;
+#       assert.result "/foo" : join "/bar" "../foo" ;
+#       assert.result "foo/giz" : join "foo/giz" "." ;
+#       assert.result "." : join lib2 ".." ;
+#       assert.result "/" : join "/a" ".." ;
+#   
+#       assert.result /a/b : join /a/b/c .. ;
+#   
+#       assert.result "foo/bar/giz" : join "foo" "bar" "giz" ;
+#       assert.result "giz" : join "foo" ".." "giz" ;
+#       assert.result "foo/giz" : join "foo" "." "giz" ;
+#   
+#       try ;
+#       {
+#           join "a" "/b" ;
+#       }
+#       catch only first element may be rooted ;
+#   
+#       local CWD = "/home/ghost/build" ;
+#       assert.result : all-parents . : . : $(CWD) ;
+#       assert.result . .. ../.. ../../..  : all-parents "Jamfile" : "" : $(CWD) ;
+#       assert.result foo . .. ../.. ../../.. : all-parents "foo/Jamfile" : "" : $(CWD) ;
+#       assert.result ../Work .. ../.. ../../.. : all-parents "../Work/Jamfile" : "" : $(CWD) ;
+#   
+#       local CWD = "/home/ghost" ;
+#       assert.result . .. : all-parents "Jamfile" : "/home" : $(CWD) ;
+#       assert.result . : all-parents "Jamfile" : "/home/ghost" : $(CWD) ;
+#       
+#       assert.result "c/d" : relative "a/b/c/d" "a/b" ;
+#       assert.result "foo" : relative "foo" "." ;
+#   
+#       local save-os = [ modules.peek path : os ] ;
+#       modules.poke path : os : NT ;
+#   
+#       assert.result "foo/bar/giz" : make "foo/bar/giz" ;
+#       assert.result "foo/bar/giz" : make "foo\\bar\\giz" ;
+#       assert.result "foo" : make "foo/." ;
+#       assert.result "foo" : make "foo/bar/.." ;
+#       assert.result "/D:/My Documents" : make "D:\\My Documents" ;
+#       assert.result "/c:/boost/tools/build/new/project.jam" : make "c:\\boost\\tools\\build\\test\\..\\new\\project.jam" ;
+#   
+#       assert.result "foo\\bar\\giz" : native "foo/bar/giz" ;
+#       assert.result "foo" : native "foo" ;
+#       assert.result "D:\\My Documents\\Work" : native "/D:/My Documents/Work" ;
+#   
+#       modules.poke path : os : UNIX ;
+#   
+#       assert.result "foo/bar/giz" : make "foo/bar/giz" ;
+#       assert.result "/sub1" : make "/sub1/." ;
+#       assert.result "/sub1" : make "/sub1/sub2/.." ;    
+#       assert.result "sub1" : make "sub1/." ;
+#       assert.result "sub1" : make "sub1/sub2/.." ;
+#       assert.result "/foo/bar" : native "/foo/bar" ;
+#   
+#       modules.poke path : os : VMS ;
+#   
+#       #
+#       # Don't really need to poke os before these
+#       #
+#       assert.result "disk:" "[dir]"  "file" : split-path-VMS "disk:[dir]file" ;
+#       assert.result "disk:" "[dir]"  ""     : split-path-VMS "disk:[dir]" ;
+#       assert.result "disk:" ""     ""       : split-path-VMS "disk:" ;
+#       assert.result "disk:" ""     "file"   : split-path-VMS "disk:file" ;
+#       assert.result ""      "[dir]"  "file" : split-path-VMS "[dir]file" ;
+#       assert.result ""      "[dir]"  ""     : split-path-VMS "[dir]" ;
+#       assert.result ""      ""     "file"   : split-path-VMS "file" ;
+#       assert.result ""      ""     ""       : split-path-VMS "" ;
+#   
+#       #
+#       # Special case: current directory
+#       #
+#       assert.result ""      "[]"     ""     : split-path-VMS "[]" ;
+#       assert.result "disk:" "[]"     ""     : split-path-VMS "disk:[]" ;
+#       assert.result ""      "[]"     "file" : split-path-VMS "[]file" ;
+#       assert.result "disk:" "[]"     "file" : split-path-VMS "disk:[]file" ;
+#   
+#       #
+#       # Make portable paths
+#       #
+#       assert.result "/disk:" : make "disk:" ;
+#       assert.result "foo/bar/giz" : make "[.foo.bar.giz]" ;
+#       assert.result "foo" : make "[.foo]" ;
+#       assert.result "foo" : make "[.foo.bar.-]" ;
+#       assert.result ".." : make "[.-]" ;
+#       assert.result ".." : make "[-]" ;
+#       assert.result "." : make "[]" ;
+#       assert.result "giz.h" : make "giz.h" ;
+#       assert.result "foo/bar/giz.h" : make "[.foo.bar]giz.h" ;
+#       assert.result "/disk:/my_docs" : make "disk:[my_docs]" ;
+#       assert.result "/disk:/boost/tools/build/new/project.jam" : make "disk:[boost.tools.build.test.-.new]project.jam" ;
+#   
+#       #
+#       # Special case (adds '.' to end of file w/o extension to
+#       # disambiguate from directory in portable path spec).
+#       #
+#       assert.result "Jamfile." : make "Jamfile" ;
+#       assert.result "dir/Jamfile." : make "[.dir]Jamfile" ;
+#       assert.result "/disk:/dir/Jamfile." : make "disk:[dir]Jamfile" ;
+#   
+#       #
+#       # Make native paths
+#       #
+#       assert.result "disk:" : native "/disk:" ;
+#       assert.result "[.foo.bar.giz]" : native "foo/bar/giz" ;
+#       assert.result "[.foo]" : native "foo" ;
+#       assert.result "[.-]" : native ".." ;
+#       assert.result "[.foo.-]" : native "foo/.." ;
+#       assert.result "[]" : native "." ;
+#       assert.result "disk:[my_docs.work]" : native "/disk:/my_docs/work" ;
+#       assert.result "giz.h" : native "giz.h" ;
+#       assert.result "disk:Jamfile." : native "/disk:Jamfile." ;
+#       assert.result "disk:[my_docs.work]Jamfile." : native "/disk:/my_docs/work/Jamfile." ;
+#   
+#       modules.poke path : os : $(save-os) ;
+#   
+#   }
+
+#
+
+
+#def glob(dir, patterns):
+#    result = []
+#    for pattern in patterns:
+#        result.extend(builtin_glob(os.path.join(dir, pattern)))
+#    return result
+
+def glob(dirs, patterns, exclude_patterns=None):
+    """Returns the list of files matching the given pattern in the
+    specified directory.  Both directories and patterns are 
+    supplied as portable paths. Each pattern should be non-absolute
+    path, and can't contain '.' or '..' elements. Each slash separated
+    element of pattern can contain the following special characters:
+    -  '?', which match any character
+    -  '*', which matches arbitrary number of characters.
+    A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3
+    if and only if e1 matches p1, e2 matches p2 and so on.
+    For example: 
+        [ glob . : *.cpp ] 
+        [ glob . : */build/Jamfile ]
+    """
+
+    assert(isinstance(patterns, list))
+    assert(isinstance(dirs, list))
+
+    if not exclude_patterns:
+        exclude_patterns = []
+    else:
+       assert(isinstance(exclude_patterns, list))
+
+    real_patterns = [os.path.join(d, p) for p in patterns for d in dirs]    
+    real_exclude_patterns = [os.path.join(d, p) for p in exclude_patterns
+                             for d in dirs]
+
+    inc = [os.path.normpath(name) for p in real_patterns
+           for name in builtin_glob(p)]
+    exc = [os.path.normpath(name) for p in real_exclude_patterns
+           for name in builtin_glob(p)]
+    return [x for x in inc if x not in exc]
+
+def glob_tree(roots, patterns, exclude_patterns=None):
+    """Recursive version of GLOB. Builds the glob of files while
+    also searching in the subdirectories of the given roots. An
+    optional set of exclusion patterns will filter out the
+    matching entries from the result. The exclusions also apply
+    to the subdirectory scanning, such that directories that
+    match the exclusion patterns will not be searched."""
+
+    if not exclude_patterns:
+        exclude_patterns = []
+
+    result = glob(roots, patterns, exclude_patterns)
+    subdirs = [s for s in result if s != "." and s != ".." and os.path.isdir(s)]
+    if subdirs:
+        result.extend(glob_tree(subdirs, patterns, exclude_patterns))
+        
+    return result
+
+def glob_in_parents(dir, patterns, upper_limit=None):
+    """Recursive version of GLOB which glob sall parent directories
+    of dir until the first match is found. Returns an empty result if no match
+    is found"""    
+    
+    assert(isinstance(dir, str))
+    assert(isinstance(patterns, list))
+
+    result = []
+
+    absolute_dir = os.path.join(os.getcwd(), dir)
+    absolute_dir = os.path.normpath(absolute_dir)
+    while absolute_dir:
+        new_dir = os.path.split(absolute_dir)[0]
+        if new_dir == absolute_dir:
+            break
+        result = glob([new_dir], patterns)
+        if result:
+            break
+        absolute_dir = new_dir
+
+    return result
+
+
+# The relpath functionality is written by
+# Cimarron Taylor
+def split(p, rest=[]):
+    (h,t) = os.path.split(p)
+    if len(h) < 1: return [t]+rest
+    if len(t) < 1: return [h]+rest
+    return split(h,[t]+rest)
+
+def commonpath(l1, l2, common=[]):
+    if len(l1) < 1: return (common, l1, l2)
+    if len(l2) < 1: return (common, l1, l2)
+    if l1[0] != l2[0]: return (common, l1, l2)
+    return commonpath(l1[1:], l2[1:], common+[l1[0]])
+
+def relpath(p1, p2):
+    (common,l1,l2) = commonpath(split(p1), split(p2))
+    p = []
+    if len(l1) > 0:
+        p = [ '../' * len(l1) ]
+    p = p + l2
+    if p:
+        return os.path.join( *p )
+    else:
+        return "."
Added: trunk/tools/build/v2/util/regex.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/regex.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,25 @@
+#  (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+import re
+
+def transform (list, pattern, indices = [1]):
+    """ Matches all elements of 'list' agains the 'pattern' 
+        and returns a list of the elements indicated by indices of 
+        all successfull matches. If 'indices' is omitted returns
+        a list of first paranthethised groups of all successfull
+        matches.
+    """
+    result = []
+
+    for e in list:
+        m = re.match (pattern, e)
+
+        if m:
+            for i in indices:
+                result.append (m.group (i))
+
+    return result
+
Added: trunk/tools/build/v2/util/sequence.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/sequence.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,52 @@
+#  (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+import operator
+
+def unique (values):
+    # TODO: is this the most efficient way?
+    #       consider using a set from Python 2.4.
+    return list(set(values))
+#    cache = {}
+#    result = []
+#    for v in values:
+#        if not cache.has_key(v):
+#            cache[v] = None
+#            result.append(v)
+#    return result
+
+
+
+def max_element (elements, ordered = None):
+    """ Returns the maximum number in 'elements'. Uses 'ordered' for comparisons,
+        or '<' is none is provided.
+    """
+    if not ordered: ordered = operator.lt
+
+    max = elements [0]
+    for e in elements [1:]:
+        if ordered (max, e):
+            max = e
+
+    return max
+
+def select_highest_ranked (elements, ranks):
+    """ Returns all of 'elements' for which corresponding element in parallel
+        list 'rank' is equal to the maximum value in 'rank'.
+    """
+    if not elements:
+        return []
+
+    max_rank = max_element (ranks)
+
+    result = []
+    while elements:
+        if ranks [0] == max_rank:
+            result.append (elements [0])
+
+        elements = elements [1:]
+        ranks = ranks [1:]
+
+    return result
Added: trunk/tools/build/v2/util/set.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/set.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,42 @@
+#  (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+from utility import to_seq
+
+def difference (b, a):
+    """ Returns the elements of B that are not in A.
+    """
+    result = []
+    for element in b:
+        if not element in a:
+            result.append (element)
+
+    return result
+
+def intersection (set1, set2):
+    """ Removes from set1 any items which don't appear in set2 and returns the result.
+    """
+    result = []
+    for v in set1:
+        if v in set2:
+            result.append (v)
+    return result
+
+def contains (small, large):
+    """ Returns true iff all elements of 'small' exist in 'large'.
+    """
+    small = to_seq (small)
+    large = to_seq (large)
+
+    for s in small:
+        if not s in large:
+            return False
+    return True
+
+def equal (a, b):
+    """ Returns True iff 'a' contains the same elements as 'b', irrespective of their order.
+        # TODO: Python 2.4 has a proper set class.
+    """
+    return contains (a, b) and contains (b, a)
Added: trunk/tools/build/v2/util/utility.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/util/utility.py	2009-07-27 04:52:41 EDT (Mon, 27 Jul 2009)
@@ -0,0 +1,155 @@
+#  (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
+#  distribute this software is granted provided this copyright notice appears in
+#  all copies. This software is provided "as is" without express or implied
+#  warranty, and with no claim as to its suitability for any purpose.
+
+""" Utility functions to add/remove/get grists.
+    Grists are string enclosed in angle brackets (<>) that are used as prefixes. See Jam for more information.
+"""
+
+import re
+import os
+import bjam
+from b2.exceptions import *
+
+__re_grist_and_value = re.compile (r'(<[^>]*>)(.*)')
+__re_grist_content = re.compile ('^<(.*)>$')
+__re_backslash = re.compile (r'\\')
+
+def to_seq (value):
+    """ If value is a sequence, returns it.
+        If it is a string, returns a sequence with value as its sole element.
+        """
+    if not value:
+        return []
+
+    if isinstance (value, str):
+        return [value]
+
+    else:
+        return value
+
+def replace_references_by_objects (manager, refs):
+    objs = []
+    for r in refs:
+        objs.append (manager.get_object (r))
+    return objs
+
+def add_grist (features):
+    """ Transform a string by bracketing it with "<>". If already bracketed, does nothing.
+        features: one string or a sequence of strings
+        return: the gristed string, if features is a string, or a sequence of gristed strings, if features is a sequence
+    """
+
+    def grist_one (feature):
+        if feature [0] != '<' and feature [len (feature) - 1] != '>':
+            return '<' + feature + '>'
+        else:
+            return feature
+    
+    if isinstance (features, str):
+        return grist_one (features)
+    else:
+        return [ grist_one (feature) for feature in features ]
+
+def replace_grist (features, new_grist):
+    """ Replaces the grist of a string by a new one.
+        Returns the string with the new grist.
+    """
+    def replace_grist_one (name, new_grist):
+        split = __re_grist_and_value.match (name)
+        if not split:
+            return new_grist + name
+        else:
+            return new_grist + split.group (2)
+
+    if isinstance (features, str):
+        return replace_grist_one (features, new_grist)
+    else:
+        return [ replace_grist_one (feature, new_grist) for feature in features ]
+
+def get_value (property):
+    """ Gets the value of a property, that is, the part following the grist, if any.
+    """
+    return replace_grist (property, '')
+    
+def get_grist (value):
+    """ Returns the grist of a string.
+        If value is a sequence, does it for every value and returns the result as a sequence.
+    """
+    def get_grist_one (name):
+        split = __re_grist_and_value.match (name)
+        if not split:
+            return ''
+        else:
+            return split.group (1)
+
+    if isinstance (value, str):
+        return get_grist_one (value)
+    else:
+        return [ get_grist_one (v) for v in value ]
+
+def ungrist (value):
+    """ Returns the value without grist. 
+        If value is a sequence, does it for every value and returns the result as a sequence.
+    """
+    def ungrist_one (value):
+        stripped = __re_grist_content.match (value)
+        if not stripped:
+            raise BaseException ("in ungrist: '%s' is not of the form <.*>" % value)
+
+        return stripped.group (1)
+
+    if isinstance (value, str):
+        return ungrist_one (value)
+    else:
+        return [ ungrist_one (v) for v in value ]
+
+def replace_suffix (name, new_suffix):
+    """ Replaces the suffix of name by new_suffix.
+        If no suffix exists, the new one is added.
+    """
+    split = os.path.splitext (name)
+    return split [0] + new_suffix
+
+def forward_slashes (s):
+    """ Converts all backslashes to forward slashes.
+    """
+    return __re_backslash.sub ('/', s)
+
+
+def split_action_id (id):
+    """ Splits an id in the toolset and specific rule parts. E.g.
+        'gcc.compile.c++' returns ('gcc', 'compile.c++')
+    """
+    split = id.split ('.', 1)
+    toolset = split [0]
+    name = ''
+    if len (split) > 1:
+        name = split [1]
+    return (toolset, name)
+
+def os_name ():
+    result = bjam.variable("OS")
+    assert(len(result) == 1)
+    return result[0]
+
+def platform ():
+    return bjam.variable("OSPLAT")
+    
+def os_version ():
+    return bjam.variable("OSVER")
+
+def on_windows ():
+    """ Returns true if running on windows, whether in cygwin or not.
+    """
+    if bjam.variable("NT"):
+        return True
+
+    elif bjam.variable("UNIX"):
+
+        uname = bjam.variable("JAMUNAME")
+        if uname and uname[0].startswith("CYGWIN"):
+            return True
+
+    return False