Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r64286 - in trunk/tools/build/v2: . build tools
From: ghost_at_[hidden]
Date: 2010-07-23 05:09:15


Author: vladimir_prus
Date: 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
New Revision: 64286
URL: http://svn.boost.org/trac/boost/changeset/64286

Log:
Introduce Feature and Property classes, and move away from messy string manipulation.

Text files modified:
   trunk/tools/build/v2/build/build_request.py | 101 +++---
   trunk/tools/build/v2/build/errors.py | 7
   trunk/tools/build/v2/build/feature.py | 543 +++++++++++++++++++--------------------
   trunk/tools/build/v2/build/property.py | 253 +++++++----------
   trunk/tools/build/v2/build/property_set.py | 150 +++++++---
   trunk/tools/build/v2/build/targets.py | 14
   trunk/tools/build/v2/build/toolset.py | 134 ++++-----
   trunk/tools/build/v2/build/virtual_target.py | 4
   trunk/tools/build/v2/build_system.py | 6
   trunk/tools/build/v2/tools/builtin.py | 34 +-
   10 files changed, 622 insertions(+), 624 deletions(-)

Modified: trunk/tools/build/v2/build/build_request.py
==============================================================================
--- trunk/tools/build/v2/build/build_request.py (original)
+++ trunk/tools/build/v2/build/build_request.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -16,71 +16,72 @@
         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 ]
+ expanded_property_sets = [ps.expand_subfeatures() for ps 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.
+ x_product_seen = set()
+ x_product_used = set()
+ return __x_product_aux (property_sets, x_product_seen, x_product_used)
+
+def __x_product_aux (property_sets, seen_features):
+ """Returns non-conflicting combinations of property sets.
+
+ property_sets is a list of PropertySet instances. seen_features is a set of Property
+ instances.
+
+ Returns a tuple of:
+ - list of lists of Property instances, such that within each list, no two Property instance
+ have the same feature, and no Property is for feature in seen_features.
+ - set of features we saw in property_sets
     """
- result = []
-
- if property_sets:
- p = feature.split (property_sets [0])
+ if not property_sets:
+ return ([], set())
+
+ properties = property_sets[0].all()
+
+ these_features = set()
+ for p in property_sets[0].non_free():
+ these_features.add(p.feature())
+
+ # Note: the algorithm as implemented here, as in original Jam code, appears to
+ # detect conflicts based on features, not properties. For example, if command
+ # line build request say:
+ #
+ # <a>1/<b>1 c<1>/<b>1
+ #
+ # It will decide that those two property sets conflict, because they both specify
+ # a value for 'b' and will not try building "<a>1 <c1> <b1>", but rather two
+ # different property sets. This is a topic for future fixing, maybe.
+ if these_features & seen_features:
+
+ (inner_result, inner_seen) = __x_product_aux(property_sets[1:], seen_features)
+ return (inner_result, inner_seen + these_features)
+
     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 = []
+
+ result = []
+ (inner_result, inner_seen) = __x_product_aux(property_sets[1:], seen_features | these_features)
+ for inner in inner_result:
+ result.append(properties + inner)
         
- 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.
+ if inner_seen & these_features:
+ # Some of elements in property_sets[1:] conflict with elements of property_sets[0],
+ # Try again, this time omitting elements of property_sets[0]
+ (inner_result2, inner_seen2) = __x_product_aux(property_sets[1:], seen_features)
+ result.extend(inner_result2)
 
- return result
+ return (result, inner_seen + these_features)
+
+
 
 def looks_like_implicit_value(v):
     """Returns true if 'v' is either implicit value, or

Modified: trunk/tools/build/v2/build/errors.py
==============================================================================
--- trunk/tools/build/v2/build/errors.py (original)
+++ trunk/tools/build/v2/build/errors.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -20,7 +20,7 @@
 import sys
 
 def format(message, prefix=""):
- parts = message.split("\n")
+ parts = str(message).split("\n")
     return "\n".join(prefix+p for p in parts)
     
 
@@ -60,7 +60,10 @@
     def report(self):
         print "error:", self.args[0]
         if self.original_exception_:
- print format(self.original_exception_.args[0], " ")
+ try:
+ print format(self.original_exception_.args[0], " ")
+ except:
+ print format(str(self.original_exception_), " ")
         print
         print " error context (most recent first):"
         for c in self.context_[::-1]:

Modified: trunk/tools/build/v2/build/feature.py
==============================================================================
--- trunk/tools/build/v2/build/feature.py (original)
+++ trunk/tools/build/v2/build/feature.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -14,7 +14,8 @@
 
 import re
 
-from b2.util import set, utility, bjam_signature
+from b2.util import utility, bjam_signature
+import b2.util.set
 from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, to_seq
 from b2.exceptions import *
 
@@ -22,16 +23,73 @@
 __re_no_hyphen = re.compile ('^([^:]+)$')
 __re_slash_or_backslash = re.compile (r'[\\/]')
 
+class Feature(object):
+
+ # Map from string attribute names to integers bit flags.
+ # This will be initialized after declaration of the class.
+ _attribute_name_to_integer = {}
+
+ def __init__(self, name, values, attributes):
+ self._name = name
+ self._values = values
+ self._attributes = 0
+ for a in attributes:
+ self._attributes = self._attributes | Feature._attribute_name_to_integer[a]
+ self._attributes_string_list = attributes
+ self._subfeatures = []
+ self._parent = None
+
+ def name(self):
+ return self._name
+
+ def values(self):
+ return self._values
+
+ def add_values(self, values):
+ self._values.extend(values)
+
+ def attributes(self):
+ return self._attributes
+
+ def set_default(self, value):
+ self._default = value
+
+ def default(self):
+ return self._default
+
+ # FIXME: remove when we fully move to using classes for features/properties
+ def attributes_string_list(self):
+ return self._attributes_string_list
+
+ def subfeatures(self):
+ return self._subfeatures
+
+ def add_subfeature(self, name):
+ self._subfeatures.append(name)
+
+ def parent(self):
+ """For subfeatures, return pair of (parent_feature, value).
+
+ Value may be None if this subfeature is not specific to any
+ value of the parent feature.
+ """
+
+ def set_parent(self, feature, value):
+ self._parent = (feature, value)
+
+ def __str__(self):
+ return self._name
+
+
 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 __features_with_attributes, __subfeature_from_value, __all_top_features, __free_features
     global __all_subfeatures
         
     # The list with all attribute names.
     __all_attributes = [ 'implicit',
- 'executed',
                         'composite',
                         'optional',
                         'symmetric',
@@ -44,12 +102,17 @@
                         'subfeature',
                         'order-sensitive'
                        ]
+ i = 1
+ for a in __all_attributes:
+ setattr(Feature, a.upper(), i)
+ Feature._attribute_name_to_integer[a] = i
+ def probe(self, flag=i):
+ return getattr(self, "_attributes") & flag
+ setattr(Feature, a.replace("-", "_"), probe)
+ i = i << 1
     
- # A map containing all features. The key is the gristed feature name. The value is a map with:
- # 'values': [],
- # 'attributes': [],
- # 'subfeatures': [],
- # 'default': None
+ # A map containing all features. The key is the feature name.
+ # The value is an instance of Feature class.
     __all_features = {}
     
     # All non-subfeatures.
@@ -58,8 +121,8 @@
     # 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': []
+ # A map containing all composite properties. The key is a Property instance,
+ # and the value is a list of Property instances
     __composite_properties = {}
     
     __features_with_attributes = {}
@@ -67,7 +130,7 @@
         __features_with_attributes [attribute] = []
     
     # Maps a value to the corresponding subfeature name.
- __subfeature_value_to_name = {}
+ __subfeature_from_value = {}
     
     # All free features
     __free_features = []
@@ -81,6 +144,13 @@
     """
     return __all_features.iteritems ()
 
+def get(name):
+ """Return the Feature instance for the specified name.
+
+ Throws if no feature by such name exists
+ """
+ return __all_features[name]
+
 # FIXME: prepare-test/finish-test?
 
 @bjam_signature((["name"], ["values", "*"], ["attributes", "*"]))
@@ -90,27 +160,22 @@
         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
-
+ feature = Feature(name, [], attributes)
+ __all_features[name] = feature
+ # Temporary measure while we have not fully moved from 'gristed strings'
+ __all_features["<" + name + ">"] = feature
+
     for attribute in attributes:
         __features_with_attributes [attribute].append (name)
+
+ name = add_grist(name)
         
     if 'subfeature' in attributes:
         __all_subfeatures.append(name)
     else:
- __all_top_features.append(name)
+ __all_top_features.append(feature)
 
     extend (name, values)
 
@@ -118,47 +183,41 @@
     if 'free' in attributes:
         __free_features.append (name)
 
+ return feature
+
+@bjam_signature((["feature"], ["value"]))
 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]
