diff options
Diffstat (limited to 'jam-files/boost-build/build/property.py')
-rw-r--r-- | jam-files/boost-build/build/property.py | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/jam-files/boost-build/build/property.py b/jam-files/boost-build/build/property.py new file mode 100644 index 00000000..c4b13dbc --- /dev/null +++ b/jam-files/boost-build/build/property.py @@ -0,0 +1,593 @@ +# Status: ported, except for tests and --abbreviate-paths. +# Base revision: 64070 +# +# Copyright 2001, 2002, 2003 Dave Abrahams +# Copyright 2006 Rene Rivera +# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +import re +from b2.util.utility import * +from b2.build import feature +from b2.util import sequence, qualify_jam_action +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_split_conditional = re.compile (r'(.+):<(.+)') +__re_colon = re.compile (':') +__re_has_condition = re.compile (r':<') +__re_separate_condition_and_property = re.compile (r'(.*):(<.*)') + +class Property(object): + + __slots__ = ('_feature', '_value', '_condition') + + def __init__(self, f, value, condition = []): + if type(f) == type(""): + f = feature.get(f) + # At present, single property has a single value. + assert type(value) != type([]) + assert(f.free() or value.find(':') == -1) + self._feature = f + 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): + result = "<" + self._feature.name() + ">" + str(self._value) + if self._condition: + result = ",".join(str(p) for p in self._condition) + ':' + result + return result + + 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 = [] + import types + if not isinstance(s, types.StringType): + print type(s) + 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 create_from_strings(string_list, allow_condition=False): + + return [create_from_string(s, allow_condition) for s in string_list] + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __results + + # A cache of results from as_path + __results = {} + +reset () + + +def path_order (x, y): + """ Helper for as_path, below. Orders properties with the implicit ones + first, and within the two sections in alphabetical order of feature + name. + """ + if x == y: + return 0 + + xg = get_grist (x) + yg = get_grist (y) + + if yg and not xg: + return -1 + + elif xg and not yg: + return 1 + + else: + if not xg: + x = feature.expand_subfeatures([x]) + y = feature.expand_subfeatures([y]) + + if x < y: + return -1 + elif x > y: + return 1 + else: + return 0 + +def identify(string): + return string + +# 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 set + result = set() + + # Records all requirements. + required = {} + + # All the elements of requirements should be present in the result + # Record them so that we can handle 'properties'. + for r in requirements: + # Don't consider conditional requirements. + if not r.condition(): + required[r.feature()] = r + + for p in properties: + # Skip conditional properties + if p.condition(): + result.add(p) + # No processing for free properties + elif p.feature().free(): + result.add(p) + else: + if required.has_key(p.feature()): + result.add(required[p.feature()]) + else: + result.add(p) + + return sequence.unique(list(result) + requirements) + +def translate_paths (properties, path): + """ Interpret all path properties in 'properties' as relative to 'path' + The property values are assumed to be in system-specific form, and + will be translated into normalized form. + """ + result = [] + + for p in properties: + + if p.feature().path(): + values = __re_two_ampersands.split(p.value()) + + new_value = "&&".join(os.path.join(path, v) for v in values) + + if new_value != p.value(): + result.append(Property(p.feature(), new_value, p.condition())) + else: + result.append(p) + + else: + result.append (p) + + return result + +def translate_indirect(properties, context_module): + """Assumes that all feature values that start with '@' are + names of rules, used in 'context-module'. Such rules can be + either local to the module or global. Qualified local rules + with the name of the module.""" + result = [] + for p in properties: + if p.value()[0] == '@': + q = qualify_jam_action(p.value()[1:], context_module) + get_manager().engine().register_bjam_action(q) + result.append(Property(p.feature(), '@' + q, p.condition())) + else: + result.append(p) + + return result + +def validate (properties): + """ Exit with error if any of the properties is not valid. + properties may be a single property or a sequence of properties. + """ + + if isinstance (properties, str): + __validate1 (properties) + else: + for p in properties: + __validate1 (p) + +def expand_subfeatures_in_conditions (properties): + + result = [] + for p in properties: + + if not p.condition(): + result.append(p) + else: + expanded = [] + 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. + expanded.extend(feature.expand_subfeatures ([c], True)) + else: + expanded.extend(feature.expand_subfeatures([c])) + + result.append(Property(p.feature(), p.value(), expanded)) + + return result + +# FIXME: this should go +def split_conditional (property): + """ If 'property' is conditional property, returns + condition and the property, e.g + <variant>debug,<toolset>gcc:<inlining>full will become + <variant>debug,<toolset>gcc <inlining>full. + Otherwise, returns empty string. + """ + m = __re_split_conditional.match (property) + + if m: + return (m.group (1), '<' + m.group (2)) + + return None + + +def select (features, properties): + """ Selects properties which correspond to any of the given features. + """ + result = [] + + # add any missing angle brackets + features = add_grist (features) + + return [p for p in properties if get_grist(p) in features] + +def validate_property_sets (sets): + for s in sets: + validate(s.all()) + +def evaluate_conditionals_in_context (properties, context): + """ Removes all conditional properties which conditions are not met + For those with met conditions, removes the condition. Properies + in conditions are looked up in 'context' + """ + base = [] + conditional = [] + + for p in properties: + if p.condition(): + conditional.append (p) + else: + base.append (p) + + result = base[:] + for p in conditional: + + # Evaluate condition + # FIXME: probably inefficient + if all(x in context for x in p.condition()): + result.append(Property(p.feature(), p.value())) + + return result + + +def change (properties, feature, value = None): + """ Returns a modified version of properties with all values of the + given feature replaced by the given value. + If 'value' is None the feature will be removed. + """ + result = [] + + feature = add_grist (feature) + + for p in properties: + if get_grist (p) == feature: + if value: + result.append (replace_grist (value, feature)) + + else: + result.append (p) + + return result + + +################################################################ +# Private functions + +def __validate1 (property): + """ Exit with error if property is not valid. + """ + msg = None + + if not property.feature().free(): + feature.validate_value_string (property.feature(), property.value()) + + +################################################################### +# Still to port. +# Original lines are prefixed with "# " +# +# +# import utility : ungrist ; +# import sequence : unique ; +# import errors : error ; +# import feature ; +# import regex ; +# import sequence ; +# import set ; +# import path ; +# import assert ; +# +# + + +# rule validate-property-sets ( property-sets * ) +# { +# for local s in $(property-sets) +# { +# validate [ feature.split $(s) ] ; +# } +# } +# + +def remove(attributes, properties): + """Returns a property sets which include all the elements + in 'properties' that do not have attributes listed in 'attributes'.""" + + result = [] + for e in properties: + attributes_new = feature.attributes(get_grist(e)) + has_common_features = 0 + for a in attributes_new: + if a in attributes: + has_common_features = 1 + break + + if not has_common_features: + result += e + + return result + + +def take(attributes, properties): + """Returns a property set which include all + properties in 'properties' that have any of 'attributes'.""" + result = [] + for e in properties: + if b2.util.set.intersection(attributes, feature.attributes(get_grist(e))): + result.append(e) + return result + +def translate_dependencies(properties, project_id, location): + + result = [] + for p in properties: + + if not p.feature().dependency(): + result.append(p) + else: + v = p.value() + m = re.match("(.*)//(.*)", v) + if m: + rooted = m.group(1) + if rooted[0] == '/': + # Either project id or absolute Linux path, do nothing. + pass + else: + rooted = os.path.join(os.getcwd(), location, rooted) + + result.append(Property(p.feature(), rooted + "//" + m.group(2), p.condition())) + + elif os.path.isabs(v): + result.append(p) + else: + result.append(Property(p.feature(), project_id + "//" + v, p.condition())) + + return result + + +class PropertyMap: + """ Class which maintains a property set -> string mapping. + """ + def __init__ (self): + self.__properties = [] + self.__values = [] + + def insert (self, properties, value): + """ Associate value with properties. + """ + self.__properties.append(properties) + self.__values.append(value) + + def find (self, properties): + """ Return the value associated with properties + or any subset of it. If more than one + subset has value assigned to it, return the + value for the longest subset, if it's unique. + """ + return self.find_replace (properties) + + def find_replace(self, properties, value=None): + matches = [] + match_ranks = [] + + for i in range(0, len(self.__properties)): + p = self.__properties[i] + + if b2.util.set.contains (p, properties): + matches.append (i) + match_ranks.append(len(p)) + + best = sequence.select_highest_ranked (matches, match_ranks) + + if not best: + return None + + if len (best) > 1: + raise NoBestMatchingAlternative () + + best = best [0] + + original = self.__values[best] + + if value: + self.__values[best] = value + + return original + +# local rule __test__ ( ) +# { +# import errors : try catch ; +# import feature ; +# import feature : feature subfeature compose ; +# +# # local rules must be explicitly re-imported +# import property : path-order ; +# +# feature.prepare-test property-test-temp ; +# +# feature toolset : gcc : implicit symmetric ; +# subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 +# 3.0 3.0.1 3.0.2 : optional ; +# feature define : : free ; +# feature runtime-link : dynamic static : symmetric link-incompatible ; +# feature optimization : on off ; +# feature variant : debug release : implicit composite symmetric ; +# feature rtti : on off : link-incompatible ; +# +# compose <variant>debug : <define>_DEBUG <optimization>off ; +# compose <variant>release : <define>NDEBUG <optimization>on ; +# +# import assert ; +# import "class" : new ; +# +# validate <toolset>gcc <toolset>gcc-3.0.1 : $(test-space) ; +# +# assert.result <toolset>gcc <rtti>off <define>FOO +# : refine <toolset>gcc <rtti>off +# : <define>FOO +# : $(test-space) +# ; +# +# assert.result <toolset>gcc <optimization>on +# : refine <toolset>gcc <optimization>off +# : <optimization>on +# : $(test-space) +# ; +# +# assert.result <toolset>gcc <rtti>off +# : refine <toolset>gcc : <rtti>off : $(test-space) +# ; +# +# assert.result <toolset>gcc <rtti>off <rtti>off:<define>FOO +# : refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO +# : $(test-space) +# ; +# +# assert.result <toolset>gcc:<define>foo <toolset>gcc:<define>bar +# : refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar +# : $(test-space) +# ; +# +# assert.result <define>MY_RELEASE +# : evaluate-conditionals-in-context +# <variant>release,<rtti>off:<define>MY_RELEASE +# : <toolset>gcc <variant>release <rtti>off +# +# ; +# +# try ; +# validate <feature>value : $(test-space) ; +# catch "Invalid property '<feature>value': unknown feature 'feature'." ; +# +# try ; +# validate <rtti>default : $(test-space) ; +# catch \"default\" is not a known value of feature <rtti> ; +# +# validate <define>WHATEVER : $(test-space) ; +# +# try ; +# validate <rtti> : $(test-space) ; +# catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ; +# +# try ; +# validate value : $(test-space) ; +# catch "value" is not a value of an implicit feature ; +# +# +# assert.result <rtti>on +# : remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ; +# +# assert.result <include>a +# : select include : <include>a <toolset>gcc ; +# +# assert.result <include>a +# : select include bar : <include>a <toolset>gcc ; +# +# assert.result <include>a <toolset>gcc +# : select include <bar> <toolset> : <include>a <toolset>gcc ; +# +# assert.result <toolset>kylix <include>a +# : change <toolset>gcc <include>a : <toolset> kylix ; +# +# # Test ordinary properties +# assert.result +# : split-conditional <toolset>gcc +# ; +# +# # Test properties with ":" +# assert.result +# : split-conditional <define>FOO=A::B +# ; +# +# # Test conditional feature +# assert.result <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO +# : split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO +# ; +# +# feature.finish-test property-test-temp ; +# } +# + |