def expand_composites (properties): """ Expand all composite properties in the set so that all components are explicitly expressed. """ explicit_features = get_grist (properties) result = [] # now expand composite features for p in properties: expanded = expand_composite (p) for x in expanded: if not x in result: f = get_grist (x) if f in __free_features: 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)) else: result.append (x) elif f in get_grist (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, ''))) else: result.append (x) return result
def split_one (properties): pieces = re.split (__re_slash_or_backslash, properties) result = [] for x in pieces: if not get_grist (x) and len (result) > 0 and get_grist (result [-1]): result = result [0:-1] + [ result [-1] + '/' + x ] else: result.append (x) return result
def add_defaults (properties): """ Given a set of properties, add default values for features not represented in the set. Note: if there's there's ordinary feature F1 and composite feature F2, which includes some value for F1, and both feature have default values, then the default value of F1 will be added, not the value in F2. This might not be right idea: consider feature variant : debug ... ; <variant>debug : .... <runtime-debugging>on feature <runtime-debugging> : off on ; Here, when adding default for an empty property set, we'll get <variant>debug <runtime_debugging>off 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 # 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)) return result
def expand_subfeatures (properties, dont_validate = False): """ Make all elements of properties corresponding to implicit features explicit, and express all subfeature values as separate properties in their own right. For example, the property gcc-2.95.2-linux-x86 might expand to <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 properties: A sequence with elements of the form <feature>value-string or just value-string in the case of implicit features. : dont_validate: If True, no validation of value string will be done. """ result = [] for p in properties: p_grist = get_grist (p) # Don't expand subfeatures in subfeatures if ':' in p_grist: result.append (p) else: result.extend (__expand_subfeatures_aux (p_grist, replace_grist (p, ''), dont_validate)) return result
def compress_subproperties (properties): """ Combine all subproperties into their parent properties Requires: for every subproperty, there is a parent property. All features are explicitly expressed. This rule probably shouldn't be needed, but build-request.expand-no-defaults is being abused for unintended purposes and it needs help """ result = [] matched_subs = [] for p in properties: pg = get_grist (p) if not pg: raise BaseException ("Gristed variable exppected. Got '%s'." % p) if not 'subfeature' in __all_features [pg]['attributes']: subs = __select_subproperties (p, properties) matched_subs.extend (subs) subvalues = '-'.join (get_value (subs)) if subvalues: subvalues = '-' + subvalues result.append (p + subvalues) else: all_subs.append (p) # TODO: this variables are used just for debugging. What's the overhead? assert (set.equal (all_subs, matched_subs)) return result
def actualize (self, scanner = None): """ Generates all the actual targets and sets up build actions for this target. If 'scanner' is specified, creates an additional target with the same location as actual target, which will depend on the actual target and be associated with 'scanner'. That additional target is returned. See the docs (#dependency_scanning) for rationale. Target must correspond to a file if 'scanner' is specified. If scanner is not specified, then actual target is returned. """ actual_name = self.actualize_no_scanner () if not scanner: return actual_name else: # Add the scanner instance to the grist for name. g = '-'.join ([ungrist(get_grist(actual_name)), str(id(scanner))]) name = replace_grist (actual_name, '<' + g + '>') if not self.made_.has_key (name): self.made_ [name] = True self.project_.manager ().engine ().add_dependency (name, actual_name) self.actualize_location (name) self.project_.manager ().scanners ().install (scanner, name, str (self)) return name
def add_prefix_and_suffix(specified_name, type, property_set): """Appends the suffix appropriate to 'type/property-set' combination to the specified name and returns the result.""" property_set = b2.util.jam_to_value_maybe(property_set) suffix = "" if type: suffix = b2.build.type.generated_target_suffix(type, property_set) # Handle suffixes for which no leading dot is desired. Those are # specified by enclosing them in <...>. Needed by python so it # can create "_d.so" extensions, for example. if get_grist(suffix): suffix = ungrist(suffix) elif suffix: suffix = "." + suffix prefix = "" if type: prefix = b2.build.type.generated_target_prefix(type, property_set) if specified_name.startswith(prefix): prefix = "" if not prefix: prefix = "" if not suffix: suffix = "" return prefix + specified_name + suffix
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']: return False specific_subfeature = __re_split_subfeatures.match (f) 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)) if top_sub [1] and add_grist (top_sub [0]) == get_grist (parent_property): return True return False
def get_values (feature, properties): """ Returns all values of the given feature specified by the given property set. """ result = [] for p in properties: if get_grist (p) == feature: result.append (replace_grist (p, '')) return result
def get_values(feature, properties): """ Returns all values of the given feature specified by the given property set. """ if feature[0] != '<': feature = '<' + feature + '>' result = [] for p in properties: if get_grist(p) == feature: result.append(replace_grist(p, '')) return result
def expand_composites(properties): """ Expand all composite properties in the set so that all components are explicitly expressed. """ explicit_features = get_grist(properties) result = [] # now expand composite features for p in properties: expanded = expand_composite(p) for x in expanded: if not x in result: f = get_grist(x) if f in __free_features: 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)) else: result.append(x) elif f in get_grist(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, ''))) else: result.append(x) return result
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 __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 compose (composite_property, component_properties): """ Sets the components of the given composite property. """ component_properties = to_seq (component_properties) feature = get_grist (composite_property) if not 'composite' in attributes (feature): raise BaseException ("'%s' is not a composite feature" % 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 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
def actualize(self, scanner=None): """ Generates all the actual targets and sets up build actions for this target. If 'scanner' is specified, creates an additional target with the same location as actual target, which will depend on the actual target and be associated with 'scanner'. That additional target is returned. See the docs (#dependency_scanning) for rationale. Target must correspond to a file if 'scanner' is specified. If scanner is not specified, then actual target is returned. """ if __debug__: from .scanner import Scanner assert scanner is None or isinstance(scanner, Scanner) actual_name = self.actualize_no_scanner() if self.always_: bjam.call("ALWAYS", actual_name) if not scanner: return actual_name else: # Add the scanner instance to the grist for name. g = '-'.join([ungrist(get_grist(actual_name)), str(id(scanner))]) name = replace_grist(actual_name, '<' + g + '>') if name not in self.made_: self.made_[name] = True self.project_.manager().engine().add_dependency( name, actual_name) self.actualize_location(name) self.project_.manager().scanners().install( scanner, name, str(self)) return name
def add_prefix_and_suffix(specified_name, type, property_set): """Appends the suffix appropriate to 'type/property-set' combination to the specified name and returns the result.""" suffix = b2.build.type.generated_target_suffix(type, property_set) # Handle suffixes for which no leading dot is desired. Those are # specified by enclosing them in <...>. Needed by python so it # can create "_d.so" extensions, for example. if get_grist(suffix): suffix = ungrist(suffix) elif suffix: suffix = "." + suffix prefix = b2.build.type.generated_target_prefix(type, property_set) if specified_name.startswith(prefix): prefix = "" if not prefix: prefix = "" if not suffix: suffix = "" return prefix + specified_name + suffix
def minimize(properties): """ Given an expanded property set, eliminate all redundancy: properties which are elements of other (composite) properties in the set will be eliminated. Non-symmetric properties equal to default values will be eliminated, unless the override a value from some composite property. Implicit properties will be expressed without feature 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) # handle subfeatures and implicit features x = __move_subfeatures_to_the_end(x) 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 # locate all subproperties of $(x[1]) in the property set subproperties = __select_subproperties(fullp, x) if subproperties: # reconstitute the joined property name subproperties.sort() joined = p + '-' + '-'.join(replace_grist(subproperties, '')) result.append(joined) x = set.difference(x[1:], subproperties) else: # eliminate properties whose value is equal to feature's # default and which are not symmetric and which do not # contradict values implied by composite properties. # since all component properties of composites in the set # 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): result.append(p) x = x[1:] return result
def __is_subproperty_of(parent_property, p): """ As is_subfeature_of, for subproperties. """ return is_subfeature_of(parent_property, get_grist(p))
def __is_subproperty_of (parent_property, p): """ As is_subfeature_of, for subproperties. """ return is_subfeature_of (parent_property, get_grist (p))
def minimize (properties): """ Given an expanded property set, eliminate all redundancy: properties which are elements of other (composite) properties in the set will be eliminated. Non-symmetric properties equal to default values will be eliminated, unless the override a value from some composite property. Implicit properties will be expressed without feature 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) # handle subfeatures and implicit features x = __move_subfeatures_to_the_end (x) 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 # locate all subproperties of $(x[1]) in the property set subproperties = __select_subproperties (fullp, x) if subproperties: # reconstitute the joined property name subproperties.sort () joined = p + '-' + '-'.join (replace_grist (subproperties, '')) result.append (joined) x = set.difference (x [1:], subproperties) else: # eliminate properties whose value is equal to feature's # default and which are not symmetric and which do not # contradict values implied by composite properties. # since all component properties of composites in the set # 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): result.append (p) x = x [1:] return result