- attributes = f['attributes']
+ f = __all_features[feature]
+ attributes = f.attributes()
     bad_attribute = None
 
- if "free" in attributes:
+ if attributes & Feature.FREE:
         bad_attribute = "free"
- elif "optional" in attributes:
+ elif attributes & Feature.OPTIONAL:
         bad_attribute = "optional"
         
     if bad_attribute:
- raise InvalidValue ("%s property %s cannot have a default" % (bad_attribute, feature))
+ raise InvalidValue ("%s property %s cannot have a default" % (bad_attribute, feature.name()))
         
- if isinstance(value, list):
- value = value[0]
-
- values = f['values']
- if not value in values:
+ if not value in f.values():
         raise InvalidValue ("The specified default value, '%s' is invalid.\n" % value + "allowed values are: %s" % values)
 
- f ['default'] = value
+ f.set_default(value)
 
-def defaults (features):
+def defaults(features):
     """ Returns the default property values for the given features.
     """
+ # FIXME: should merge feature and property modules.
+ import property
+
     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))
+ if not f.free() and not f.optional() and f.default():
+ result.append(property.Property(f, f.default()))
 
     return result
 
@@ -175,20 +234,20 @@
 def attributes (feature):
     """ Returns the attributes of the given feature.
     """
- return __all_features [feature]['attributes']
+ return __all_features[feature].attributes_string_list()
         
 def values (feature):
     """ Return the values of the given feature.
     """
     validate_feature (feature)
- return __all_features [feature]['values']
+ 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
 
@@ -211,15 +270,15 @@
     return __implicit_features[components[0]]
 
 def __find_implied_subfeature (feature, subvalue, value_string):
- feature = add_grist (feature)
- if value_string == None: value_string = ''
+
+ #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):
+ if not __subfeature_from_value.has_key(feature) \
+ or not __subfeature_from_value[feature].has_key(value_string) \
+ or not __subfeature_from_value[feature][value_string].has_key (subvalue):
         return None
         
