$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r64537 - in trunk/tools/build/v2: build util
From: ghost_at_[hidden]
Date: 2010-08-02 07:38:57
Author: vladimir_prus
Date: 2010-08-02 07:38:54 EDT (Mon, 02 Aug 2010)
New Revision: 64537
URL: http://svn.boost.org/trac/boost/changeset/64537
Log:
Complete porting of build/targets.jam
Text files modified: 
   trunk/tools/build/v2/build/generators.py |    23 ++++                                    
   trunk/tools/build/v2/build/targets.py    |   167 ++++++++++++++++++++++++++------------- 
   trunk/tools/build/v2/util/sequence.py    |    24 ++---                                   
   3 files changed, 140 insertions(+), 74 deletions(-)
Modified: trunk/tools/build/v2/build/generators.py
==============================================================================
--- trunk/tools/build/v2/build/generators.py	(original)
+++ trunk/tools/build/v2/build/generators.py	2010-08-02 07:38:54 EDT (Mon, 02 Aug 2010)
@@ -853,7 +853,7 @@
         m = g.match_rank(prop_set)
         if m:
             dout("  is viable")
-            viable_generators.append(g)
+            viable_generators.append(g)            
                             
     return viable_generators
 
@@ -873,6 +873,8 @@
         # TODO: is this really used?
         if not g in __active_generators:
             viable_generators.append (g)
+        else:
+            dout("      generator %s is active, discarning" % g.id())
 
     # Generators which override 'all'.
     all_overrides = []
@@ -941,7 +943,7 @@
     return result;
 
 
-def construct (project, name, target_type, prop_set, sources):
+def construct (project, name, target_type, prop_set, sources, top_level=False):
     """ 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
@@ -951,9 +953,19 @@
         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.            
+        targets.
+        
+        If 'top-level' is set, does not suppress generators that are already
+        used in the stack. This may be useful in cases where a generator
+        has to build a metatargets -- for example a target corresponding to
+        built tool.        
     """
-    # TODO: Why is global needed here?
+
+    global __active_generators
+    if top_level:
+        saved_active = __active_generators
+        __active_generators = []
+
     global __construct_stack
     if __construct_stack:
         __ensure_type (sources)
@@ -976,5 +988,8 @@
         
     __construct_stack = __construct_stack [1:]
 
+    if top_level:
+        __active_generators = saved_active
+
     return result
     
Modified: trunk/tools/build/v2/build/targets.py
==============================================================================
--- trunk/tools/build/v2/build/targets.py	(original)
+++ trunk/tools/build/v2/build/targets.py	2010-08-02 07:38:54 EDT (Mon, 02 Aug 2010)
@@ -1,7 +1,5 @@
-# Status: being ported by Vladimir Prus
-# Still to do: call toolset.requirements when those are ported.
-# Remember the location of target.
-# Base revision: 40480
+# Status: ported.
+# Base revision: 64488
 
 # Copyright Vladimir Prus 2002-2007.
 # Copyright Rene Rivera 2006.
@@ -104,6 +102,8 @@
 
         self.debug_building_ = "--debug-building" in bjam.variable("ARGV")
 
+        self.targets_ = []
+
     def main_target_alternative (self, target):
         """ Registers the specified target as a main target alternatives.
             Returns 'target'.
@@ -232,6 +232,16 @@
         if self.debug_building_:
             print self.indent_ + message
 
+    def push_target(self, target):
+        self.targets_.append(target)
+
+    def pop_target(self):
+        self.targets_ = self.targets_[:-1]
+
+    def current(self):
+        return self.targets_[0]
+
+
 class GenerateResult:
     
     def __init__ (self, ur=None, targets=None):
@@ -363,6 +373,9 @@
         # Targets marked as explicit.
         self.explicit_targets_ = set()
 
+        # Targets marked as always
+        self.always_targets_ = set()
+
         # The constants defined for this project.
         self.constants_ = {}
 
@@ -423,7 +436,7 @@
         # 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))
+            result.append (self.find(pn + "/"))
                         
         return result
 
@@ -434,6 +447,9 @@
         # Record the name of the target, not instance, since this
         # rule is called before main target instaces are created.
         self.explicit_targets_.add(target_name)
+
+    def mark_target_as_always(self, target_name):
+        self.always_targets_.add(target_name)
     
     def add_alternative (self, target_instance):
         """ Add new target alternative.
@@ -548,6 +564,9 @@
             if not self.main_target_.has_key (name):
                 t = MainTarget (name, self.project_)
                 self.main_target_ [name] = t
+
+            if name in self.always_targets_:
+                a.always()
             
             self.main_target_ [name].add_alternative (a)
 
