diff options
author | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
---|---|---|
committer | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
commit | 670a8f984fc6d8342180c59ae9e96b0b76f34d3d (patch) | |
tree | 9f2ce7eec1a77e56b3bb1ad0ad40f212d7a996b0 /jam-files/boost-build/build/property_set.py | |
parent | eb3ee28dc0eb1d3e5ed01ba0df843be329ae450d (diff) | |
parent | 2f64af3e06a518b93f7ca2c30a9d0aeb2c947031 (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'jam-files/boost-build/build/property_set.py')
-rw-r--r-- | jam-files/boost-build/build/property_set.py | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/jam-files/boost-build/build/property_set.py b/jam-files/boost-build/build/property_set.py new file mode 100644 index 00000000..f12eb90c --- /dev/null +++ b/jam-files/boost-build/build/property_set.py @@ -0,0 +1,449 @@ +# Status: ported. +# Base revision: 40480 + +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +from b2.util.utility import * +import property, feature, string +import b2.build.feature +from b2.exceptions import * +from b2.util.sequence import unique +from b2.util.set import difference +from b2.util import cached + +from b2.manager import get_manager + + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + """ + global __cache + + # A cache of property sets + # TODO: use a map of weak refs? + __cache = {} + +reset () + + +def create (raw_properties = []): + """ Creates a new 'PropertySet' instance for the given raw properties, + or returns an already existing one. + """ + # 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(x) + + return __cache [key] + +def create_with_validation (raw_properties): + """ Creates new 'PropertySet' instances after checking + that all properties are valid and converting incidental + properties into gristed form. + """ + properties = [property.create_from_string(s) for s in raw_properties] + property.validate(properties) + + return create(properties) + +def empty (): + """ Returns PropertySet with empty set of properties. + """ + return create () + +def create_from_user_input(raw_properties, jamfile_module, location): + """Creates a property-set from the input given by the user, in the + context of 'jamfile-module' at 'location'""" + + properties = property.create_from_strings(raw_properties, True) + properties = property.translate_paths(properties, location) + properties = property.translate_indirect(properties, jamfile_module) + + project_id = get_manager().projects().attributeDefault(jamfile_module, 'id', None) + if not project_id: + project_id = os.path.abspath(location) + properties = property.translate_dependencies(properties, project_id, location) + properties = property.expand_subfeatures_in_conditions(properties) + return create(properties) + + +def refine_from_user_input(parent_requirements, specification, jamfile_module, + location): + """Refines requirements with requirements provided by the user. + Specially handles "-<property>value" syntax in specification + to remove given requirements. + - parent-requirements -- property-set object with requirements + to refine + - specification -- string list of requirements provided by the use + - project-module -- the module to which context indirect features + will be bound. + - location -- the path to which path features are relative.""" + + + if not specification: + return parent_requirements + + + add_requirements = [] + remove_requirements = [] + + for r in specification: + if r[0] == '-': + remove_requirements.append(r[1:]) + else: + add_requirements.append(r) + + if remove_requirements: + # Need to create property set, so that path features + # and indirect features are translated just like they + # are in project requirements. + ps = create_from_user_input(remove_requirements, + jamfile_module, location) + + parent_requirements = create(difference(parent_requirements.all(), + ps.all())) + specification = add_requirements + + requirements = create_from_user_input(specification, + jamfile_module, location) + + return parent_requirements.refine(requirements) + +class PropertySet: + """ Class for storing a set of properties. + - there's 1<->1 correspondence between identity and value. No + two instances of the class are equal. To maintain this property, + the 'PropertySet.create' rule should be used to create new instances. + Instances are immutable. + + - each property is classified with regard to it's effect on build + results. Incidental properties have no effect on build results, from + Boost.Build point of view. Others are either free, or non-free, which we + call 'base'. Each property belong to exactly one of those categories and + it's possible to get list of properties in each category. + + In addition, it's possible to get list of properties with specific + attribute. + + - several operations, like and refine and as_path are provided. They all use + caching whenever possible. + """ + def __init__ (self, properties = []): + + + raw_properties = [] + for p in properties: + raw_properties.append(p.to_raw()) + + self.all_ = properties + self.all_raw_ = raw_properties + self.all_set_ = set(properties) + + self.incidental_ = [] + self.free_ = [] + self.base_ = [] + self.dependency_ = [] + self.non_dependency_ = [] + self.conditional_ = [] + self.non_conditional_ = [] + self.propagated_ = [] + self.link_incompatible = [] + + # A cache of refined properties. + self.refined_ = {} + + # A cache of property sets created by adding properties to this one. + self.added_ = {} + + # Cache for the default properties. + self.defaults_ = None + + # Cache for the expanded properties. + self.expanded_ = None + + # Cache for the expanded composite properties + self.composites_ = None + + # Cache for property set with expanded subfeatures + self.subfeatures_ = None + + # Cache for the property set containing propagated properties. + self.propagated_ps_ = None + + # A map of features to its values. + self.feature_map_ = None + + # A tuple (target path, is relative to build directory) + self.target_path_ = None + + self.as_path_ = None + + # A cache for already evaluated sets. + self.evaluated_ = {} + + for p in raw_properties: + if not get_grist (p): + raise BaseException ("Invalid property: '%s'" % p) + + att = feature.attributes (get_grist (p)) + + if 'propagated' in att: + self.propagated_.append (p) + + if 'link_incompatible' in att: + self.link_incompatible.append (p) + + for p in properties: + + # A feature can be both incidental and free, + # in which case we add it to incidental. + 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) + + if p.feature().dependency(): + self.dependency_.append (p) + else: + self.non_dependency_.append (p) + + + def all(self): + return self.all_ + + def raw (self): + """ Returns the list of stored properties. + """ + return self.all_raw_ + + def __str__(self): + return ' '.join(str(p) for p in self.all_) + + def base (self): + """ Returns properties that are neither incidental nor free. + """ + return self.base_ + + def free (self): + """ Returns free properties which are not dependency properties. + """ + return self.free_ + + def non_free(self): + return self.base_ + self.incidental_ + + def dependency (self): + """ Returns dependency properties. + """ + return self.dependency_ + + def non_dependency (self): + """ Returns properties that are not dependencies. + """ + return self.non_dependency_ + + def conditional (self): + """ Returns conditional properties. + """ + return self.conditional_ + + def non_conditional (self): + """ Returns properties that are not conditional. + """ + return self.non_conditional_ + + def incidental (self): + """ Returns incidental properties. + """ + return self.incidental_ + + def refine (self, requirements): + """ Refines this set's properties using the requirements passed as an argument. + """ + assert isinstance(requirements, PropertySet) + if not self.refined_.has_key (requirements): + r = property.refine(self.all_, requirements.all_) + + self.refined_[requirements] = create(r) + + return self.refined_[requirements] + + def expand (self): + if not self.expanded_: + expanded = feature.expand(self.all_) + self.expanded_ = create(expanded) + return self.expanded_ + + def expand_subfeatures(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): + # FIXME: figure why the call messes up first parameter + self.evaluated_[context] = create( + property.evaluate_conditionals_in_context(self.all(), context)) + + return self.evaluated_[context] + + def propagated (self): + if not self.propagated_ps_: + self.propagated_ps_ = create (self.propagated_) + return self.propagated_ps_ + + def add_defaults (self): + # FIXME: this caching is invalidated when new features + # are declare inside non-root Jamfiles. + if not self.defaults_: + expanded = feature.add_defaults(self.all_) + self.defaults_ = create(expanded) + return self.defaults_ + + def as_path (self): + if not self.as_path_: + + 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_ + + def target_path (self): + """ Computes the target path that should be used for + target with these properties. + Returns a tuple of + - the computed path + - if the path is relative to build directory, a value of + 'true'. + """ + if not self.target_path_: + # The <location> feature can be used to explicitly + # change the location of generated targets + l = self.get ('<location>') + if l: + computed = l[0] + is_relative = False + + else: + p = self.as_path () + + # Really, an ugly hack. Boost regression test system requires + # specific target paths, and it seems that changing it to handle + # other directory layout is really hard. For that reason, + # we teach V2 to do the things regression system requires. + # The value o '<location-prefix>' is predended to the path. + prefix = self.get ('<location-prefix>') + + if prefix: + if len (prefix) > 1: + raise AlreadyDefined ("Two <location-prefix> properties specified: '%s'" % prefix) + + computed = os.path.join(prefix[0], p) + + else: + computed = p + + if not computed: + computed = "." + + is_relative = True + + self.target_path_ = (computed, is_relative) + + return self.target_path_ + + def add (self, ps): + """ Creates a new property set containing the properties in this one, + plus the ones of the property set passed as argument. + """ + if not self.added_.has_key(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, + plus the ones passed as argument. + """ + return self.add (create (properties)) + + + def get (self, feature): + """ Returns all values of 'feature'. + """ + if type(feature) == type([]): + feature = feature[0] + if not isinstance(feature, b2.build.feature.Feature): + feature = b2.build.feature.get(feature) + + if not self.feature_map_: + self.feature_map_ = {} + + 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.value()) + + return self.feature_map_.get(feature, []) + + @cached + def get_properties(self, feature): + """Returns all contained properties associated with 'feature'""" + + if not isinstance(feature, b2.build.feature.Feature): + feature = b2.build.feature.get(feature) + + result = [] + for p in self.all_: + if p.feature() == feature: + result.append(p) + return result + + def __contains__(self, item): + return item in self.all_set_ + |