- return __subfeature_value_to_name[feature][value_string][subvalue]
+ return __subfeature_from_value[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
@@ -238,9 +297,10 @@
 def validate_feature (name):
     """ Checks if all name is a valid feature. Otherwise, raises an exception.
     """
- x = valid (name)
- if not x:
+ if not __all_features.has_key(name):
         raise InvalidFeature ("'%s' is not a valid feature name" % name)
+ else:
+ return __all_features[name]
 
 def valid (names):
     """ Returns true iff all elements of names are valid features.
@@ -252,7 +312,8 @@
     else:
         return [ valid_one (name) for name in names ]
 
-def __expand_subfeatures_aux (feature, value, dont_validate = False):
+# Uses Property
+def __expand_subfeatures_aux (property, 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
@@ -267,20 +328,18 @@
         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)
-
+ f = property.feature()
+ v = property.value()
     if not dont_validate:
- validate_value_string(feature, value)
-
- components = value.split ("-")
+ validate_value_string(f, v)
+
+ components = v.split ("-")
     
- # get the top-level feature's value
- value = replace_grist(components[0], '')
+ v = components[0]
+
+ import property
 
- result = [ replace_grist(components[0], feature) ]
+ result = [property.Property(f, components[0])]
     
     subvalues = components[1:]
 
@@ -288,17 +347,13 @@
         subvalue = subvalues [0] # pop the head off of subvalues
         subvalues = subvalues [1:]
         
- subfeature = __find_implied_subfeature (feature, subvalue, value)
+ subfeature = __find_implied_subfeature (f, subvalue, v)
         
         # 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]
+ return [property.Property(f, '-'.join(components))]
             
- f = ungrist (feature)
- # FIXME: why grist includes '<>'?
- result.append (replace_grist (subvalue, '<' + f + '-' + subfeature + '>'))
+ result.append(property.Property(subfeature, subvalue))
     
     return result
 
@@ -321,12 +376,11 @@
     """
     result = []
     for p in properties:
- p_grist = get_grist (p)
         # Don't expand subfeatures in subfeatures
- if ':' in p_grist:
+ if p.feature().subfeature():
             result.append (p)
         else:
- result.extend (__expand_subfeatures_aux (p_grist, replace_grist (p, ''), dont_validate))
+ result.extend(__expand_subfeatures_aux (p, dont_validate))
 
     return result
 
@@ -356,42 +410,41 @@
     name = add_grist (name)
     __validate_feature (name)
     feature = __all_features [name]
-
- if 'implicit' in feature ['attributes']:
+
+ if feature.implicit():
         for v in values:
- if __implicit_features.has_key (v):
+ 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:
+ 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.set_default(values[0])
 
- feature['values'].extend (values)
+ feature.add_values(values)
 
-def validate_value_string (feature, value_string):
+def validate_value_string (f, 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']:
+ if f.free() or value_string in f.values():
         return
 
     values = [value_string]
 
- if f['subfeatures']:
- if not value_string in f['subfeatures']:
+ if f.subfeatures():
+ if not value_string in 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']))
+ if not values[0] in f.values() and \
+ (values[0] or not f.optional()):
+ 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])
+ implied_subfeature(f, v, values[0])
 
 
 """ Extends the given subfeature with the subvalues. If the optional
@@ -411,19 +464,28 @@
     
     subvalues: The additional values of the subfeature being defined.
 """
-def extend_subfeature (feature, value_string, subfeature, subvalues):
- feature = add_grist (feature)
- validate_feature (feature)
+def extend_subfeature (feature_name, value_string, subfeature_name, subvalues):
 
+ feature = validate_feature(feature_name)
+
     if value_string:
- validate_value_string (feature, value_string)
+ validate_value_string(feature, value_string)
 
- subfeature_name = __get_subfeature_name (subfeature, value_string)
+ subfeature_name = feature_name + '-' + __get_subfeature_name (subfeature_name, value_string)
     
- f = ungrist (feature)
- extend (f + '-' + subfeature_name, subvalues) ;
+ extend(subfeature_name, subvalues) ;
+ subfeature = __all_features[subfeature_name]
+
+ if value_string == None: value_string = ''
     
- __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues)
+ if not __subfeature_from_value.has_key(feature):
+ __subfeature_from_value [feature] = {}
+
+ if not __subfeature_from_value[feature].has_key(value_string):
+ __subfeature_from_value [feature][value_string] = {}
+
+ for subvalue in subvalues:
+ __subfeature_from_value [feature][value_string][subvalue] = subfeature
 
 def subfeature (feature_name, value_string, subfeature, subvalues, attributes = []):
     """ Declares a subfeature.
@@ -435,50 +497,59 @@
         subvalues: The allowed values of this subfeature.
         attributes: The attributes of the subfeature.
     """
- feature_name = add_grist (feature_name)
- validate_feature (feature_name)
+ parent_feature = 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']:
+ 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'])
+ f = feature (feature_name + '-' + subfeature_name, subvalues, attributes + ['subfeature'])
+ f.set_parent(parent_feature, value_string)
+
+ parent_feature.add_subfeature(f)
     
     # Now make sure the subfeature values are known.
     extend_subfeature (feature_name, value_string, subfeature, subvalues)
 
-def compose (composite_property, component_properties):
+
+def compose (composite_property_s, component_properties_s):
     """ Sets the components of the given composite property.
+
+ All paremeters are <feature>value strings
     """
- component_properties = to_seq (component_properties)
+ import property
 
