def subfeature(feature_name, value_string, subfeature, subvalues, attributes=[]): """ Declares a subfeature. feature_name: Root feature that is not a subfeature. value_string: An optional value-string specifying which feature or subfeature values this subfeature is specific to, if any. subfeature: The name of the subfeature being declared. subvalues: The allowed values of this subfeature. attributes: The attributes of the subfeature. """ feature_name = add_grist(feature_name) 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']: 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']) # Now make sure the subfeature values are known. extend_subfeature(feature_name, value_string, subfeature, subvalues)
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 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 subfeature (feature_name, value_string, subfeature, subvalues, attributes = []): """ Declares a subfeature. feature_name: Root feature that is not a subfeature. value_string: An optional value-string specifying which feature or subfeature values this subfeature is specific to, if any. subfeature: The name of the subfeature being declared. subvalues: The allowed values of this subfeature. attributes: The attributes of the subfeature. """ feature_name = add_grist (feature_name) 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']: 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']) # Now make sure the subfeature values are known. extend_subfeature (feature_name, value_string, subfeature, subvalues)
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 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 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 extend_subfeature (feature, value_string, subfeature, subvalues): feature = add_grist (feature) validate_feature (feature) if value_string: validate_value_string (feature, value_string) subfeature_name = __get_subfeature_name (subfeature, value_string) f = ungrist (feature) extend (f + '-' + subfeature_name, subvalues) ; __add_to_subfeature_value_to_name_map (feature, value_string, subfeature_name, subvalues)
def __expand_subfeatures_aux(feature, value, 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 subfeatures and their values. For example: expand_subfeatures <toolset>gcc-2.95.2-linux-x86 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 equivalent to: expand_subfeatures gcc-2.95.2-linux-x86 feature: The name of the feature, or empty if value corresponds to an implicit property 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) if not dont_validate: validate_value_string(feature, value) components = value.split("-") # get the top-level feature's value value = replace_grist(components[0], '') result = [replace_grist(components[0], feature)] subvalues = components[1:] while len(subvalues) > 0: subvalue = subvalues[0] # pop the head off of subvalues subvalues = subvalues[1:] subfeature = __find_implied_subfeature(feature, subvalue, value) # 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] f = ungrist(feature) # FIXME: why grist includes '<>'? result.append(replace_grist(subvalue, '<' + f + '-' + subfeature + '>')) 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_aux (feature, value, 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 subfeatures and their values. For example: expand_subfeatures <toolset>gcc-2.95.2-linux-x86 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86 equivalent to: expand_subfeatures gcc-2.95.2-linux-x86 feature: The name of the feature, or empty if value corresponds to an implicit property 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) if not dont_validate: validate_value_string(feature, value) components = value.split ("-") # get the top-level feature's value value = replace_grist(components[0], '') result = [ replace_grist(components[0], feature) ] subvalues = components[1:] while len(subvalues) > 0: subvalue = subvalues [0] # pop the head off of subvalues subvalues = subvalues [1:] subfeature = __find_implied_subfeature (feature, subvalue, value) # 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] f = ungrist (feature) # FIXME: why grist includes '<>'? result.append (replace_grist (subvalue, '<' + f + '-' + subfeature + '>')) return result
def get_prefix(package_name, requirements): specified = property.select("install-default-prefix", requirements) if specified: specified = ungrist(specified[0]) prefix = option.get("prefix", specified) requirements = property.change(requirements, "install-default-prefix", None) # Or some likely defaults if neither is given. if not prefix: if os.name == "nt": prefix = "C:\\" + package_name elif os.name == "posix": prefix = "/usr/local" return prefix
def install(name, package_name=None, requirements=[], binaries=[], libraries=[], headers=[]): requirements = requirements[:] binaries = binaries[:] libraries if not package_name: package_name = name if option.get("prefix"): # If --prefix is explicitly specified on the command line, # then we need wipe away any settings of libdir/includir that # is specified via options in config files. option.set("bindir", None) option.set("libdir", None) option.set("includedir", None) # If <install-source-root> is not specified, all headers are installed to # prefix/include, no matter what their relative path is. Sometimes that is # what is needed. install_source_root = property.select('install-source-root', requirements) if install_source_root: requirements = property.change(requirements, 'install-source-root', None) install_header_subdir = property.select('install-header-subdir', requirements) if install_header_subdir: install_header_subdir = ungrist(install_header_subdir[0]) requirements = property.change(requirements, 'install-header-subdir', None) # First, figure out all locations. Use the default if no prefix option # given. prefix = get_prefix(name, requirements) # Architecture dependent files. exec_locate = option.get("exec-prefix", prefix) # Binaries. bin_locate = option.get("bindir", os.path.join(prefix, "bin")) # Object code libraries. lib_locate = option.get("libdir", os.path.join(prefix, "lib")) # Source header files. include_locate = option.get("includedir", os.path.join(prefix, "include")) stage.install(name + "-bin", binaries, requirements + ["<location>" + bin_locate]) alias(name + "-lib", [name + "-lib-shared", name + "-lib-static"]) # Since the install location of shared libraries differs on universe # and cygwin, use target alternatives to make different targets. # We should have used indirection conditioanl requirements, but it's # awkward to pass bin-locate and lib-locate from there to another rule. alias(name + "-lib-shared", [name + "-lib-shared-universe"]) alias(name + "-lib-shared", [name + "-lib-shared-cygwin"], ["<target-os>cygwin"]) # For shared libraries, we install both explicitly specified one and the # shared libraries that the installed executables depend on. stage.install(name + "-lib-shared-universe", binaries + libraries, requirements + ["<location>" + lib_locate, "<install-dependencies>on", "<install-type>SHARED_LIB"]) stage.install(name + "-lib-shared-cygwin", binaries + libraries, requirements + ["<location>" + bin_locate, "<install-dependencies>on", "<install-type>SHARED_LIB"]) # For static libraries, we do not care about executable dependencies, since # static libraries are already incorporated into them. stage.install(name + "-lib-static", libraries, requirements + ["<location>" + lib_locate, "<install-dependencies>on", "<install-type>STATIC_LIB"]) stage.install(name + "-headers", headers, requirements \ + ["<location>" + os.path.join(include_locate, s) for s in install_header_subdir] + install_source_root) alias(name, [name + "-bin", name + "-lib", name + "-headers"]) pt = get_manager().projects().current() for subname in ["bin", "lib", "headers", "lib-shared", "lib-static", "lib-shared-universe", "lib-shared-cygwin"]: pt.mark_targets_as_explicit([name + "-" + subname])
def install(name, package_name=None, requirements=[], binaries=[], libraries=[], headers=[]): requirements = requirements[:] binaries = binaries[:] libraries if not package_name: package_name = name if option.get("prefix"): # If --prefix is explicitly specified on the command line, # then we need wipe away any settings of libdir/includir that # is specified via options in config files. option.set("bindir", None) option.set("libdir", None) option.set("includedir", None) # If <install-source-root> is not specified, all headers are installed to # prefix/include, no matter what their relative path is. Sometimes that is # what is needed. install_source_root = property.select('install-source-root', requirements) if install_source_root: requirements = property.change(requirements, 'install-source-root', None) install_header_subdir = property.select('install-header-subdir', requirements) if install_header_subdir: install_header_subdir = ungrist(install_header_subdir[0]) requirements = property.change(requirements, 'install-header-subdir', None) # First, figure out all locations. Use the default if no prefix option # given. prefix = get_prefix(name, requirements) # Architecture dependent files. exec_locate = option.get("exec-prefix", prefix) # Binaries. bin_locate = option.get("bindir", os.path.join(prefix, "bin")) # Object code libraries. lib_locate = option.get("libdir", os.path.join(prefix, "lib")) # Source header files. include_locate = option.get("includedir", os.path.join(prefix, "include")) stage.install(name + "-bin", binaries, requirements + ["<location>" + bin_locate]) alias(name + "-lib", [name + "-lib-shared", name + "-lib-static"]) # Since the install location of shared libraries differs on universe # and cygwin, use target alternatives to make different targets. # We should have used indirection conditioanl requirements, but it's # awkward to pass bin-locate and lib-locate from there to another rule. alias(name + "-lib-shared", [name + "-lib-shared-universe"]) alias(name + "-lib-shared", [name + "-lib-shared-cygwin"], ["<target-os>cygwin"]) # For shared libraries, we install both explicitly specified one and the # shared libraries that the installed executables depend on. stage.install( name + "-lib-shared-universe", binaries + libraries, requirements + [ "<location>" + lib_locate, "<install-dependencies>on", "<install-type>SHARED_LIB" ]) stage.install( name + "-lib-shared-cygwin", binaries + libraries, requirements + [ "<location>" + bin_locate, "<install-dependencies>on", "<install-type>SHARED_LIB" ]) # For static libraries, we do not care about executable dependencies, since # static libraries are already incorporated into them. stage.install( name + "-lib-static", libraries, requirements + [ "<location>" + lib_locate, "<install-dependencies>on", "<install-type>STATIC_LIB" ]) stage.install(name + "-headers", headers, requirements \ + ["<location>" + os.path.join(include_locate, s) for s in install_header_subdir] + install_source_root) alias(name, [name + "-bin", name + "-lib", name + "-headers"]) pt = get_manager().projects().current() for subname in [ "bin", "lib", "headers", "lib-shared", "lib-static", "lib-shared-universe", "lib-shared-cygwin" ]: pt.mark_targets_as_explicit([name + "-" + subname])