@@ -561,7 +580,16 @@
         """
 
         if path:
-            value = os.path.join(self.location_, value)
+            l = self.location_
+            if not l:
+                # Project corresponding to config files do not have 
+                # 'location' attribute, but do have source location.
+                # It might be more reasonable to make every project have
+                # a location and use some other approach to prevent buildable
+                # targets in config files, but that's for later.
+                l = get('source-location')
+                
+            value = os.path.join(l, value)
             # Now make the value absolute path
             value = os.path.join(os.getcwd(), value)
 
@@ -660,46 +688,7 @@
         return best
 
     def apply_default_build (self, property_set):
-        # 1. First, see what properties from default_build
-        # are already present in property_set. 
-
-        specified_features = set(p.feature() for p in property_set.all())
-        
-        defaults_to_apply = []
-        for d in self.default_build_.all():
-            if not d.feature() 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(property_set.all())
-
-            result = build_request.expand_no_defaults(
-                b2.build.property_set.create([p]) for p in (compressed + defaults_to_apply))
-            
-        else:
-            result.append (property_set)
-
-        return result
+        return apply_default_build(property_set, self.default_build_)
 
     def generate (self, ps):
         """ Select an alternative for this main target, by finding all alternatives
@@ -821,11 +810,16 @@
         self.request_cache = {}
 
         self.user_context_ = self.manager_.errors().capture_user_context()
+
+        self.always_ = False
+
+    def always(self):
+        self.always_ = True
         
     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'
+            The only used of this rule at the moment is the '--dump-tests'
             feature of the test system.            
         """
         if self.source_targets_ == None:
@@ -1084,6 +1078,8 @@
                 "Command line free features: '%s'" % str (cf.raw ()))            
             self.manager().targets().log(
                 "Target requirements: %s'" % str (self.requirements().raw ()))
+            
+        self.manager().targets().push_target(self)
 
         if not self.generated_.has_key(ps):
 
@@ -1127,7 +1123,10 @@
                 # 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)
+                # Use stable sort, since for some targets the order is
+                # important. E.g. RUN_PY target need python source to come
+                # first.
+                source_targets = unique(source_targets, stable=True)
 
                 # FIXME: figure why this call messes up source_targets in-place
                 result = self.construct (self.name_, source_targets[:], rproperties)
@@ -1137,6 +1136,10 @@
                     gur = result [0]
                     result = result [1]
 
+                    if self.always_:
+                        for t in result:
+                            t.always()
+
                     s = self.create_subvariant (
                         result,
                         self.manager().virtual_targets().recent_targets(), ps,
@@ -1155,17 +1158,25 @@
                 else:
                     self.generated_[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_[ps] = GenerateResult (rproperties, [])
+                # If we just see <build>no, we cannot produce any reasonable
+                # diagnostics. The code that adds this property is expected
+                # to explain why a target is not built, for example using
+                # the configure.log-component-configuration function.
+
+                # If this target fails to build, add <build>no to properties
+                # to cause any parent target to fail to build.  Except that it
+                # - does not work now, since we check for <build>no only in
+                #   common properties, but not in properties that came from
+                #   dependencies
+                # - it's not clear if that's a good idea anyway.  The alias
+                #   target, for example, should not fail to build if a dependency
+                #   fails.                
+                self.generated_[ps] = GenerateResult(
+                    property_set.create(["<build>no"]), [])
         else:
             self.manager().targets().log ("Already built")
 
+        self.manager().targets().pop_target()
         self.manager().targets().decrease_indent()
 
         return self.generated_[ps]
@@ -1273,7 +1284,7 @@
 
         r = generators.construct (self.project_, name, self.type_, 
                                   prop_set.add_raw(['<main-target-type>' + self.type_]),
-            source_targets)
+                                  source_targets, True)
 
         if not r:
             print "warning: Unable to construct '%s'" % self.full_name ()
@@ -1290,6 +1301,48 @@
         
         return r
 
+def apply_default_build(property_set, default_build):
+    # 1. First, see what properties from default_build
+    # are already present in property_set. 
+
+    specified_features = set(p.feature() for p in property_set.all())
+
+    defaults_to_apply = []
+    for d in default_build.all():
+        if not d.feature() 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(property_set.all())
+
+        result = build_request.expand_no_defaults(
+            b2.build.property_set.create([p]) for p in (compressed + defaults_to_apply))
+
+    else:
+        result.append (property_set)
+
+    return result
+
 
 def create_typed_metatarget(name, type, sources, requirements, default_build, usage_requirements):
     
Modified: trunk/tools/build/v2/util/sequence.py
==============================================================================
--- trunk/tools/build/v2/util/sequence.py	(original)
+++ trunk/tools/build/v2/util/sequence.py	2010-08-02 07:38:54 EDT (Mon, 02 Aug 2010)
@@ -5,19 +5,17 @@
 
 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 unique (values, stable=False):
+    if stable:
+        s = set()
+        r = []
+        for v in values:
+            if not v in s:
+                r.append(v)
+                s.add(v)
+        return r
+    else:
+        return list(set(values))
 
 def max_element (elements, ordered = None):
     """ Returns the maximum number in 'elements'. Uses 'ordered' for comparisons,