- feature = get_grist (composite_property)
- if not 'composite' in attributes (feature):
- raise BaseException ("'%s' is not a composite feature" % feature)
+ component_properties_s = to_seq (component_properties_s)
+ composite_property = property.create_from_string(composite_property_s)
+ f = composite_property.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 len(component_properties_s) > 0 and isinstance(component_properties_s[0], property.Property):
+ component_properties = component_properties_s
+ else:
+ component_properties = [property.create_from_string(p) for p in component_properties_s]
+
+ if not f.composite():
+ raise BaseException ("'%s' is not a composite feature" % f)
+
+ if __composite_properties.has_key(property):
+ raise BaseException ('components of "%s" already set: %s' % (composite_property, str (__composite_properties[composite_property])))
 
     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
+ __composite_properties[composite_property] = component_properties
 
 
-def expand_composite (property):
+def expand_composite(property):
     result = [ property ]
- if __composite_properties.has_key (property):
- for p in __composite_properties [property]['components']:
- result.extend (expand_composite (p))
+ if __composite_properties.has_key(property):
+ for p in __composite_properties[property]:
+ result.extend(expand_composite(p))
     return result
 
 
@@ -501,68 +572,66 @@
     """ Expand all composite properties in the set so that all components
         are explicitly expressed.
     """
- explicit_features = get_grist (properties)
+ explicit_features = set(p.feature() for p in properties)
 
     result = []
 
     # now expand composite features
     for p in properties:
- expanded = expand_composite (p)
+ expanded = expand_composite(p)
 
         for x in expanded:
             if not x in result:
- f = get_grist (x)
+ f = x.feature()
 
- if f in __free_features:
+ if f.free():
                     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))
+ if any(r.feature() == f for r in result):
+ raise FeatureConflict(
+ "expansions of composite features result in "
+ "conflicting values for '%s'\nvalues: '%s'\none contributing composite property was '%s'" %
+ (f.name(), [r.value() for r in result if r.feature() == f] + [x.value()], p))
                         else:
                             result.append (x)
- elif f in get_grist (result):
+ elif any(r.feature() == f for r in 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, '')))
+ [r.value() for r in result if r.feature() == f], p, x.value()))
                 else:
                     result.append (x)
 
     return result
 
+# Uses Property
 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']:
+ if not f.subfeature():
         return False
 
- specific_subfeature = __re_split_subfeatures.match (f)
+ p = f.parent()
+ if not p:
+ return False
 
- 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))
+ parent_feature = p[0]
+ parent_value = p[1]
+
+ if parent_feature != parent_property.feature():
+ return False
 
- if top_sub [1] and add_grist (top_sub [0]) == get_grist (parent_property):
- return True
+ if parent_value and parent_value != parent_property.value():
+ return False
 
- return False
+ return True
 
 def __is_subproperty_of (parent_property, p):
     """ As is_subfeature_of, for subproperties.
     """
- return is_subfeature_of (parent_property, get_grist (p))
+ return is_subfeature_of (parent_property, p.feature())
 
     
 # Returns true iff the subvalue is valid for the feature. When the
@@ -573,24 +642,21 @@
     if not value_string:
         value_string = ''
 
- if not __subfeature_value_to_name.has_key(feature):
+ if not __subfeature_from_value.has_key(feature):
         return False
         
- if not __subfeature_value_to_name[feature].has_key(value_string):
+ if not __subfeature_from_value[feature].has_key(value_string):
         return False
         
- if not __subfeature_value_to_name[feature][value_string].has_key(subvalue):
+ if not __subfeature_from_value[feature][value_string].has_key(subvalue):
         return False
 
