|
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