- if __subfeature_value_to_name[feature][value_string][subvalue]\
+ if __subfeature_from_value[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:
@@ -599,6 +665,7 @@
     return result
 
 
+# Uses Property
 def expand (properties):
     """ Given a property set which may consist of composite and implicit
         properties and combined subfeature values, returns an expanded,
@@ -610,36 +677,10 @@
         two values of a given non-free feature are directly expressed in the
         input, an error is issued.
     """
- expanded = expand_subfeatures (properties)
+ 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
-
+# Accepts list of Property objects
 def add_defaults (properties):
     """ Given a set of properties, add default values for features not
         represented in the set.
@@ -658,35 +699,28 @@
          
           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
+ result = [x for x in properties]
     
+ handled_features = set()
+ for p in properties:
+ # We don't add default for conditional properties. We don't want
+ # <variant>debug:<define>DEBUG to be takes as specified value for <variant>
+ if not p.condition():
+ handled_features.add(p.feature())
+
+ missing_top = [f for f in __all_top_features if not f in handled_features]
+ more = defaults(missing_top)
+ result.extend(more)
+ for p in more:
+ handled_features.add(p.feature())
+
     # 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))
+ for p in result[:]:
+ s = p.feature().subfeatures()
+ more = defaults([s for s in p.feature().subfeatures() if not s in handled_features])
+ for p in more:
+ handled_features.add(p.feature())
+ result.extend(more)
     
     return result
 
@@ -699,47 +733,35 @@
         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)
+ components.extend(__composite_properties[property])
+ properties = b2.util.set.difference (properties, components)
     
     # handle subfeatures and implicit features
- x = __move_subfeatures_to_the_end (x)
+
+ # move subfeatures to the end of the list
+ properties = [p for p in properties if not p.feature().subfeature()] +\
+ [p for p in properties if p.feature().subfeature()]
     
     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
-
+ while properties:
+ p = properties[0]
+ f = p.feature()
+
         # locate all subproperties of $(x[1]) in the property set
- subproperties = __select_subproperties (fullp, x)
+ subproperties = __select_subproperties (p, properties)
         
         if subproperties:
             # reconstitute the joined property name
             subproperties.sort ()
- joined = p + '-' + '-'.join (replace_grist (subproperties, ''))
- result.append (joined)
+ joined = Property(p.feature(), p.value() + '-' + '-'.join ([sp.value() for sp in subproperties]))
+ result.append(joined)
 
- x = set.difference (x [1:], subproperties)
+ properties = b2.util.set.difference(properties[1:], subproperties)
 
         else:
             # eliminate properties whose value is equal to feature's
@@ -750,11 +772,14 @@
             # 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):
+ if p.value() != f.default() or f.symmetric():
                 result.append (p)
+ #\
+ #or get_grist (fullp) in get_grist (components):
+ # FIXME: restore above
+
 
- x = x [1:]
+ properties = properties[1:]
 
     return result
 
@@ -809,7 +834,7 @@
         if not pg:
             raise BaseException ("Gristed variable exppected. Got '%s'." % p)
         
- if not 'subfeature' in __all_features [pg]['attributes']:
+ if not __all_features [pg].subfeature():
             subs = __select_subproperties (p, properties)
             
             matched_subs.extend (subs)
@@ -823,7 +848,7 @@
             all_subs.append (p)
 
     # TODO: this variables are used just for debugging. What's the overhead?
- assert (set.equal (all_subs, matched_subs))
+ assert (b2.util.set.equal (all_subs, matched_subs))
 
     return result
 
@@ -833,22 +858,6 @@
 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 = ''
@@ -861,7 +870,7 @@
 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))
+ raise InvalidAttribute ("unknown attributes: '%s' in feature declaration: '%s'" % (str (b2.util.set.difference (attributes, __all_attributes)), name))
     
     if name in __all_features:
             raise AlreadyDefined ("feature '%s' already defined" % name)
@@ -877,20 +886,6 @@
     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

Modified: trunk/tools/build/v2/build/property.py
==============================================================================
--- trunk/tools/build/v2/build/property.py (original)
+++ trunk/tools/build/v2/build/property.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -10,20 +10,90 @@
 import re
 from b2.util.utility import *
 from b2.build import feature
-from b2.util import sequence, set
+from b2.util import sequence
+import b2.util.set
 from b2.manager import get_manager
 
 __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'(.*):(<.*)')
 __re_indirect_rule = re.compile("^([^%]*)%([^%]+)$")
 
+class Property(object):
+
+ __slots__ = ('_feature', '_value', '_condition')
+
+ def __init__(self, feature, value, condition = []):
+ assert(feature.free() or value.find(':') == -1)
+ self._feature = feature
+ self._value = value
+ self._condition = condition
+
+
+ def feature(self):
+ return self._feature
+
+ def value(self):
+ return self._value
+
+ def condition(self):
+ return self._condition
+
+ def to_raw(self):
+ # FIXME: include condition!
+ return "<" + self._feature.name() + ">" + self._value
+
+ def __str__(self):
+ return self.to_raw()
+
+ def __hash__(self):
+ # FIXME: consider if this class should be value-is-identity one
+ return hash((self._feature, self._value, tuple(self._condition)))
+
+ def __cmp__(self, other):
+ return cmp((self._feature, self._value, self._condition),
+ (other._feature, other._value, other._condition))
+
+
+def create_from_string(s, allow_condition = False):
+
+ condition = []
+ if __re_has_condition.search(s):
+
+ if not allow_condition:
+ raise BaseException("Conditional property is not allowed in this context")
+
+ m = __re_separate_condition_and_property.match(s)
+ condition = m.group(1)
+ s = m.group(2)
+
+ # FIXME: break dependency cycle
+ from b2.manager import get_manager
+
+ feature_name = get_grist(s)
+ if not feature_name:
+ if feature.is_implicit_value(s):
+ f = feature.implied_feature(s)
+ value = s
+ else:
+ raise get_manager().errors()("Invalid property '%s' -- unknown feature" % s)
+ else:
+ f = feature.get(feature_name)
+
+ value = get_value(s)
+ if not value:
+ get_manager().errors()("Invalid property '%s' -- no value specified" % s)
+
+
+ if condition:
+ condition = [create_from_string(x) for x in condition.split(',')]
+
+ return Property(f, value, condition)
+
 def reset ():
     """ Clear the module state. This is mainly for testing purposes.
     """
@@ -64,51 +134,18 @@
         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]
-
+# Uses Property
 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 = {}
+ # The result has no duplicates, so we store it in a set
+ result = set()
     
     # Records all requirements.
     required = {}
@@ -117,31 +154,23 @@
     # 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, '')
+ if r.condition():
+ required[r.feature()] = r
 
     for p in properties:
         # Skip conditional properties
- if is_conditional (p):
- result [p] = None
+ if p.condition():
+ result.add(p)
         # No processing for free properties
- elif 'free' in feature.attributes (get_grist (p)):
- result [p] = None
+ elif p.feature().free():
+ result.add(p)
         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
+ if required.has_key(p.feature()):
+ result.add(required[p.feature()])
             else:
- result [p] = None
+ result.add(p)
 
- return result.keys () + requirements
+ return sequence.unique(list(result) + requirements)
 
 def translate_paths (properties, path):
     """ Interpret all path properties in 'properties' as relative to 'path'
@@ -168,7 +197,7 @@
             result.append (condition + tp)
             
         else:
- result.append (condition + p)
+ result.append (condition + ":" + p)
 
     return result
 
@@ -205,7 +234,7 @@
             
             result.append(get_grist(px) + "@" + m)
         else:
- result.append(p)
+ result.append(px)
 
     return result
 
@@ -224,38 +253,20 @@
 
     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))
+ if not p.condition():
+ result.append(p)
 
- else:
- e.append (feature.expand_subfeatures (c))
-
- if e == condition:
- result.append (p)
+ for c in p.condition():
 
+ if c.feature().name().startswith("toolset") or c.feature().name() == "os":
+ # 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.
+ result.extend(feature.expand_subfeatures ([c], True))
             else:
- result.append (','.join(e) + ':' + value)
+ result.extend(feature.expand_subfeatures([c]))
 
     return result
 
@@ -277,6 +288,7 @@
     return result
 
 
+# FIXME: this should go
 def split_conditional (property):
     """ If 'property' is conditional property, returns
         condition and the property, e.g
@@ -291,6 +303,7 @@
 
     return None
 
+# FIXME: this should go
 def is_conditional (property):
     """ Returns True if a property is conditional.
     """
@@ -311,8 +324,7 @@
 
 def validate_property_sets (sets):
     for s in sets:
- validate(feature.split(s))
-
+ validate(s.all())
 
 def evaluate_conditionals_in_context (properties, context):
     """ Removes all conditional properties which conditions are not met
@@ -338,41 +350,11 @@
         conditions = s.group (1).split (',')
 
         # Evaluate condition
- if set.contains (c, context):
+ if b2.util.set.contains (conditions, 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
@@ -402,30 +384,8 @@
     """
     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))
+ if not property.feature().free():
+ feature.validate_value_string (property.feature(), property.value())
 
 
 ###################################################################
@@ -479,7 +439,7 @@
     properties in 'properties' that have any of 'attributes'."""
     result = []
     for e in properties:
- if set.intersection(attributes, feature.attributes(get_grist(e))):
+ if b2.util.set.intersection(attributes, feature.attributes(get_grist(e))):
             result.append(e)
     return result
 
@@ -514,6 +474,7 @@
 
     return result
 
+
 class PropertyMap:
     """ Class which maintains a property set -> string mapping.
     """
@@ -542,7 +503,7 @@
         for i in range(0, len(self.__properties)):
             p = self.__properties[i]
                         
- if set.contains (p, properties):
+ if b2.util.set.contains (p, properties):
                 matches.append (i)
                 match_ranks.append(len(p))
 

Modified: trunk/tools/build/v2/build/property_set.py
==============================================================================
--- trunk/tools/build/v2/build/property_set.py (original)
+++ trunk/tools/build/v2/build/property_set.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -28,13 +28,20 @@
     """ 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)
+ # FIXME: propagate to callers.
+ if len(raw_properties) > 0 and isinstance(raw_properties[0], property.Property):
+ x = raw_properties
+ else:
+ x = [property.create_from_string(ps) for ps in raw_properties]
+ x.sort ()
+ x = unique (x)
+
+ # FIXME: can we do better, e.g. by directly computing
+ # has value of the list?
+ key = tuple(x)
 
     if not __cache.has_key (key):
- __cache [key] = PropertySet (raw_properties)
+ __cache [key] = PropertySet(x)
 
     return __cache [key]
 
@@ -43,9 +50,10 @@
         that all properties are valid and converting incidental
         properties into gristed form.
     """
- property.validate (raw_properties)
+ properties = [property.create_from_string(s) for s in raw_properties]
+ property.validate(properties)
     
- return create (property.make (raw_properties))
+ return create(properties)
 
 def empty ():
     """ Returns PropertySet with empty set of properties.
@@ -56,13 +64,12 @@
     """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)
+ properties = [property.create_from_string(s, True) for s in specification]
+ properties = property.expand_subfeatures_in_conditions(properties)
+ return create(properties)
 
 
 def refine_from_user_input(parent_requirements, specification, jamfile_module,
@@ -126,13 +133,20 @@
         - several operations, like and refine and as_path are provided. They all use
           caching whenever possible.
     """
- def __init__ (self, raw_properties = []):
+ def __init__ (self, properties = []):
+
+
+ raw_properties = []
+ for p in properties:
+ raw_properties.append(p.to_raw())
 
- self.raw_ = raw_properties
+ self.all_ = properties
+ self.all_raw_ = raw_properties
         
         self.incidental_ = []
         self.free_ = []
         self.base_ = []
+ self.base_raw_ = []
         self.dependency_ = []
         self.non_dependency_ = []
         self.conditional_ = []
@@ -155,6 +169,9 @@
         # Cache for the expanded composite properties
         self.composites_ = None
 
+ # Cache for property set with expanded subfeatures
+ self.subfeatures_ = None
+
         # Cache for the property set containing propagated properties.
         self.propagated_ps_ = None
         
@@ -178,40 +195,55 @@
             # A feature can be both incidental and free,
             # in which case we add it to incidental.
             if 'incidental' in att:
- self.incidental_.append (p)
+ pass
+# self.incidental_.append (p)
             elif 'free' in att:
- self.free_.append (p)
+ # self.free_.append (p)
+ pass
             else:
- self.base_.append (p)
+ self.base_raw_.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)
+
+ for p in properties:
+
+ if p.feature().incidental():
+ self.incidental_.append(p)
+ elif p.feature().free():
+ self.free_.append(p)
+ else:
+ self.base_.append(p)
+
+ if p.condition():
+ self.conditional_.append(p)
+ else:
+ self.non_conditional_.append(p)
+
+ def all(self):
+ return self.all_
     
     def raw (self):
         """ Returns the list of stored properties.
         """
- return self.raw_
+ return self.all_raw_
 
     def __str__(self):
- return string.join(self.raw_)
+ return string.join(self.all_raw_)
     
     def base (self):
         """ Returns properties that are neither incidental nor free.
         """
- return self.base_
+ return self.base_raw_
     
     def free (self):
         """ Returns free properties which are not dependency properties.
@@ -246,32 +278,37 @@
     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 ())
+ assert isinstance(requirements, PropertySet)
+ if not self.refined_.has_key (requirements):
+ r = property.refine(self.all_, requirements.all_)
 
- self.refined_ [str_req] = create (r)
+ self.refined_[requirements] = create(r)
 
- return self.refined_ [str_req]
+ return self.refined_[requirements]
 
     def expand (self):
         if not self.expanded_:
- expanded = feature.expand (self.raw_)
- self.expanded_ = create (expanded)
+ expanded = feature.expand(self.all_)
+ self.expanded_ = create(expanded)
         return self.expanded_
 
     def expand_componsite(self):
         if not self.componsites_:
- self.composites_ = create(feature.expand_composires(self.raw_))
+ self.composites_ = create(feature.expand_composires(self.all_raw_))
         return self.composites_
 
+ def expand_subfeature(self):
+ if not self.subfeatures_:
+ self.subfeatures_ = create(feature.expand_subfeatures(self.all_))
+ return self.subfeatures_
+
     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_,
+ property.evaluate_conditionals_in_context(self.all_raw_,
                                                           context.raw()))
 
         return self.evaluated_[context]
@@ -283,13 +320,37 @@
 
     def add_defaults (self):
         if not self.defaults_:
- expanded = feature.add_defaults(self.raw_)
+ expanded = feature.add_defaults(self.all_)
             self.defaults_ = create(expanded)
         return self.defaults_
 
     def as_path (self):
         if not self.as_path_:
- self.as_path_ = property.as_path(self.base_)
+
+ def path_order (p1, p2):
+
+ i1 = p1.feature().implicit()
+ i2 = p2.feature().implicit()
+
+ if i1 != i2:
+ return i2 - i1
+ else:
+ return cmp(p1.feature().name(), p2.feature().name())
+
+ # trim redundancy
+ properties = feature.minimize(self.base_)
+
+ # sort according to path_order
+ properties.sort (path_order)
+
+ components = []
+ for p in properties:
+ if p.feature().implicit():
+ components.append(p.value())
+ else:
+ components.append(p.feature().name() + "-" + p.value())
+
+ self.as_path_ = '/'.join (components)
 
         return self.as_path_
 
@@ -341,9 +402,9 @@
         """ 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)]
+ if not self.added_.has_key(ps):
+ self.added_[ps] = create(self.all_ + ps.all())
+ return self.added_[ps]
     
     def add_raw (self, properties):
         """ Creates a new property set containing the properties in this one,
@@ -353,16 +414,15 @@
 
     
     def get (self, feature):
- """ Returns all values of 'feature'.
+ """ Returns all properties for '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, ''))
+ for v in self.all_:
+ if not self.feature_map_.has_key(v.feature()):
+ self.feature_map_[v.feature()] = []
+ self.feature_map_[v.feature()].append(v)
         
- return self.feature_map_.get (feature, [])
+ return self.feature_map_.get(feature, [])
     

Modified: trunk/tools/build/v2/build/targets.py
==============================================================================
--- trunk/tools/build/v2/build/targets.py (original)
+++ trunk/tools/build/v2/build/targets.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -685,7 +685,7 @@
             # be an indication that
             # build_request.expand-no-defaults is the wrong rule
             # to use here.
- compressed = feature.compress-subproperties (raw)
+ compressed = feature.compress_subproperties (raw)
 
             properties = build_request.expand_no_defaults (compressed, defaults_to_apply)
               
@@ -873,13 +873,13 @@
         # 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 ())
+ 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)
+ key = (build_request, 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)
+ 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'.
@@ -909,7 +909,7 @@
     
         unconditional = feature.expand(requirements.non_conditional())
     
- raw = context.raw()
+ raw = context.all()
         raw = property.refine(raw, unconditional)
       
         # We've collected properties that surely must be present in common

Modified: trunk/tools/build/v2/build/toolset.py
==============================================================================
--- trunk/tools/build/v2/build/toolset.py (original)
+++ trunk/tools/build/v2/build/toolset.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -11,8 +11,9 @@
 """
 
 import feature, property, generators, property_set
+import b2.util.set
 from b2.util.utility import *
-from b2.util import set, bjam_signature
+from b2.util import bjam_signature
 
 __re_split_last_segment = re.compile (r'^(.+)\.([^\.])*')
 __re_two_ampersands = re.compile ('(&&)')
@@ -67,30 +68,13 @@
 # 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.
     
 @bjam_signature((["rule_or_module", "variable_name", "condition", "*"],
                  ["values", "*"]))
-def flags (rule_or_module, variable_name, condition, values = []):
+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.
@@ -140,63 +124,64 @@
         condition = None
     
     if condition:
- property.validate_property_sets (condition)
- condition = normalize_condition ([condition])
+ transformed = []
+ for c in condition:
+ # FIXME: 'split' might be a too raw tool here.
+ pl = [property.create_from_string(s) for s in c.split('/')]
+ pl = feature.expand_subfeatures(pl);
+ transformed.append(property_set.create(pl))
+ condition = transformed
+
+ property.validate_property_sets(condition)
     
     __add_flag (rule_or_module, variable_name, condition, values)
 
-def set_target_variables (manager, rule_or_module, targets, properties):
+def set_target_variables (manager, rule_or_module, targets, ps):
     """
     """
- key = rule_or_module + '.' + str (properties)
- settings = __stv.get (key, None)
+ settings = __stv.get(ps, None)
     if not settings:
- settings = __set_target_variables_aux (manager, rule_or_module, properties)
+ settings = __set_target_variables_aux(manager, rule_or_module, ps)
 
- __stv [key] = settings
+ __stv[ps] = 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):
+def find_satisfied_condition(conditions, ps):
     """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
+ features = set(p.feature() for p in ps.all())
+
+ for condition in conditions:
+
+ found_all = True
+ for i in condition.all():
+
+ found = False
+ if i.value():
+ found = i in ps.get(i.feature())
+ else:
+ # Handle value-less properties like '<architecture>' (compare with
+ # '<architecture>x86').
+ # 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
+ found = not i.feature() in features
+
+ found_all = found_all and found
+
+ if found_all:
+ return condition
 
     return None
     
@@ -241,7 +226,7 @@
     call it as needed."""
     for f in __module_flags.get(base, []):
         
- if not f.condition or set.difference(f.condition, prohibited_properties):
+ if not f.condition or b2.util.set.difference(f.condition, prohibited_properties):
             match = __re_first_group.match(f.rule)
             rule_ = None
             if match:
@@ -292,7 +277,7 @@
 ######################################################################################
 # Private functions
 
-def __set_target_variables_aux (manager, rule_or_module, properties):
+def __set_target_variables_aux (manager, rule_or_module, ps):
     """ 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.
@@ -301,12 +286,12 @@
 
     for f in __flags.get(rule_or_module, []):
            
- if not f.condition or find_property_subset (f.condition, properties):
+ if not f.condition or find_satisfied_condition (f.condition, ps):
             processed = []
             for v in f.values:
                 # The value might be <feature-name> so needs special
                 # treatment.
- processed += __handle_flag_value (manager, v, properties)
+ processed += __handle_flag_value (manager, v, ps)
 
             for r in processed:
                 result.append ((f.variable_name, r))
@@ -316,27 +301,28 @@
     
     if next:
         result.extend(__set_target_variables_aux(
- manager, next.group(1), properties))
+ manager, next.group(1), ps))
 
     return result
 
-def __handle_flag_value (manager, value, properties):
+def __handle_flag_value (manager, value, ps):
     result = []
     
     if get_grist (value):
- matches = property.select (value, properties)
- for p in matches:
- att = feature.attributes (get_grist (p))
-
- ungristed = replace_grist (p, '')
+ f = feature.get(value)
+ properties = ps.get(feature)
+
+ for p in properties:
+
+ value = p.value()
 
- if 'dependency' in att:
+ if f.dependency():
                 # 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 ())
+ result.append (manager.targets ().find (p.value()).actualize ())
 
- elif 'path' in att or 'free' in att:
+ elif f.path() or f.free():
                 values = []
                 
                 # Treat features with && in the value

Modified: trunk/tools/build/v2/build/virtual_target.py
==============================================================================
--- trunk/tools/build/v2/build/virtual_target.py (original)
+++ trunk/tools/build/v2/build/virtual_target.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -755,14 +755,12 @@
 
         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)
+ toolset.set_target_variables (self.manager_, self.action_name_, actual_targets, properties)
              
         engine = self.manager_.engine ()
         

Modified: trunk/tools/build/v2/build_system.py
==============================================================================
--- trunk/tools/build/v2/build_system.py (original)
+++ trunk/tools/build/v2/build_system.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -275,12 +275,10 @@
     (target_ids, properties) = b2.build.build_request.from_command_line(
         argv[1:] + extra_build_request)
 
+ properties = [property_set.create(feature.split(ps)) for ps in properties]
+
     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()]
 

Modified: trunk/tools/build/v2/tools/builtin.py
==============================================================================
--- trunk/tools/build/v2/tools/builtin.py (original)
+++ trunk/tools/build/v2/tools/builtin.py 2010-07-23 05:09:14 EDT (Fri, 23 Jul 2010)
@@ -47,39 +47,35 @@
     """
     parents = []
     if not explicit_properties:
- if get_grist (parents_or_properties [0]):
- explicit_properties = parents_or_properties
-
- else:
- parents = parents_or_properties
-
+ explicit_properties = parents_or_properties
     else:
         parents = parents_or_properties
+
+ inherited = property_set.empty()
+ if parents:
 
- # 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:
+ # If we allow multiple parents, we'd have to to check for conflicts
+ # between base variants, and there was no demand for so to bother.
+ if len (parents) > 1:
+ raise BaseException ("Multiple base variants are not yet supported")
+
+ p = parents[0]
         # 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]
+ inherited = __variant_explicit_properties[p]
 
- property.validate (explicit_properties)
- explicit_properties = property.refine (inherited, explicit_properties)
+ explicit_properties = property_set.create_with_validation(explicit_properties)
+ explicit_properties = inherited.refine(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
+ __variant_explicit_properties[name] = explicit_properties
            
     feature.extend('variant', [name])
- feature.compose (replace_grist (name, '<variant>'), explicit_properties)
+ feature.compose ("<variant>" + name, explicit_properties.all())
 
 __os_names = """
     amiga aix bsd cygwin darwin dos emx freebsd hpux iphone linux netbsd


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk