2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-14 00:32:11 +00:00

Add type checking to help ease porting.

- Adds python optimization by default with a "-z" disable flag.
- Add type checking to bjam_signature and type checking asserts.
- Fix a type assertion in testing.
This commit is contained in:
Aaron Boman
2015-09-15 09:28:11 -05:00
committed by Vladimir Prus
parent 67d0547451
commit b04bfcf171
24 changed files with 1161 additions and 305 deletions

View File

@@ -29,7 +29,7 @@ import targets
import property_set
from b2.manager import get_manager
from b2.util import metatarget
from b2.util import metatarget, is_iterable_typed
class AliasTarget(targets.BasicTarget):
@@ -37,9 +37,17 @@ class AliasTarget(targets.BasicTarget):
targets.BasicTarget.__init__(self, *args)
def construct(self, name, source_targets, properties):
if __debug__:
from .virtual_target import VirtualTarget
assert isinstance(name, basestring)
assert is_iterable_typed(source_targets, VirtualTarget)
assert isinstance(properties, property_set.PropertySet)
return [property_set.empty(), source_targets]
def compute_usage_requirements(self, subvariant):
if __debug__:
from .virtual_target import Subvariant
assert isinstance(subvariant, Subvariant)
base = targets.BasicTarget.compute_usage_requirements(self, subvariant)
# Add source's usage requirement. If we don't do this, "alias" does not
# look like 100% alias.
@@ -47,7 +55,11 @@ class AliasTarget(targets.BasicTarget):
@metatarget
def alias(name, sources=[], requirements=[], default_build=[], usage_requirements=[]):
assert isinstance(name, basestring)
assert is_iterable_typed(sources, basestring)
assert is_iterable_typed(requirements, basestring)
assert is_iterable_typed(default_build, basestring)
assert is_iterable_typed(usage_requirements, basestring)
project = get_manager().projects().current()
targets = get_manager().targets()

View File

@@ -11,12 +11,14 @@ import b2.build.feature
feature = b2.build.feature
from b2.util.utility import *
from b2.util import is_iterable_typed
import b2.build.property_set as property_set
def expand_no_defaults (property_sets):
""" Expand the given build request by combining all property_sets which don't
specify conflicting non-free features.
"""
assert is_iterable_typed(property_sets, property_set.PropertySet)
# First make all features and subfeatures explicit
expanded_property_sets = [ps.expand_subfeatures() for ps in property_sets]
@@ -30,6 +32,7 @@ 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.
"""
assert is_iterable_typed(property_sets, property_set.PropertySet)
x_product_seen = set()
return __x_product_aux (property_sets, x_product_seen)[0]
@@ -44,6 +47,8 @@ def __x_product_aux (property_sets, seen_features):
have the same feature, and no Property is for feature in seen_features.
- set of features we saw in property_sets
"""
assert is_iterable_typed(property_sets, property_set.PropertySet)
assert isinstance(seen_features, set)
if not property_sets:
return ([], set())
@@ -90,6 +95,7 @@ def __x_product_aux (property_sets, seen_features):
def looks_like_implicit_value(v):
"""Returns true if 'v' is either implicit value, or
the part before the first '-' symbol is implicit value."""
assert isinstance(v, basestring)
if feature.is_implicit_value(v):
return 1
else:
@@ -104,7 +110,7 @@ def from_command_line(command_line):
and constructs build request from it. Returns a list of two
lists. First is the set of targets specified in the command line,
and second is the set of requested build properties."""
assert is_iterable_typed(command_line, basestring)
targets = []
properties = []
@@ -122,7 +128,7 @@ def from_command_line(command_line):
# Converts one element of command line build request specification into
# internal form.
def convert_command_line_element(e):
assert isinstance(e, basestring)
result = None
parts = e.split("/")
for p in parts:

View File

@@ -16,11 +16,11 @@
import b2.build.property as property
import b2.build.property_set as property_set
import b2.build.targets
from b2.build import targets as targets_
from b2.manager import get_manager
from b2.util.sequence import unique
from b2.util import bjam_signature, value_to_jam
from b2.util import bjam_signature, value_to_jam, is_iterable
import bjam
import os
@@ -41,17 +41,22 @@ __log_fd = -1
def register_components(components):
"""Declare that the components specified by the parameter exist."""
assert is_iterable(components)
__components.extend(components)
def components_building(components):
"""Declare that the components specified by the parameters will be build."""
assert is_iterable(components)
__built_components.extend(components)
def log_component_configuration(component, message):
"""Report something about component configuration that the user should better know."""
assert isinstance(component, basestring)
assert isinstance(message, basestring)
__component_logs.setdefault(component, []).append(message)
def log_check_result(result):
assert isinstance(result, basestring)
global __announced_checks
if not __announced_checks:
print "Performing configuration checks"
@@ -60,7 +65,9 @@ def log_check_result(result):
print result
def log_library_search_result(library, result):
log_check_result((" - %(library)s : %(result)s" % locals()).rjust(width))
assert isinstance(library, basestring)
assert isinstance(result, basestring)
log_check_result((" - %(library)s : %(result)s" % locals()).rjust(__width))
def print_component_configuration():
@@ -84,6 +91,10 @@ def builds(metatarget_reference, project, ps, what):
# Attempt to build a metatarget named by 'metatarget-reference'
# in context of 'project' with properties 'ps'.
# Returns non-empty value if build is OK.
assert isinstance(metatarget_reference, basestring)
assert isinstance(project, targets_.ProjectTarget)
assert isinstance(ps, property_set.PropertySet)
assert isinstance(what, basestring)
result = []
@@ -93,7 +104,7 @@ def builds(metatarget_reference, project, ps, what):
result = False
__builds_cache[(what, ps)] = False
targets = b2.build.targets.generate_from_reference(
targets = targets_.generate_from_reference(
metatarget_reference, project, ps).targets()
jam_targets = []
for t in targets:
@@ -112,6 +123,7 @@ def builds(metatarget_reference, project, ps, what):
return existing
def set_log_file(log_file_name):
assert isinstance(log_file_name, basestring)
# Called by Boost.Build startup code to specify name of a file
# that will receive results of configure checks. This
# should never be called by users.
@@ -134,7 +146,7 @@ class CheckTargetBuildsWorker:
self.false_properties = property.create_from_strings(false_properties, True)
def check(self, ps):
assert isinstance(ps, property_set.PropertySet)
# FIXME: this should not be hardcoded. Other checks might
# want to consider different set of features as relevant.
toolset = ps.get('toolset')[0]

View File

@@ -10,46 +10,50 @@ import operator
import re
import b2.build.property_set as property_set
import b2.util
class BjamAction:
from b2.util import set_jam_action, is_iterable
class BjamAction(object):
"""Class representing bjam action defined from Python."""
def __init__(self, action_name, function):
assert isinstance(action_name, basestring)
assert callable(function) or function is None
self.action_name = action_name
self.function = function
def __call__(self, targets, sources, property_set):
def __call__(self, targets, sources, property_set_):
assert is_iterable(targets)
assert is_iterable(sources)
assert isinstance(property_set_, property_set.PropertySet)
# Bjam actions defined from Python have only the command
# to execute, and no associated jam procedural code. So
# passing 'property_set' to it is not necessary.
bjam_interface.call("set-update-action", self.action_name,
targets, sources, [])
if self.function:
self.function(targets, sources, property_set)
self.function(targets, sources, property_set_)
class BjamNativeAction:
class BjamNativeAction(BjamAction):
"""Class representing bjam action defined by Jam code.
We still allow to associate a Python callable that will
be called when this action is installed on any target.
"""
def __init__(self, action_name, function):
self.action_name = action_name
self.function = function
def __call__(self, targets, sources, property_set):
def __call__(self, targets, sources, property_set_):
assert is_iterable(targets)
assert is_iterable(sources)
assert isinstance(property_set_, property_set.PropertySet)
if self.function:
self.function(targets, sources, property_set)
self.function(targets, sources, property_set_)
p = []
if property_set:
p = property_set.raw()
p = property_set_.raw()
set_jam_action(self.action_name, targets, sources, p)
b2.util.set_jam_action(self.action_name, targets, sources, p)
action_modifiers = {"updated": 0x01,
"together": 0x02,
"ignore": 0x04,
@@ -77,6 +81,8 @@ class Engine:
targets = [targets]
if isinstance (sources, str):
sources = [sources]
assert is_iterable(targets)
assert is_iterable(sources)
for target in targets:
for source in sources:
@@ -105,6 +111,11 @@ class Engine:
echo [ on $(targets) return $(MY-VAR) ] ;
"Hello World"
"""
if isinstance(targets, str):
targets = [targets]
assert is_iterable(targets)
assert isinstance(variable, basestring)
return bjam_interface.call('get-target-variable', targets, variable)
def set_target_variable (self, targets, variable, value, append=0):
@@ -114,13 +125,19 @@ class Engine:
where to generate targets, and will also be available to
updating rule for that 'taret'.
"""
if isinstance (targets, str):
if isinstance (targets, str):
targets = [targets]
if isinstance(value, str):
value = [value]
assert is_iterable(targets)
assert isinstance(variable, basestring)
assert is_iterable(value)
for target in targets:
self.do_set_target_variable (target, variable, value, append)
def set_update_action (self, action_name, targets, sources, properties=property_set.empty()):
def set_update_action (self, action_name, targets, sources, properties=None):
""" Binds a target to the corresponding update action.
If target needs to be updated, the action registered
with action_name will be used.
@@ -128,9 +145,17 @@ class Engine:
either 'register_action' or 'register_bjam_action'
method.
"""
assert(isinstance(properties, property_set.PropertySet))
if isinstance (targets, str):
if isinstance(targets, str):
targets = [targets]
if isinstance(sources, str):
sources = [sources]
if properties is None:
properties = property_set.empty()
assert isinstance(action_name, basestring)
assert is_iterable(targets)
assert is_iterable(sources)
assert(isinstance(properties, property_set.PropertySet))
self.do_set_update_action (action_name, targets, sources, properties)
def register_action (self, action_name, command, bound_list = [], flags = [],
@@ -149,11 +174,14 @@ class Engine:
This function will be called by set_update_action, and can
set additional target variables.
"""
assert isinstance(action_name, basestring)
assert isinstance(command, basestring)
assert is_iterable(bound_list)
assert is_iterable(flags)
assert function is None or callable(function)
if self.actions.has_key(action_name):
raise "Bjam action %s is already defined" % action_name
assert(isinstance(flags, list))
bjam_flags = reduce(operator.or_,
(action_modifiers[flag] for flag in flags), 0)
@@ -178,25 +206,37 @@ class Engine:
# action name. This way, jamfile rules that take action names
# can just register them without specially checking if
# action is already registered.
assert isinstance(action_name, basestring)
assert function is None or callable(function)
if not self.actions.has_key(action_name):
self.actions[action_name] = BjamNativeAction(action_name, function)
# Overridables
def do_set_update_action (self, action_name, targets, sources, property_set):
def do_set_update_action (self, action_name, targets, sources, property_set_):
assert isinstance(action_name, basestring)
assert is_iterable(targets)
assert is_iterable(sources)
assert isinstance(property_set_, property_set.PropertySet)
action = self.actions.get(action_name)
if not action:
raise Exception("No action %s was registered" % action_name)
action(targets, sources, property_set)
action(targets, sources, property_set_)
def do_set_target_variable (self, target, variable, value, append):
assert isinstance(target, basestring)
assert isinstance(variable, basestring)
assert is_iterable(value)
assert isinstance(append, int) # matches bools
if append:
bjam_interface.call("set-target-variable", target, variable, value, "true")
else:
bjam_interface.call("set-target-variable", target, variable, value)
def do_add_dependency (self, target, source):
assert isinstance(target, basestring)
assert isinstance(source, basestring)
bjam_interface.call("DEPENDS", target, source)

View File

@@ -9,7 +9,7 @@
import re
from b2.util import utility, bjam_signature
from b2.util import utility, bjam_signature, is_iterable_typed
import b2.util.set
from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, to_seq
from b2.exceptions import *
@@ -25,6 +25,9 @@ class Feature(object):
_attribute_name_to_integer = {}
def __init__(self, name, values, attributes):
assert isinstance(name, basestring)
assert is_iterable_typed(values, basestring)
assert is_iterable_typed(attributes, basestring)
self._name = name
self._values = values
self._default = None
@@ -42,12 +45,14 @@ class Feature(object):
return self._values
def add_values(self, values):
assert is_iterable_typed(values, basestring)
self._values.extend(values)
def attributes(self):
return self._attributes
def set_default(self, value):
assert isinstance(value, basestring)
for attr in ('free', 'optional'):
if getattr(self, attr)():
get_manager().errors()('"{}" feature "<{}>" cannot have a default value.'
@@ -66,6 +71,7 @@ class Feature(object):
return self._subfeatures
def add_subfeature(self, name):
assert isinstance(name, Feature)
self._subfeatures.append(name)
def parent(self):
@@ -77,6 +83,8 @@ class Feature(object):
return self._parent
def set_parent(self, feature, value):
assert isinstance(feature, Feature)
assert isinstance(value, basestring)
self._parent = (feature, value)
def __str__(self):
@@ -151,6 +159,7 @@ def get(name):
Throws if no feature by such name exists
"""
assert isinstance(name, basestring)
return __all_features[name]
# FIXME: prepare-test/finish-test?
@@ -213,9 +222,10 @@ def set_default (feature, value):
def defaults(features):
""" Returns the default property values for the given features.
"""
assert is_iterable_typed(features, Feature)
# FIXME: should merge feature and property modules.
import property
from . import property
result = []
for f in features:
if not f.free() and not f.optional() and f.default():
@@ -226,21 +236,22 @@ def defaults(features):
def valid (names):
""" Returns true iff all elements of names are valid features.
"""
def valid_one (name): return __all_features.has_key (name)
if isinstance (names, str):
return valid_one (names)
else:
return all([ valid_one (name) for name in names ])
if isinstance(names, str):
names = [names]
assert is_iterable_typed(names, basestring)
return all(name in __all_features for name in names)
def attributes (feature):
""" Returns the attributes of the given feature.
"""
assert isinstance(feature, basestring)
return __all_features[feature].attributes_string_list()
def values (feature):
""" Return the values of the given feature.
"""
assert isinstance(feature, basestring)
validate_feature (feature)
return __all_features[feature].values()
@@ -248,7 +259,7 @@ def is_implicit_value (value_string):
""" Returns true iff 'value_string' is a value_string
of an implicit feature.
"""
assert isinstance(value_string, basestring)
if __implicit_features.has_key(value_string):
return __implicit_features[value_string]
@@ -268,6 +279,7 @@ def is_implicit_value (value_string):
def implied_feature (implicit_value):
""" Returns the implicit feature associated with the given implicit value.
"""
assert isinstance(implicit_value, basestring)
components = implicit_value.split('-')
if not __implicit_features.has_key(components[0]):
@@ -276,15 +288,14 @@ def implied_feature (implicit_value):
return __implicit_features[components[0]]
def __find_implied_subfeature (feature, subvalue, value_string):
#if value_string == None: value_string = ''
assert isinstance(feature, Feature)
assert isinstance(subvalue, basestring)
assert isinstance(value_string, basestring)
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):
try:
return __subfeature_from_value[feature][value_string][subvalue]
except KeyError:
return None
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
@@ -294,6 +305,9 @@ def __find_implied_subfeature (feature, subvalue, value_string):
# value-string # The value of the main feature
def implied_subfeature (feature, subvalue, value_string):
assert isinstance(feature, Feature)
assert isinstance(subvalue, basestring)
assert isinstance(value_string, basestring)
result = __find_implied_subfeature (feature, subvalue, value_string)
if not result:
raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string))
@@ -303,28 +317,20 @@ def implied_subfeature (feature, subvalue, value_string):
def validate_feature (name):
""" Checks if all name is a valid feature. Otherwise, raises an exception.
"""
assert isinstance(name, basestring)
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.
"""
def valid_one (name): return __all_features.has_key (name)
if isinstance (names, str):
return valid_one (names)
else:
return [ valid_one (name) for name in names ]
# Uses Property
def __expand_subfeatures_aux (property, dont_validate = False):
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
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:
@@ -334,8 +340,12 @@ def __expand_subfeatures_aux (property, dont_validate = False):
value: The value of the feature.
dont_validate: If True, no validation of value string will be done.
"""
f = property.feature()
v = property.value()
from . import property # no __debug__ since Property is used elsewhere
assert isinstance(property_, property.Property)
assert isinstance(dont_validate, int) # matches bools
f = property_.feature()
v = property_.value()
if not dont_validate:
validate_value_string(f, v)
@@ -343,10 +353,8 @@ def __expand_subfeatures_aux (property, dont_validate = False):
v = components[0]
import property
result = [property.Property(f, components[0])]
result = [property.Property(f, components[0])]
subvalues = components[1:]
while len(subvalues) > 0:
@@ -380,6 +388,10 @@ def expand_subfeatures(properties, dont_validate = False):
case of implicit features.
: dont_validate: If True, no validation of value string will be done.
"""
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
assert isinstance(dont_validate, int) # matches bools
result = []
for p in properties:
# Don't expand subfeatures in subfeatures
@@ -413,6 +425,8 @@ def expand_subfeatures(properties, dont_validate = False):
def extend (name, values):
""" Adds the given values to the given feature.
"""
assert isinstance(name, basestring)
assert is_iterable_typed(values, basestring)
name = add_grist (name)
__validate_feature (name)
feature = __all_features [name]
@@ -434,6 +448,8 @@ def extend (name, values):
def validate_value_string (f, value_string):
""" Checks that value-string is a valid value-string for the given feature.
"""
assert isinstance(f, Feature)
assert isinstance(value_string, basestring)
if f.free() or value_string in f.values():
return
@@ -472,7 +488,10 @@ def validate_value_string (f, value_string):
subvalues: The additional values of the subfeature being defined.
"""
def extend_subfeature (feature_name, value_string, subfeature_name, subvalues):
assert isinstance(feature_name, basestring)
assert isinstance(value_string, basestring)
assert isinstance(subfeature_name, basestring)
assert is_iterable_typed(subvalues, basestring)
feature = validate_feature(feature_name)
if value_string:
@@ -532,7 +551,7 @@ def compose (composite_property_s, component_properties_s):
All parameters are <feature>value strings
"""
import property
from . import property
component_properties_s = to_seq (component_properties_s)
composite_property = property.create_from_string(composite_property_s)
@@ -555,10 +574,13 @@ def compose (composite_property_s, component_properties_s):
__composite_properties[composite_property] = component_properties
def expand_composite(property):
result = [ property ]
if __composite_properties.has_key(property):
for p in __composite_properties[property]:
def expand_composite(property_):
if __debug__:
from .property import Property
assert isinstance(property_, Property)
result = [ property_ ]
if __composite_properties.has_key(property_):
for p in __composite_properties[property_]:
result.extend(expand_composite(p))
return result
@@ -584,6 +606,9 @@ def expand_composites (properties):
""" Expand all composite properties in the set so that all components
are explicitly expressed.
"""
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
explicit_features = set(p.feature() for p in properties)
result = []
@@ -622,6 +647,11 @@ def is_subfeature_of (parent_property, f):
feature, or if f is a subfeature of the parent_property's feature
specific to the parent_property's value.
"""
if __debug__:
from .property import Property
assert isinstance(parent_property, Property)
assert isinstance(f, Feature)
if not f.subfeature():
return False
@@ -643,38 +673,27 @@ def is_subfeature_of (parent_property, f):
def __is_subproperty_of (parent_property, p):
""" As is_subfeature_of, for subproperties.
"""
if __debug__:
from .property import Property
assert isinstance(parent_property, Property)
assert isinstance(p, Property)
return is_subfeature_of (parent_property, p.feature())
# Returns true iff the subvalue is valid for the feature. When the
# optional value-string is provided, returns true iff the subvalues
# are valid for the given value of the feature.
def is_subvalue(feature, value_string, subfeature, subvalue):
assert isinstance(feature, basestring)
assert isinstance(value_string, basestring)
assert isinstance(subfeature, basestring)
assert isinstance(subvalue, basestring)
if not value_string:
value_string = ''
if not __subfeature_from_value.has_key(feature):
try:
return __subfeature_from_value[feature][value_string][subvalue] == subfeature
except KeyError:
return False
if not __subfeature_from_value[feature].has_key(value_string):
return False
if not __subfeature_from_value[feature][value_string].has_key(subvalue):
return False
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:
raise InvalidValue ("'%s' is not a known subfeature value of '%s%s'" % (subvalue, feature, value_string))
return result
# Uses Property
@@ -689,6 +708,9 @@ def expand (properties):
two values of a given non-free feature are directly expressed in the
input, an error is issued.
"""
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
expanded = expand_subfeatures(properties)
return expand_composites (expanded)
@@ -711,6 +733,10 @@ def add_defaults (properties):
and that's kind of strange.
"""
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
result = [x for x in properties]
handled_features = set()
@@ -744,8 +770,10 @@ def minimize (properties):
Implicit properties will be expressed without feature
grist, and sub-property values will be expressed as elements joined
to the corresponding main property.
"""
"""
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
# remove properties implied by composite features
components = []
for property in properties:
@@ -807,7 +835,7 @@ def split (properties):
substitution of backslashes for slashes, since Jam, unbidden,
sometimes swaps slash direction on NT.
"""
assert isinstance(properties, basestring)
def split_one (properties):
pieces = re.split (__re_slash_or_backslash, properties)
result = []
@@ -839,6 +867,8 @@ def compress_subproperties (properties):
build-request.expand-no-defaults is being abused for unintended
purposes and it needs help
"""
from .property import Property
assert is_iterable_typed(properties, Property)
result = []
matched_subs = set()
all_subs = set()
@@ -852,7 +882,7 @@ def compress_subproperties (properties):
matched_subs.update(subs)
subvalues = '-'.join (sub.value() for sub in subs)
result.append(b2.build.property.Property(
result.append(Property(
p.feature(), p.value() + '-' + subvalues,
p.condition()))
else:
@@ -870,10 +900,16 @@ def compress_subproperties (properties):
# Private methods
def __select_subproperties (parent_property, properties):
if __debug__:
from .property import Property
assert is_iterable_typed(properties, Property)
assert isinstance(parent_property, Property)
return [ x for x in properties if __is_subproperty_of (parent_property, x) ]
def __get_subfeature_name (subfeature, value_string):
if value_string == None:
assert isinstance(subfeature, basestring)
assert isinstance(value_string, basestring) or value_string is None
if value_string == None:
prefix = ''
else:
prefix = value_string + ':'
@@ -882,10 +918,12 @@ def __get_subfeature_name (subfeature, value_string):
def __validate_feature_attributes (name, attributes):
assert isinstance(name, basestring)
assert is_iterable_typed(attributes, basestring)
for attribute in attributes:
if not attribute in __all_attributes:
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)
elif 'implicit' in attributes and 'free' in attributes:
@@ -897,6 +935,7 @@ def __validate_feature_attributes (name, attributes):
def __validate_feature (feature):
""" Generates an error if the feature is unknown.
"""
assert isinstance(feature, basestring)
if not __all_features.has_key (feature):
raise BaseException ('unknown feature "%s"' % feature)
@@ -907,6 +946,10 @@ def __select_subfeatures (parent_property, features):
subfeatures of the property's feature which are conditional on the
property's value.
"""
if __debug__:
from .property import Property
assert isinstance(parent_property, Property)
assert is_iterable_typed(features, Feature)
return [f for f in features if is_subfeature_of (parent_property, f)]
# FIXME: copy over tests.

View File

@@ -52,10 +52,10 @@ import cStringIO
import os.path
from virtual_target import Subvariant
import virtual_target, type, property_set, property
from . import virtual_target, type, property_set, property
from b2.util.logger import *
from b2.util.utility import *
from b2.util import set
from b2.util import set as set_, is_iterable_typed, is_iterable
from b2.util.sequence import unique
import b2.util.sequence as sequence
from b2.manager import get_manager
@@ -114,7 +114,7 @@ def decrease_indent():
# same generator. Does nothing if a non-derived target type is passed to it.
#
def update_cached_information_with_a_new_type(type):
assert isinstance(type, basestring)
base_type = b2.build.type.base(type)
if base_type:
@@ -184,8 +184,11 @@ class Generator:
NOTE: all subclasses must have a similar signature for clone to work!
"""
def __init__ (self, id, composing, source_types, target_types_and_names, requirements = []):
assert(not isinstance(source_types, str))
assert(not isinstance(target_types_and_names, str))
assert isinstance(id, basestring)
assert isinstance(composing, bool)
assert is_iterable_typed(source_types, basestring)
assert is_iterable_typed(target_types_and_names, basestring)
assert is_iterable_typed(requirements, basestring)
self.id_ = id
self.composing_ = composing
self.source_types_ = source_types
@@ -229,9 +232,11 @@ class Generator:
- id
- value to <toolset> feature in properties
"""
return self.__class__ (new_id,
self.composing_,
self.source_types_,
assert isinstance(new_id, basestring)
assert is_iterable_typed(new_toolset_properties, basestring)
return self.__class__ (new_id,
self.composing_,
self.source_types_,
self.target_types_and_names_,
# Note: this does not remove any subfeatures of <toolset>
# which might cause problems
@@ -241,6 +246,8 @@ class Generator:
"""Creates another generator that is the same as $(self), except that
if 'base' is in target types of $(self), 'type' will in target types
of the new generator."""
assert isinstance(base, basestring)
assert isinstance(type, basestring)
target_types = []
for t in self.target_types_and_names_:
m = _re_match_type.match(t)
@@ -291,6 +298,7 @@ class Generator:
# 'properties'. Treat a feature name in requirements
# (i.e. grist-only element), as matching any value of the
# feature.
assert isinstance(ps, property_set.PropertySet)
all_requirements = self.requirements ()
property_requirements = []
@@ -321,7 +329,13 @@ class Generator:
sources: Source targets.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
# intermediary targets don't have names, so None is possible
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
if project.manager ().logger ().on ():
project.manager ().logger ().log (__name__, " generator '%s'" % self.id_)
project.manager ().logger ().log (__name__, " composing: '%s'" % self.composing_)
@@ -345,7 +359,13 @@ class Generator:
return []
def run_really (self, project, name, prop_set, sources):
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
# intermediary targets don't have names, so None is possible
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
# consumed: Targets that this generator will consume directly.
# bypassed: Targets that can't be consumed and will be returned as-is.
@@ -380,6 +400,12 @@ class Generator:
name:
prop_set: Properties to be used for all actions create here
"""
if __debug__:
from .targets import ProjectTarget
assert is_iterable_typed(consumed, virtual_target.VirtualTarget)
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
result = []
# If this is 1->1 transformation, apply it to all consumed targets in order.
if len (self.source_types_) < 2 and not self.composing_:
@@ -395,6 +421,7 @@ class Generator:
return result
def determine_target_name(self, fullname):
assert isinstance(fullname, basestring)
# Determine target name from fullname (maybe including path components)
# Place optional prefix and postfix around basename
@@ -415,7 +442,7 @@ class Generator:
def determine_output_name(self, sources):
"""Determine the name of the produced target from the
names of the sources."""
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
# The simple case if when a name
# of source has single dot. Then, we take the part before
# dot. Several dots can be caused by:
@@ -460,6 +487,12 @@ class Generator:
in make. It's a way to produce target which name is different for name of
source.
"""
if __debug__:
from .targets import ProjectTarget
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
if not name:
name = self.determine_output_name(sources)
@@ -494,6 +527,13 @@ class Generator:
consumed: all targets that can be consumed.
bypassed: all targets that cannot be consumed.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(name, basestring) or name is None
assert isinstance(project, ProjectTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(only_one, bool)
consumed = []
bypassed = []
missing_types = []
@@ -563,7 +603,16 @@ class Generator:
"""
consumed = []
bypassed = []
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(project, ProjectTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
# We process each source one-by-one, trying to convert it to
# a usable type.
for s in sources:
@@ -578,6 +627,7 @@ class Generator:
return (consumed, bypassed)
def consume_directly (self, source):
assert isinstance(source, virtual_target.VirtualTarget)
real_source_type = source.type ()
# If there are no source types, we can consume anything
@@ -607,11 +657,13 @@ class Generator:
def find (id):
""" Finds the generator with id. Returns None if not found.
"""
assert isinstance(id, basestring)
return __generators.get (id, None)
def register (g):
""" Registers new generator instance 'g'.
"""
assert isinstance(g, Generator)
id = g.id()
__generators [id] = g
@@ -660,6 +712,19 @@ def register (g):
invalidate_extendable_viable_source_target_type_cache()
def check_register_types(fn):
def wrapper(id, source_types, target_types, requirements=[]):
assert isinstance(id, basestring)
assert is_iterable_typed(source_types, basestring)
assert is_iterable_typed(target_types, basestring)
assert is_iterable_typed(requirements, basestring)
return fn(id, source_types, target_types, requirements=requirements)
wrapper.__name__ = fn.__name__
wrapper.__doc__ = fn.__doc__
return wrapper
@check_register_types
def register_standard (id, source_types, target_types, requirements = []):
""" Creates new instance of the 'generator' class and registers it.
Returns the creates instance.
@@ -671,6 +736,8 @@ def register_standard (id, source_types, target_types, requirements = []):
register (g)
return g
@check_register_types
def register_composing (id, source_types, target_types, requirements = []):
g = Generator (id, True, source_types, target_types, requirements)
register (g)
@@ -679,6 +746,7 @@ def register_composing (id, source_types, target_types, requirements = []):
def generators_for_toolset (toolset):
""" Returns all generators which belong to 'toolset'.
"""
assert isinstance(toolset, basestring)
return __generators_for_toolset.get(toolset, [])
def override (overrider_id, overridee_id):
@@ -691,7 +759,8 @@ def override (overrider_id, overridee_id):
The overridden generators are discarded immediately
after computing the list of viable generators, before
running any of them."""
assert isinstance(overrider_id, basestring)
assert isinstance(overridee_id, basestring)
__overrides.setdefault(overrider_id, []).append(overridee_id)
def __viable_source_types_real (target_type):
@@ -702,6 +771,7 @@ def __viable_source_types_real (target_type):
returns union of source types for those generators and result
of calling itself recusrively on source types.
"""
assert isinstance(target_type, basestring)
generators = []
# 't0' is the initial list of target types we need to process to get a list
@@ -750,13 +820,14 @@ def __viable_source_types_real (target_type):
if not n in t0:
t.append (n)
result.append (n)
return result
def viable_source_types (target_type):
""" Helper rule, caches the result of '__viable_source_types_real'.
"""
assert isinstance(target_type, basestring)
if not __viable_source_types_cache.has_key(target_type):
__vst_cached_types.append(target_type)
__viable_source_types_cache [target_type] = __viable_source_types_real (target_type)
@@ -767,6 +838,7 @@ def viable_source_types_for_generator_real (generator):
method of 'generator', has some change of being eventually used
(probably after conversion by other generators)
"""
assert isinstance(generator, Generator)
source_types = generator.source_types ()
if not source_types:
@@ -791,6 +863,7 @@ def viable_source_types_for_generator_real (generator):
def viable_source_types_for_generator (generator):
""" Caches the result of 'viable_source_types_for_generator'.
"""
assert isinstance(generator, Generator)
if not __viable_source_types_cache.has_key(generator):
__vstg_cached_generators.append(generator)
__viable_source_types_cache[generator] = viable_source_types_for_generator_real (generator)
@@ -800,6 +873,14 @@ def viable_source_types_for_generator (generator):
def try_one_generator_really (project, name, generator, target_type, properties, sources):
""" Returns usage requirements + list of created targets.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(generator, Generator)
assert isinstance(target_type, basestring)
assert isinstance(properties, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
targets = generator.run (project, name, properties, sources)
usage_requirements = []
@@ -834,6 +915,14 @@ def try_one_generator (project, name, generator, target_type, properties, source
to fail. If so, quickly returns empty list. Otherwise, calls
try_one_generator_really.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(generator, Generator)
assert isinstance(target_type, basestring)
assert isinstance(properties, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
source_types = []
for s in sources:
@@ -842,7 +931,7 @@ def try_one_generator (project, name, generator, target_type, properties, source
viable_source_types = viable_source_types_for_generator (generator)
if source_types and viable_source_types != ['*'] and\
not set.intersection (source_types, viable_source_types):
not set_.intersection (source_types, viable_source_types):
if project.manager ().logger ().on ():
id = generator.id ()
project.manager ().logger ().log (__name__, "generator '%s' pruned" % id)
@@ -856,10 +945,16 @@ def try_one_generator (project, name, generator, target_type, properties, source
def construct_types (project, name, target_types, prop_set, sources):
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert is_iterable_typed(target_types, basestring)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
result = []
usage_requirements = property_set.empty()
for t in target_types:
r = construct (project, name, t, prop_set, sources)
@@ -886,6 +981,7 @@ def __ensure_type (targets):
""" Ensures all 'targets' have types. If this is not so, exists with
error.
"""
assert is_iterable_typed(targets, virtual_target.VirtualTarget)
for t in targets:
if not t.type ():
get_manager().errors()("target '%s' has no type" % str (t))
@@ -902,11 +998,13 @@ def find_viable_generators_aux (target_type, prop_set):
Note: this algorithm explicitly ignores generators for base classes if there's
at least one generator for requested target_type.
"""
assert isinstance(target_type, basestring)
assert isinstance(prop_set, property_set.PropertySet)
# Select generators that can create the required target type.
viable_generators = []
initial_generators = []
import type
from . import type
# Try all-type generators first. Assume they have
# quite specific requirements.
@@ -949,6 +1047,8 @@ def find_viable_generators_aux (target_type, prop_set):
return viable_generators
def find_viable_generators (target_type, prop_set):
assert isinstance(target_type, basestring)
assert isinstance(prop_set, property_set.PropertySet)
key = target_type + '.' + str (prop_set)
l = __viable_generators_cache.get (key, None)
@@ -994,6 +1094,13 @@ def __construct_really (project, name, target_type, prop_set, sources):
""" Attempts to construct target by finding viable generators, running them
and selecting the dependency graph.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(target_type, basestring)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
viable_generators = find_viable_generators (target_type, prop_set)
result = []
@@ -1048,7 +1155,14 @@ def construct (project, name, target_type, prop_set, sources, top_level=False):
has to build a metatarget -- for example a target corresponding to
built tool.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(target_type, basestring)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(top_level, bool)
global __active_generators
if top_level:
saved_active = __active_generators
@@ -1086,7 +1200,7 @@ def add_usage_requirements (result, raw_properties):
if isinstance (result[0], property_set.PropertySet):
return (result[0].add_raw(raw_properties), result[1])
else:
return (propery_set.create(raw-properties), result)
return (property_set.create(raw_properties), result)
#if [ class.is-a $(result[1]) : property-set ]
#{
# return [ $(result[1]).add-raw $(raw-properties) ] $(result[2-]) ;

View File

@@ -40,9 +40,10 @@
# their project id.
import b2.util.path
import b2.build.targets
from b2.build import property_set, property
from b2.build.errors import ExceptionWithUserContext
import b2.build.targets
from b2.manager import get_manager
import bjam
import b2
@@ -56,7 +57,10 @@ import imp
import traceback
import b2.util.option as option
from b2.util import record_jam_to_value_mapping, qualify_jam_action
from b2.util import (
record_jam_to_value_mapping, qualify_jam_action, is_iterable_typed, bjam_signature,
is_iterable)
class ProjectRegistry:
@@ -130,6 +134,7 @@ class ProjectRegistry:
file and jamfile needed by the loaded one will be loaded recursively.
If the jamfile at that location is loaded already, does nothing.
Returns the project module for the Jamfile."""
assert isinstance(jamfile_location, basestring)
absolute = os.path.join(os.getcwd(), jamfile_location)
absolute = os.path.normpath(absolute)
@@ -159,6 +164,7 @@ class ProjectRegistry:
return mname
def load_used_projects(self, module_name):
assert isinstance(module_name, basestring)
# local used = [ modules.peek $(module-name) : .used-projects ] ;
used = self.used_projects[module_name]
@@ -172,7 +178,7 @@ class ProjectRegistry:
def load_parent(self, location):
"""Loads parent of Jamfile at 'location'.
Issues an error if nothing is found."""
assert isinstance(location, basestring)
found = b2.util.path.glob_in_parents(
location, self.JAMROOT + self.JAMFILE)
@@ -187,6 +193,8 @@ class ProjectRegistry:
"""Given 'name' which can be project-id or plain directory name,
return project module corresponding to that id or directory.
Returns nothing of project is not found."""
assert isinstance(name, basestring)
assert isinstance(current_location, basestring)
project_module = None
@@ -214,6 +222,7 @@ class ProjectRegistry:
"""Returns the name of module corresponding to 'jamfile-location'.
If no module corresponds to location yet, associates default
module name with that location."""
assert isinstance(jamfile_location, basestring)
module = self.location2module.get(jamfile_location)
if not module:
# Root the path, so that locations are always umbiguious.
@@ -230,6 +239,9 @@ class ProjectRegistry:
exact names of all the Jamfiles in the given directory. The optional
parent-root argument causes this to search not the given directory
but the ones above it up to the directory given in it."""
assert isinstance(dir, basestring)
assert isinstance(parent_root, (int, bool))
assert isinstance(no_errors, (int, bool))
# Glob for all the possible Jamfiles according to the match pattern.
#
@@ -280,6 +292,8 @@ Please consult the documentation at 'http://boost.org/boost-build2'."""
"""Load a Jamfile at the given directory. Returns nothing.
Will attempt to load the file as indicated by the JAMFILE patterns.
Effect of calling this rule twice with the same 'dir' is underfined."""
assert isinstance(dir, basestring)
assert isinstance(jamfile_module, basestring)
# See if the Jamfile is where it should be.
is_jamroot = False
@@ -359,12 +373,15 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
The caller is required to never call this method twice on
the same file.
"""
assert isinstance(jamfile_module, basestring)
assert isinstance(file, basestring)
self.used_projects[jamfile_module] = []
bjam.call("load", jamfile_module, file)
self.load_used_projects(jamfile_module)
def is_jamroot(self, basename):
assert isinstance(basename, basestring)
match = [ pat for pat in self.JAMROOT if re.match(pat, basename)]
if match:
return 1
@@ -378,7 +395,9 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
location is the location (directory) of the project to initialize.
If not specified, standalone project will be initialized
"""
assert isinstance(module_name, basestring)
assert isinstance(location, basestring) or location is None
assert isinstance(basename, basestring) or basename is None
if "--debug-loading" in self.manager.argv():
print "Initializing project '%s'" % module_name
@@ -465,6 +484,8 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
def inherit_attributes(self, project_module, parent_module):
"""Make 'project-module' inherit attributes of project
root and parent module."""
assert isinstance(project_module, basestring)
assert isinstance(parent_module, basestring)
attributes = self.module2attributes[project_module]
pattributes = self.module2attributes[parent_module]
@@ -502,6 +523,8 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
def register_id(self, id, module):
"""Associate the given id with the given project module."""
assert isinstance(id, basestring)
assert isinstance(module, basestring)
self.id2module[id] = module
def current(self):
@@ -509,11 +532,17 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
return self.current_project
def set_current(self, c):
if __debug__:
from .targets import ProjectTarget
assert isinstance(c, ProjectTarget)
self.current_project = c
def push_current(self, project):
"""Temporary changes the current project to 'project'. Should
be followed by 'pop-current'."""
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
self.saved_current_project.append(self.current_project)
self.current_project = project
@@ -524,11 +553,14 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
def attributes(self, project):
"""Returns the project-attribute instance for the
specified jamfile module."""
assert isinstance(project, basestring)
return self.module2attributes[project]
def attribute(self, project, attribute):
"""Returns the value of the specified attribute in the
specified jamfile module."""
assert isinstance(project, basestring)
assert isinstance(attribute, basestring)
try:
return self.module2attributes[project].get(attribute)
except:
@@ -537,10 +569,14 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
def attributeDefault(self, project, attribute, default):
"""Returns the value of the specified attribute in the
specified jamfile module."""
assert isinstance(project, basestring)
assert isinstance(attribute, basestring)
assert isinstance(default, basestring) or default is None
return self.module2attributes[project].getDefault(attribute, default)
def target(self, project_module):
"""Returns the project target corresponding to the 'project-module'."""
assert isinstance(project_module, basestring)
if not self.module2target.has_key(project_module):
self.module2target[project_module] = \
b2.build.targets.ProjectTarget(project_module, project_module,
@@ -550,6 +586,8 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
def use(self, id, location):
# Use/load a project.
assert isinstance(id, basestring)
assert isinstance(location, basestring)
saved_project = self.current_project
project_module = self.load(location)
declared_id = self.attributeDefault(project_module, "id", "")
@@ -564,16 +602,24 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
self.current_module = saved_project
def add_rule(self, name, callable):
def add_rule(self, name, callable_):
"""Makes rule 'name' available to all subsequently loaded Jamfiles.
Calling that rule wil relay to 'callable'."""
self.project_rules_.add_rule(name, callable)
assert isinstance(name, basestring)
assert callable(callable_)
self.project_rules_.add_rule(name, callable_)
def project_rules(self):
return self.project_rules_
def glob_internal(self, project, wildcards, excludes, rule_name):
if __debug__:
from .targets import ProjectTarget
assert isinstance(project, ProjectTarget)
assert is_iterable_typed(wildcards, basestring)
assert is_iterable_typed(excludes, basestring) or excludes is None
assert isinstance(rule_name, basestring)
location = project.get("source-location")[0]
result = []
@@ -656,6 +702,8 @@ actual value %s""" % (jamfile_module, saved_project, self.current_project))
since then we might get naming conflicts between standard
Python modules and those.
"""
assert isinstance(name, basestring)
assert is_iterable_typed(extra_path, basestring) or extra_path is None
# See if we loaded module of this name already
existing = self.loaded_tool_modules_.get(name)
if existing:
@@ -774,7 +822,20 @@ class ProjectAttributes:
def set(self, attribute, specification, exact=False):
"""Set the named attribute from the specification given by the user.
The value actually set may be different."""
assert isinstance(attribute, basestring)
assert isinstance(exact, (int, bool))
if __debug__ and not exact:
if attribute == 'requirements':
assert (isinstance(specification, property_set.PropertySet)
or all(isinstance(s, basestring) for s in specification))
elif attribute in (
'usage-requirements', 'default-build', 'source-location', 'build-dir', 'id'):
assert is_iterable_typed(specification, basestring)
elif __debug__:
assert (
isinstance(specification, (property_set.PropertySet, type(None), basestring))
or all(isinstance(s, basestring) for s in specification)
)
if exact:
self.__dict__[attribute] = specification
@@ -838,9 +899,11 @@ for project at '%s'""" % (attribute, self.location))
self.__dict__[attribute] = specification
def get(self, attribute):
assert isinstance(attribute, basestring)
return self.__dict__[attribute]
def getDefault(self, attribute, default):
assert isinstance(attribute, basestring)
return self.__dict__.get(attribute, default)
def dump(self):
@@ -876,41 +939,51 @@ class ProjectRules:
"error_reporting_wrapper", "add_rule_for_type", "reverse"]]
self.all_names_ = [x for x in self.local_names]
def _import_rule(self, bjam_module, name, callable):
if hasattr(callable, "bjam_signature"):
bjam.import_rule(bjam_module, name, self.make_wrapper(callable), callable.bjam_signature)
def _import_rule(self, bjam_module, name, callable_):
assert isinstance(bjam_module, basestring)
assert isinstance(name, basestring)
assert callable(callable_)
if hasattr(callable_, "bjam_signature"):
bjam.import_rule(bjam_module, name, self.make_wrapper(callable_), callable_.bjam_signature)
else:
bjam.import_rule(bjam_module, name, self.make_wrapper(callable))
bjam.import_rule(bjam_module, name, self.make_wrapper(callable_))
def add_rule_for_type(self, type):
assert isinstance(type, basestring)
rule_name = type.lower().replace("_", "-")
def xpto (name, sources = [], requirements = [], default_build = [], usage_requirements = []):
@bjam_signature([['name'], ['sources', '*'], ['requirements', '*'],
['default_build', '*'], ['usage_requirements', '*']])
def xpto (name, sources=[], requirements=[], default_build=[], usage_requirements=[]):
return self.manager_.targets().create_typed_target(
type, self.registry.current(), name[0], sources,
type, self.registry.current(), name, sources,
requirements, default_build, usage_requirements)
self.add_rule(rule_name, xpto)
def add_rule(self, name, callable):
self.rules[name] = callable
def add_rule(self, name, callable_):
assert isinstance(name, basestring)
assert callable(callable_)
self.rules[name] = callable_
self.all_names_.append(name)
# Add new rule at global bjam scope. This might not be ideal,
# added because if a jamroot does 'import foo' where foo calls
# add_rule, we need to import new rule to jamroot scope, and
# I'm lazy to do this now.
self._import_rule("", name, callable)
self._import_rule("", name, callable_)
def all_names(self):
return self.all_names_
def call_and_report_errors(self, callable, *args, **kw):
def call_and_report_errors(self, callable_, *args, **kw):
assert callable(callable_)
result = None
try:
self.manager_.errors().push_jamfile_context()
result = callable(*args, **kw)
result = callable_(*args, **kw)
except ExceptionWithUserContext, e:
e.report()
except Exception, e:
@@ -923,16 +996,18 @@ class ProjectRules:
return result
def make_wrapper(self, callable):
def make_wrapper(self, callable_):
"""Given a free-standing function 'callable', return a new
callable that will call 'callable' and report all exceptins,
using 'call_and_report_errors'."""
assert callable(callable_)
def wrapper(*args, **kw):
return self.call_and_report_errors(callable, *args, **kw)
return self.call_and_report_errors(callable_, *args, **kw)
return wrapper
def init_project(self, project_module, python_standalone=False):
assert isinstance(project_module, basestring)
assert isinstance(python_standalone, bool)
if python_standalone:
m = sys.modules[project_module]
@@ -961,7 +1036,7 @@ class ProjectRules:
self._import_rule(project_module, n, self.rules[n])
def project(self, *args):
assert is_iterable(args) and all(is_iterable(arg) for arg in args)
jamfile_module = self.registry.current().project_module()
attributes = self.registry.attributes(jamfile_module)
@@ -1017,7 +1092,8 @@ attribute is allowed only for top-level 'project' invocations""")
"""Declare and set a project global constant.
Project global constants are normal variables but should
not be changed. They are applied to every child Jamfile."""
m = "Jamfile</home/ghost/Work/Boost/boost-svn/tools/build/v2_python/python/tests/bjam/make>"
assert is_iterable_typed(name, basestring)
assert is_iterable_typed(value, basestring)
self.registry.current().add_constant(name[0], value)
def path_constant(self, name, value):
@@ -1025,6 +1101,8 @@ attribute is allowed only for top-level 'project' invocations""")
path is adjusted to be relative to the invocation directory. The given
value path is taken to be either absolute, or relative to this project
root."""
assert is_iterable_typed(name, basestring)
assert is_iterable_typed(value, basestring)
if len(value) > 1:
self.registry.manager.error()("path constant should have one element")
self.registry.current().add_constant(name[0], value[0], path=1)
@@ -1032,27 +1110,35 @@ attribute is allowed only for top-level 'project' invocations""")
def use_project(self, id, where):
# See comment in 'load' for explanation why we record the
# parameters as opposed to loading the project now.
m = self.registry.current().project_module();
assert is_iterable_typed(id, basestring)
assert is_iterable_typed(where, basestring)
m = self.registry.current().project_module()
self.registry.used_projects[m].append((id[0], where[0]))
def build_project(self, dir):
assert(isinstance(dir, list))
assert is_iterable_typed(dir, basestring)
jamfile_module = self.registry.current().project_module()
attributes = self.registry.attributes(jamfile_module)
now = attributes.get("projects-to-build")
attributes.set("projects-to-build", now + dir, exact=True)
def explicit(self, target_names):
assert is_iterable_typed(target_names, basestring)
self.registry.current().mark_targets_as_explicit(target_names)
def always(self, target_names):
assert is_iterable_typed(target_names, basestring)
self.registry.current().mark_targets_as_alays(target_names)
def glob(self, wildcards, excludes=None):
assert is_iterable_typed(wildcards, basestring)
assert is_iterable_typed(excludes, basestring)or excludes is None
return self.registry.glob_internal(self.registry.current(),
wildcards, excludes, "glob")
def glob_tree(self, wildcards, excludes=None):
assert is_iterable_typed(wildcards, basestring)
assert is_iterable_typed(excludes, basestring) or excludes is None
bad = 0
for p in wildcards:
if os.path.dirname(p):
@@ -1076,6 +1162,7 @@ attribute is allowed only for top-level 'project' invocations""")
# will expect the module to be found even though
# the directory is not in BOOST_BUILD_PATH.
# So temporary change the search path.
assert is_iterable_typed(toolset, basestring)
current = self.registry.current()
location = current.get('location')
@@ -1090,7 +1177,9 @@ attribute is allowed only for top-level 'project' invocations""")
self.registry.set_current(current)
def import_(self, name, names_to_import=None, local_names=None):
assert is_iterable_typed(name, basestring)
assert is_iterable_typed(names_to_import, basestring) or names_to_import is None
assert is_iterable_typed(local_names, basestring)or local_names is None
name = name[0]
py_name = name
if py_name == "os":
@@ -1133,7 +1222,8 @@ attribute is allowed only for top-level 'project' invocations""")
lib x : x.cpp : [ conditional <toolset>gcc <variant>debug :
<define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ;
"""
assert is_iterable_typed(condition, basestring)
assert is_iterable_typed(requirements, basestring)
c = string.join(condition, ",")
if c.find(":") != -1:
return [c + r for r in requirements]
@@ -1141,6 +1231,8 @@ attribute is allowed only for top-level 'project' invocations""")
return [c + ":" + r for r in requirements]
def option(self, name, value):
assert is_iterable(name) and isinstance(name[0], basestring)
assert is_iterable(value) and isinstance(value[0], basestring)
name = name[0]
if not name in ["site-config", "user-config", "project-config"]:
get_manager().errors()("The 'option' rule may be used only in site-config or user-config")

View File

@@ -11,7 +11,7 @@ import re
import sys
from b2.util.utility import *
from b2.build import feature
from b2.util import sequence, qualify_jam_action
from b2.util import sequence, qualify_jam_action, is_iterable_typed
import b2.util.set
from b2.manager import get_manager
@@ -70,7 +70,9 @@ class Property(object):
def create_from_string(s, allow_condition=False,allow_missing_value=False):
assert isinstance(s, basestring)
assert isinstance(allow_condition, bool)
assert isinstance(allow_missing_value, bool)
condition = []
import types
if not isinstance(s, types.StringType):
@@ -119,11 +121,11 @@ def create_from_string(s, allow_condition=False,allow_missing_value=False):
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):
assert is_iterable_typed(string_list, basestring)
return [create_from_string(s, allow_condition) for s in string_list]
def reset ():
@@ -185,6 +187,8 @@ def refine (properties, requirements):
Conditional requirements are just added without modification.
Returns the resulting list of properties.
"""
assert is_iterable_typed(properties, Property)
assert is_iterable_typed(requirements, Property)
# The result has no duplicates, so we store it in a set
result = set()
@@ -242,6 +246,8 @@ def translate_indirect(properties, context_module):
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."""
assert is_iterable_typed(properties, Property)
assert isinstance(context_module, basestring)
result = []
for p in properties:
if p.value()[0] == '@':
@@ -257,15 +263,14 @@ 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)
if isinstance(properties, Property):
properties = [properties]
assert is_iterable_typed(properties, Property)
for p in properties:
__validate1(p)
def expand_subfeatures_in_conditions (properties):
assert is_iterable_typed(properties, Property)
result = []
for p in properties:
@@ -296,6 +301,7 @@ def split_conditional (property):
<variant>debug,<toolset>gcc <inlining>full.
Otherwise, returns empty string.
"""
assert isinstance(property, basestring)
m = __re_split_conditional.match (property)
if m:
@@ -307,6 +313,7 @@ def split_conditional (property):
def select (features, properties):
""" Selects properties which correspond to any of the given features.
"""
assert is_iterable_typed(properties, basestring)
result = []
# add any missing angle brackets
@@ -315,6 +322,9 @@ def select (features, properties):
return [p for p in properties if get_grist(p) in features]
def validate_property_sets (sets):
if __debug__:
from .property_set import PropertySet
assert is_iterable_typed(sets, PropertySet)
for s in sets:
validate(s.all())
@@ -323,6 +333,10 @@ def evaluate_conditionals_in_context (properties, context):
For those with met conditions, removes the condition. Properies
in conditions are looked up in 'context'
"""
if __debug__:
from .property_set import PropertySet
assert is_iterable_typed(properties, Property)
assert isinstance(context, PropertySet)
base = []
conditional = []
@@ -348,8 +362,11 @@ def change (properties, feature, value = None):
given feature replaced by the given value.
If 'value' is None the feature will be removed.
"""
assert is_iterable_typed(properties, basestring)
assert isinstance(feature, basestring)
assert isinstance(value, (basestring, type(None)))
result = []
feature = add_grist (feature)
for p in properties:
@@ -368,7 +385,8 @@ def change (properties, feature, value = None):
def __validate1 (property):
""" Exit with error if property is not valid.
"""
"""
assert isinstance(property, Property)
msg = None
if not property.feature().free():
@@ -405,7 +423,10 @@ def __validate1 (property):
def remove(attributes, properties):
"""Returns a property sets which include all the elements
in 'properties' that do not have attributes listed in 'attributes'."""
if isinstance(attributes, basestring):
attributes = [attributes]
assert is_iterable_typed(attributes, basestring)
assert is_iterable_typed(properties, basestring)
result = []
for e in properties:
attributes_new = feature.attributes(get_grist(e))
@@ -424,6 +445,8 @@ def remove(attributes, properties):
def take(attributes, properties):
"""Returns a property set which include all
properties in 'properties' that have any of 'attributes'."""
assert is_iterable_typed(attributes, basestring)
assert is_iterable_typed(properties, basestring)
result = []
for e in properties:
if b2.util.set.intersection(attributes, feature.attributes(get_grist(e))):
@@ -431,7 +454,9 @@ def take(attributes, properties):
return result
def translate_dependencies(properties, project_id, location):
assert is_iterable_typed(properties, Property)
assert isinstance(project_id, basestring)
assert isinstance(location, basestring)
result = []
for p in properties:
@@ -464,10 +489,12 @@ class PropertyMap:
def __init__ (self):
self.__properties = []
self.__values = []
def insert (self, properties, value):
""" Associate value with properties.
"""
assert is_iterable_typed(properties, basestring)
assert isinstance(value, basestring)
self.__properties.append(properties)
self.__values.append(value)
@@ -477,9 +504,12 @@ class PropertyMap:
subset has value assigned to it, return the
value for the longest subset, if it's unique.
"""
assert is_iterable_typed(properties, basestring)
return self.find_replace (properties)
def find_replace(self, properties, value=None):
assert is_iterable_typed(properties, basestring)
assert isinstance(value, (basestring, type(None)))
matches = []
match_ranks = []

View File

@@ -8,6 +8,7 @@
import hashlib
import bjam
from b2.util.utility import *
import property, feature
import b2.build.feature
@@ -15,7 +16,7 @@ from b2.exceptions import *
from b2.build.property import get_abbreviated_paths
from b2.util.sequence import unique
from b2.util.set import difference
from b2.util import cached, abbreviate_dashed
from b2.util import cached, abbreviate_dashed, is_iterable_typed
from b2.manager import get_manager
@@ -36,6 +37,8 @@ def create (raw_properties = []):
""" Creates a new 'PropertySet' instance for the given raw properties,
or returns an already existing one.
"""
assert (is_iterable_typed(raw_properties, property.Property)
or is_iterable_typed(raw_properties, basestring))
# FIXME: propagate to callers.
if len(raw_properties) > 0 and isinstance(raw_properties[0], property.Property):
x = raw_properties
@@ -58,6 +61,7 @@ def create_with_validation (raw_properties):
that all properties are valid and converting implicit
properties into gristed form.
"""
assert is_iterable_typed(raw_properties, basestring)
properties = [property.create_from_string(s) for s in raw_properties]
property.validate(properties)
@@ -71,7 +75,9 @@ def empty ():
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'"""
assert is_iterable_typed(raw_properties, basestring)
assert isinstance(jamfile_module, basestring)
assert isinstance(location, basestring)
properties = property.create_from_strings(raw_properties, True)
properties = property.translate_paths(properties, location)
properties = property.translate_indirect(properties, jamfile_module)
@@ -95,7 +101,10 @@ def refine_from_user_input(parent_requirements, specification, jamfile_module,
- project-module -- the module to which context indirect features
will be bound.
- location -- the path to which path features are relative."""
assert isinstance(parent_requirements, PropertySet)
assert is_iterable_typed(specification, basestring)
assert isinstance(jamfile_module, basestring)
assert isinstance(location, basestring)
if not specification:
return parent_requirements
@@ -146,7 +155,7 @@ class PropertySet:
caching whenever possible.
"""
def __init__ (self, properties = []):
assert is_iterable_typed(properties, property.Property)
raw_properties = []
for p in properties:
@@ -304,6 +313,7 @@ class PropertySet:
return self.subfeatures_
def evaluate_conditionals(self, context=None):
assert isinstance(context, (PropertySet, type(None)))
if not context:
context = self
@@ -410,6 +420,7 @@ class PropertySet:
""" Creates a new property set containing the properties in this one,
plus the ones of the property set passed as argument.
"""
assert isinstance(ps, PropertySet)
if not self.added_.has_key(ps):
self.added_[ps] = create(self.all_ + ps.all())
return self.added_[ps]
@@ -428,6 +439,7 @@ class PropertySet:
feature = feature[0]
if not isinstance(feature, b2.build.feature.Feature):
feature = b2.build.feature.get(feature)
assert isinstance(feature, b2.build.feature.Feature)
if not self.feature_map_:
self.feature_map_ = {}
@@ -442,9 +454,9 @@ class PropertySet:
@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)
assert isinstance(feature, b2.build.feature.Feature)
result = []
for p in self.all_:

View File

@@ -34,6 +34,8 @@ import bjam
import os
from b2.exceptions import *
from b2.manager import get_manager
from b2.util import is_iterable_typed
def reset ():
""" Clear the module state. This is mainly for testing purposes.
@@ -56,6 +58,8 @@ def register(scanner_class, relevant_properties):
properties relevant to this scanner. Ctor for that class
should have one parameter: list of properties.
"""
assert issubclass(scanner_class, Scanner)
assert isinstance(relevant_properties, basestring)
__scanners[str(scanner_class)] = relevant_properties
def registered(scanner_class):
@@ -67,6 +71,8 @@ def get(scanner_class, properties):
""" Returns an instance of previously registered scanner
with the specified properties.
"""
assert issubclass(scanner_class, Scanner)
assert is_iterable_typed(properties, basestring)
scanner_name = str(scanner_class)
if not registered(scanner_name):
@@ -130,6 +136,9 @@ class ScannerRegistry:
""" Installs the specified scanner on actual target 'target'.
vtarget: virtual target from which 'target' was actualized.
"""
assert isinstance(scanner, Scanner)
assert isinstance(target, basestring)
assert isinstance(vtarget, basestring)
engine = self.manager_.engine()
engine.set_target_variable(target, "HDRSCAN", scanner.pattern())
if not self.exported_scanners_.has_key(scanner):
@@ -150,6 +159,8 @@ class ScannerRegistry:
pass
def propagate(self, scanner, targets):
assert isinstance(scanner, Scanner)
assert is_iterable_typed(targets, basestring) or isinstance(targets, basestring)
engine = self.manager_.engine()
engine.set_target_variable(targets, "HDRSCAN", scanner.pattern())
engine.set_target_variable(targets, "HDRRULE",

View File

@@ -81,7 +81,7 @@ import property, project, virtual_target, property_set, feature, generators, too
from virtual_target import Subvariant
from b2.exceptions import *
from b2.util.sequence import unique
from b2.util import path, bjam_signature
from b2.util import path, bjam_signature, safe_isinstance, is_iterable_typed
from b2.build.errors import user_error_checkpoint
import b2.build.build_request as build_request
@@ -107,6 +107,7 @@ class TargetRegistry:
""" Registers the specified target as a main target alternatives.
Returns 'target'.
"""
assert isinstance(target, AbstractTarget)
target.project ().add_alternative (target)
return target
@@ -116,6 +117,9 @@ class TargetRegistry:
as main target instances, and the name of such targets are adjusted to
be '<name_of_this_target>__<name_of_source_target>'. Such renaming
is disabled is non-empty value is passed for 'no-renaming' parameter."""
assert is_iterable_typed(sources, basestring)
assert isinstance(main_target_name, basestring)
assert isinstance(no_renaming, (int, bool))
result = []
for t in sources:
@@ -149,7 +153,8 @@ class TargetRegistry:
'specification' are the properties xplicitly specified for a
main target
'project' is the project where the main taret is to be declared."""
assert is_iterable_typed(specification, basestring)
assert isinstance(project, ProjectTarget)
specification.extend(toolset.requirements())
requirements = property_set.refine_from_user_input(
@@ -166,6 +171,8 @@ class TargetRegistry:
specification: Use-properties explicitly specified for a main target
project: Project where the main target is to be declared
"""
assert is_iterable_typed(specification, basestring)
assert isinstance(project, ProjectTarget)
project_usage_requirements = project.get ('usage-requirements')
# We don't use 'refine-from-user-input' because I'm not sure if:
@@ -184,6 +191,8 @@ class TargetRegistry:
specification: Default build explicitly specified for a main target
project: Project where the main target is to be declared
"""
assert is_iterable_typed(specification, basestring)
assert isinstance(project, ProjectTarget)
if specification:
return property_set.create_with_validation(specification)
else:
@@ -192,6 +201,7 @@ class TargetRegistry:
def start_building (self, main_target_instance):
""" Helper rules to detect cycles in main target references.
"""
assert isinstance(main_target_instance, MainTarget)
if self.targets_being_built_.has_key(id(main_target_instance)):
names = []
for t in self.targets_being_built_.values() + [main_target_instance]:
@@ -202,6 +212,7 @@ class TargetRegistry:
self.targets_being_built_[id(main_target_instance)] = main_target_instance
def end_building (self, main_target_instance):
assert isinstance(main_target_instance, MainTarget)
assert (self.targets_being_built_.has_key (id (main_target_instance)))
del self.targets_being_built_ [id (main_target_instance)]
@@ -211,6 +222,11 @@ class TargetRegistry:
'usage_requirements' are assumed to be in the form specified
by the user in Jamfile corresponding to 'project'.
"""
assert isinstance(type, basestring)
assert isinstance(project, ProjectTarget)
assert is_iterable_typed(sources, basestring)
assert is_iterable_typed(requirements, basestring)
assert is_iterable_typed(default_build, basestring)
return self.main_target_alternative (TypedTarget (name, project, type,
self.main_target_sources (sources, name),
self.main_target_requirements (requirements, project),
@@ -231,6 +247,7 @@ class TargetRegistry:
print self.indent_ + message
def push_target(self, target):
assert isinstance(target, AbstractTarget)
self.targets_.append(target)
def pop_target(self):
@@ -245,10 +262,10 @@ class GenerateResult:
def __init__ (self, ur=None, targets=None):
if not targets:
targets = []
assert isinstance(ur, property_set.PropertySet) or ur is None
assert is_iterable_typed(targets, virtual_target.VirtualTarget)
self.__usage_requirements = ur
self.__targets = targets
assert all(isinstance(t, virtual_target.VirtualTarget) for t in targets)
if not self.__usage_requirements:
self.__usage_requirements = property_set.empty ()
@@ -274,6 +291,7 @@ class AbstractTarget:
project: the project target to which this one belongs
manager:the manager object. If none, uses project.manager ()
"""
assert isinstance(name, basestring)
assert (isinstance (project, ProjectTarget))
# Note: it might seem that we don't need either name or project at all.
# However, there are places where we really need it. One example is error
@@ -329,6 +347,7 @@ class AbstractTarget:
raise BaseException ("method should be defined in derived classes")
def rename (self, new_name):
assert isinstance(new_name, basestring)
self.name_ = new_name
class ProjectTarget (AbstractTarget):
@@ -346,6 +365,10 @@ class ProjectTarget (AbstractTarget):
all alternatives are enumerated an main targets are created.
"""
def __init__ (self, manager, name, project_module, parent_project, requirements, default_build):
assert isinstance(project_module, basestring)
assert isinstance(parent_project, (ProjectTarget, type(None)))
assert isinstance(requirements, (type(None), property_set.PropertySet))
assert isinstance(default_build, (type(None), property_set.PropertySet))
AbstractTarget.__init__ (self, name, self, manager)
self.project_module_ = project_module
@@ -390,6 +413,7 @@ class ProjectTarget (AbstractTarget):
return self.project_module_
def get (self, attribute):
assert isinstance(attribute, basestring)
return self.manager().projects().attribute(
self.project_module_, attribute)
@@ -404,6 +428,7 @@ class ProjectTarget (AbstractTarget):
def generate (self, ps):
""" Generates all possible targets contained in this project.
"""
assert isinstance(ps, property_set.PropertySet)
self.manager_.targets().log(
"Building project '%s' with '%s'" % (self.name (), str(ps)))
self.manager_.targets().increase_indent ()
@@ -444,20 +469,24 @@ class ProjectTarget (AbstractTarget):
# Record the name of the target, not instance, since this
# rule is called before main target instaces are created.
assert is_iterable_typed(target_names, basestring)
self.explicit_targets_.update(target_names)
def mark_targets_as_always(self, target_names):
assert is_iterable_typed(target_names, basestring)
self.always_targets_.update(target_names)
def add_alternative (self, target_instance):
""" Add new target alternative.
"""
assert isinstance(target_instance, AbstractTarget)
if self.built_main_targets_:
raise IllegalOperation ("add-alternative called when main targets are already created for project '%s'" % self.full_name ())
self.alternatives_.append (target_instance)
def main_target (self, name):
assert isinstance(name, basestring)
if not self.built_main_targets_:
self.build_main_targets()
@@ -465,6 +494,7 @@ class ProjectTarget (AbstractTarget):
def has_main_target (self, name):
"""Tells if a main target with the specified name exists."""
assert isinstance(name, basestring)
if not self.built_main_targets_:
self.build_main_targets()
@@ -473,6 +503,7 @@ class ProjectTarget (AbstractTarget):
def create_main_target (self, name):
""" Returns a 'MainTarget' class instance corresponding to the 'name'.
"""
assert isinstance(name, basestring)
if not self.built_main_targets_:
self.build_main_targets ()
@@ -483,7 +514,8 @@ class ProjectTarget (AbstractTarget):
""" Find and return the target with the specified id, treated
relative to self.
"""
result = None
assert isinstance(id, basestring)
result = None
current_location = self.get ('location')
__re_split_project_target = re.compile (r'(.*)//(.*)')
@@ -542,6 +574,8 @@ class ProjectTarget (AbstractTarget):
return result
def find (self, id, no_error = False):
assert isinstance(id, basestring)
assert isinstance(no_error, int) # also matches bools
v = self.ids_cache_.get (id, None)
if not v:
@@ -576,7 +610,9 @@ class ProjectTarget (AbstractTarget):
the constant will be interpreted relatively
to the location of project.
"""
assert isinstance(name, basestring)
assert isinstance(value, basestring)
assert isinstance(path, int) # will also match bools
if path:
l = self.location_
if not l:
@@ -585,8 +621,8 @@ class ProjectTarget (AbstractTarget):
# It might be more reasonable to make every project have
# a location and use some other approach to prevent buildable
# targets in config files, but that's for later.
l = get('source-location')
l = self.get('source-location')
value = os.path.join(l, value)
# Now make the value absolute path. Constants should be in
# platform-native form.
@@ -596,6 +632,7 @@ class ProjectTarget (AbstractTarget):
bjam.call("set-variable", self.project_module(), name, value)
def inherit(self, parent_project):
assert isinstance(parent_project, ProjectTarget)
for c in parent_project.constants_:
# No need to pass the type. Path constants were converted to
# absolute paths already by parent.
@@ -624,6 +661,7 @@ class MainTarget (AbstractTarget):
def add_alternative (self, target):
""" Add a new alternative for this target.
"""
assert isinstance(target, AbstractTarget)
d = target.default_build ()
if self.alternatives_ and self.default_build_ != d:
@@ -637,7 +675,7 @@ class MainTarget (AbstractTarget):
self.alternatives_.append (target)
def __select_alternatives (self, property_set, debug):
def __select_alternatives (self, property_set_, debug):
""" Returns the best viable alternative for this property_set
See the documentation for selection rules.
# TODO: shouldn't this be 'alternative' (singular)?
@@ -647,8 +685,10 @@ class MainTarget (AbstractTarget):
# lib l : l.cpp : <variant>debug ;
# lib l : l_opt.cpp : <variant>release ;
# won't work unless we add default value <variant>debug.
property_set = property_set.add_defaults ()
assert isinstance(property_set_, property_set.PropertySet)
assert isinstance(debug, int) # also matches bools
property_set_ = property_set_.add_defaults ()
# The algorithm: we keep the current best viable alternative.
# When we've got new best viable alternative, we compare it
# with the current one.
@@ -662,11 +702,11 @@ class MainTarget (AbstractTarget):
return self.alternatives_ [0]
if debug:
print "Property set for selection:", property_set
print "Property set for selection:", property_set_
for v in self.alternatives_:
properties = v.match (property_set, debug)
properties = v.match (property_set_, debug)
if properties is not None:
if not best:
best = v
@@ -689,8 +729,9 @@ class MainTarget (AbstractTarget):
return best
def apply_default_build (self, property_set):
return apply_default_build(property_set, self.default_build_)
def apply_default_build (self, property_set_):
assert isinstance(property_set_, property_set.PropertySet)
return apply_default_build(property_set_, self.default_build_)
def generate (self, ps):
""" Select an alternative for this main target, by finding all alternatives
@@ -698,6 +739,7 @@ class MainTarget (AbstractTarget):
longest requirements set.
Returns the result of calling 'generate' on that alternative.
"""
assert isinstance(ps, property_set.PropertySet)
self.manager_.targets ().start_building (self)
# We want composite properties in build request act as if
@@ -722,6 +764,7 @@ class MainTarget (AbstractTarget):
generated virtual target in other elements. It's possible
that no targets are generated.
"""
assert isinstance(prop_set, property_set.PropertySet)
best_alternative = self.__select_alternatives (prop_set, debug=0)
if not best_alternative:
@@ -737,6 +780,7 @@ class MainTarget (AbstractTarget):
return result
def rename(self, new_name):
assert isinstance(new_name, basestring)
AbstractTarget.rename(self, new_name)
for a in self.alternatives_:
a.rename(new_name)
@@ -783,6 +827,8 @@ def resolve_reference(target_reference, project):
as properties explicitly specified for this reference.
"""
# Separate target name from properties override
assert isinstance(target_reference, basestring)
assert isinstance(project, ProjectTarget)
split = _re_separate_target_from_properties.match (target_reference)
if not split:
raise BaseException ("Invalid reference: '%s'" % target_reference)
@@ -800,7 +846,7 @@ def resolve_reference(target_reference, project):
return (target, property_set.create(sproperties))
def generate_from_reference(target_reference, project, property_set):
def generate_from_reference(target_reference, project, property_set_):
""" Attempts to generate the target given by target reference, which
can refer both to a main target or to a file.
Returns a list consisting of
@@ -810,11 +856,14 @@ def generate_from_reference(target_reference, project, property_set):
project: Project where the reference is made
property_set: Properties of the main target that makes the reference
"""
assert isinstance(target_reference, basestring)
assert isinstance(project, ProjectTarget)
assert isinstance(property_set_, property_set.PropertySet)
target, sproperties = resolve_reference(target_reference, project)
# Take properties which should be propagated and refine them
# with source-specific requirements.
propagated = property_set.propagated()
propagated = property_set_.propagated()
rproperties = propagated.refine(sproperties)
return target.generate(rproperties)
@@ -828,6 +877,10 @@ class BasicTarget (AbstractTarget):
targets.
"""
def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None):
assert is_iterable_typed(sources, basestring)
assert isinstance(requirements, property_set.PropertySet)
assert isinstance(default_build, property_set.PropertySet)
assert isinstance(usage_requirements, property_set.PropertySet)
AbstractTarget.__init__ (self, name, project)
for s in sources:
@@ -894,6 +947,8 @@ class BasicTarget (AbstractTarget):
# without using complex algorithsm.
# This gives the complex algorithm better chance of caching results.
# The exact effect of this "optimization" is no longer clear
assert isinstance(build_request, property_set.PropertySet)
assert isinstance(requirements, property_set.PropertySet)
free_unconditional = []
other = []
for p in requirements.all():
@@ -934,6 +989,9 @@ class BasicTarget (AbstractTarget):
# <threading>single
#
# might come from project's requirements.
assert isinstance(requirements, property_set.PropertySet)
assert isinstance(context, property_set.PropertySet)
assert isinstance(what, basestring)
unconditional = feature.expand(requirements.non_conditional())
context = context.refine(property_set.create(unconditional))
@@ -1013,6 +1071,8 @@ class BasicTarget (AbstractTarget):
# and expands to <foo2>bar2, but default value of <foo2> is not bar2,
# in which case it's not clear what to do.
#
assert isinstance(build_request, property_set.PropertySet)
assert isinstance(requirements, property_set.PropertySet)
build_request = build_request.add_defaults()
# Featured added by 'add-default' can be composite and expand
# to features without default values -- so they are not added yet.
@@ -1022,8 +1082,8 @@ class BasicTarget (AbstractTarget):
return self.evaluate_requirements(requirements, build_request,
"refined")
def match (self, property_set, debug):
def match (self, property_set_, debug):
""" Returns the alternative condition for this alternative, if
the condition is satisfied by 'property_set'.
"""
@@ -1035,14 +1095,15 @@ class BasicTarget (AbstractTarget):
# On the other hand, if we have <variant>release in condition it
# does not make sense to require <optimization>full to be in
# build request just to select this variant.
assert isinstance(property_set_, property_set.PropertySet)
bcondition = self.requirements_.base ()
ccondition = self.requirements_.conditional ()
condition = b2.util.set.difference (bcondition, ccondition)
if debug:
print " next alternative: required properties:", [str(p) for p in condition]
if b2.util.set.contains (condition, property_set.all()):
if b2.util.set.contains (condition, property_set_.all()):
if debug:
print " matched"
@@ -1053,12 +1114,14 @@ class BasicTarget (AbstractTarget):
return None
def generate_dependency_targets (self, target_ids, property_set):
def generate_dependency_targets (self, target_ids, property_set_):
assert is_iterable_typed(target_ids, basestring)
assert isinstance(property_set_, property_set.PropertySet)
targets = []
usage_requirements = []
for id in target_ids:
result = generate_from_reference(id, self.project_, property_set)
result = generate_from_reference(id, self.project_, property_set_)
targets += result.targets()
usage_requirements += result.usage_requirements().all()
@@ -1071,6 +1134,8 @@ class BasicTarget (AbstractTarget):
Returns a tuple (result, usage_requirements).
"""
assert is_iterable_typed(properties, property.Property)
assert isinstance(ps, property_set.PropertySet)
result_properties = []
usage_requirements = []
for p in properties:
@@ -1093,6 +1158,7 @@ class BasicTarget (AbstractTarget):
and calls 'construct'. This method should not be
overridden.
"""
assert isinstance(ps, property_set.PropertySet)
self.manager_.errors().push_user_context(
"Generating target " + self.full_name(), self.user_context_)
@@ -1215,6 +1281,7 @@ class BasicTarget (AbstractTarget):
properties, determines and sets appripriate usage requirements
on those targets.
"""
assert isinstance(subvariant, virtual_target.Subvariant)
rproperties = subvariant.build_properties ()
xusage_requirements =self.evaluate_requirements(
self.usage_requirements_, rproperties, "added")
@@ -1257,7 +1324,12 @@ class BasicTarget (AbstractTarget):
targets created while building this main target
- 'build-request' is property-set instance with
requested build properties"""
assert is_iterable_typed(root_targets, virtual_target.VirtualTarget)
assert is_iterable_typed(all_targets, virtual_target.VirtualTarget)
assert isinstance(build_request, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(rproperties, property_set.PropertySet)
assert isinstance(usage_requirements, property_set.PropertySet)
for e in root_targets:
e.root (True)
@@ -1282,6 +1354,7 @@ class TypedTarget (BasicTarget):
import generators
def __init__ (self, name, project, type, sources, requirements, default_build, usage_requirements):
assert isinstance(type, basestring)
BasicTarget.__init__ (self, name, project, sources, requirements, default_build, usage_requirements)
self.type_ = type
@@ -1290,9 +1363,11 @@ class TypedTarget (BasicTarget):
def type (self):
return self.type_
def construct (self, name, source_targets, prop_set):
def construct (self, name, source_targets, prop_set):
assert isinstance(name, basestring)
assert is_iterable_typed(source_targets, virtual_target.VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
r = generators.construct (self.project_, os.path.splitext(name)[0],
self.type_,
prop_set.add_raw(['<main-target-type>' + self.type_]),
@@ -1313,11 +1388,12 @@ class TypedTarget (BasicTarget):
return r
def apply_default_build(property_set, default_build):
def apply_default_build(property_set_, default_build):
# 1. First, see what properties from default_build
# are already present in property_set.
specified_features = set(p.feature() for p in property_set.all())
# are already present in property_set.
assert isinstance(property_set_, property_set.PropertySet)
assert isinstance(default_build, property_set.PropertySet)
specified_features = set(p.feature() for p in property_set_.all())
defaults_to_apply = []
for d in default_build.all():
@@ -1345,19 +1421,23 @@ def apply_default_build(property_set, default_build):
# be an indication that
# build_request.expand-no-defaults is the wrong rule
# to use here.
compressed = feature.compress_subproperties(property_set.all())
compressed = feature.compress_subproperties(property_set_.all())
result = build_request.expand_no_defaults(
b2.build.property_set.create(feature.expand([p])) for p in (compressed + defaults_to_apply))
else:
result.append (property_set)
result.append (property_set_)
return result
def create_typed_metatarget(name, type, sources, requirements, default_build, usage_requirements):
assert isinstance(name, basestring)
assert isinstance(type, basestring)
assert is_iterable_typed(requirements, basestring)
assert is_iterable_typed(default_build, basestring)
assert is_iterable_typed(usage_requirements, basestring)
from b2.manager import get_manager
t = get_manager().targets()
@@ -1372,6 +1452,11 @@ def create_typed_metatarget(name, type, sources, requirements, default_build, us
def create_metatarget(klass, name, sources, requirements=[], default_build=[], usage_requirements=[]):
assert isinstance(name, basestring)
assert is_iterable_typed(sources, basestring)
assert is_iterable_typed(requirements, basestring)
assert is_iterable_typed(default_build, basestring)
assert is_iterable_typed(usage_requirements, basestring)
from b2.manager import get_manager
t = get_manager().targets()

View File

@@ -12,7 +12,9 @@
import feature, property, generators, property_set
import b2.util.set
from b2.util import cached, qualify_jam_action
import bjam
from b2.util import cached, qualify_jam_action, is_iterable_typed, is_iterable
from b2.util.utility import *
from b2.util import bjam_signature
from b2.manager import get_manager
@@ -34,6 +36,11 @@ __re_first_group = re.compile (r'[^.]*\.(.*)')
class Flag:
def __init__(self, variable_name, values, condition, rule = None):
assert isinstance(variable_name, basestring)
assert is_iterable(values) and all(
isinstance(v, (basestring, type(None))) for v in values)
assert is_iterable_typed(condition, property_set.PropertySet)
assert isinstance(rule, (basestring, type(None)))
self.variable_name = variable_name
self.values = values
self.condition = condition
@@ -117,6 +124,10 @@ def flags(rule_or_module, variable_name, condition, values = []):
is specified, then the value of 'feature'
will be added.
"""
assert isinstance(rule_or_module, basestring)
assert isinstance(variable_name, basestring)
assert is_iterable_typed(condition, basestring)
assert is_iterable(values) and all(isinstance(v, (basestring, type(None))) for v in values)
caller = bjam.caller()
if not '.' in rule_or_module and caller and caller[:-1].startswith("Jamfile"):
# Unqualified rule name, used inside Jamfile. Most likely used with
@@ -156,6 +167,9 @@ def flags(rule_or_module, variable_name, condition, values = []):
def set_target_variables (manager, rule_or_module, targets, ps):
"""
"""
assert isinstance(rule_or_module, basestring)
assert is_iterable_typed(targets, basestring)
assert isinstance(ps, property_set.PropertySet)
settings = __set_target_variables_aux(manager, rule_or_module, ps)
if settings:
@@ -166,7 +180,8 @@ def set_target_variables (manager, rule_or_module, targets, ps):
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."""
assert is_iterable_typed(conditions, property_set.PropertySet)
assert isinstance(ps, property_set.PropertySet)
features = set(p.feature() for p in ps.all())
for condition in conditions:
@@ -202,9 +217,14 @@ def find_satisfied_condition(conditions, ps):
def register (toolset):
""" Registers a new toolset.
"""
assert isinstance(toolset, basestring)
feature.extend('toolset', [toolset])
def inherit_generators (toolset, properties, base, generators_to_ignore = []):
assert isinstance(toolset, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(base, basestring)
assert is_iterable_typed(generators_to_ignore, basestring)
if not properties:
properties = [replace_grist (toolset, '<toolset>')]
@@ -237,6 +257,9 @@ def inherit_flags(toolset, base, prohibited_properties = []):
or version of a base toolset, it won't ever match the inheriting toolset. When
such flag settings must be inherited, define a rule in base toolset module and
call it as needed."""
assert isinstance(toolset, basestring)
assert isinstance(base, basestring)
assert is_iterable_typed(prohibited_properties, basestring)
for f in __module_flags.get(base, []):
if not f.condition or b2.util.set.difference(f.condition, prohibited_properties):
@@ -296,6 +319,8 @@ def __set_target_variables_aux (manager, rule_or_module, ps):
variables names and values, which must be set on targets for that
rule/properties combination.
"""
assert isinstance(rule_or_module, basestring)
assert isinstance(ps, property_set.PropertySet)
result = []
for f in __flags.get(rule_or_module, []):
@@ -320,6 +345,8 @@ def __set_target_variables_aux (manager, rule_or_module, ps):
return result
def __handle_flag_value (manager, value, ps):
assert isinstance(value, basestring)
assert isinstance(ps, property_set.PropertySet)
result = []
if get_grist (value):
@@ -355,8 +382,13 @@ def __add_flag (rule_or_module, variable_name, condition, values):
""" Adds a new flag setting with the specified values.
Does no checking.
"""
assert isinstance(rule_or_module, basestring)
assert isinstance(variable_name, basestring)
assert is_iterable_typed(condition, property_set.PropertySet)
assert is_iterable(values) and all(
isinstance(v, (basestring, type(None))) for v in values)
f = Flag(variable_name, values, condition, rule_or_module)
# Grab the name of the module
m = __re_first_segment.match (rule_or_module)
assert m
@@ -377,7 +409,7 @@ def add_requirements(requirements):
will be automatically added to the requirements for all main targets, as if
they were specified literally. For best results, all requirements added should
be conditional or indirect conditional."""
assert is_iterable_typed(requirements, basestring)
#if ! $(.ignore-requirements)
#{
__requirements.extend(requirements)
@@ -392,6 +424,8 @@ def add_requirements(requirements):
# 3. All flags are inherited
# 4. All rules are imported.
def inherit(toolset, base):
assert isinstance(toolset, basestring)
assert isinstance(base, basestring)
get_manager().projects().load_module(base, []);
inherit_generators(toolset, [], base)

View File

@@ -14,7 +14,7 @@ import os.path
from b2.util.utility import replace_grist, os_name
from b2.exceptions import *
from b2.build import feature, property, scanner
from b2.util import bjam_signature
from b2.util import bjam_signature, is_iterable_typed
__re_hyphen = re.compile ('-')
@@ -87,17 +87,17 @@ def register (type, suffixes = [], base_type = None):
# Generated targets of 'type' will use the first of 'suffixes'
# (this may be overriden)
set_generated_target_suffix (type, [], suffixes [0])
# Specify mapping from suffixes to type
register_suffixes (suffixes, type)
feature.extend('target-type', [type])
feature.extend('main-target-type', [type])
feature.extend('base-target-type', [type])
if base_type:
feature.compose ('<target-type>' + type, replace_grist (base_type, '<base-target-type>'))
feature.compose ('<base-target-type>' + type, '<base-target-type>' + base_type)
feature.compose ('<target-type>' + type, [replace_grist (base_type, '<base-target-type>')])
feature.compose ('<base-target-type>' + type, ['<base-target-type>' + base_type])
import b2.build.generators as generators
# Adding a new derived type affects generator selection so we need to
@@ -111,6 +111,7 @@ def register (type, suffixes = [], base_type = None):
# FIXME: quick hack.
def type_from_rule_name(rule_name):
assert isinstance(rule_name, basestring)
return rule_name.upper().replace("-", "_")
@@ -118,6 +119,8 @@ def register_suffixes (suffixes, type):
""" Specifies that targets with suffix from 'suffixes' have the type 'type'.
If a different type is already specified for any of syffixes, issues an error.
"""
assert is_iterable_typed(suffixes, basestring)
assert isinstance(type, basestring)
for s in suffixes:
if __suffixes_to_types.has_key (s):
old_type = __suffixes_to_types [s]
@@ -129,23 +132,33 @@ def register_suffixes (suffixes, type):
def registered (type):
""" Returns true iff type has been registered.
"""
assert isinstance(type, basestring)
return __types.has_key (type)
def validate (type):
""" Issues an error if 'type' is unknown.
"""
assert isinstance(type, basestring)
if not registered (type):
raise BaseException ("Unknown target type '%s'" % type)
def set_scanner (type, scanner):
""" Sets a scanner class that will be used for this 'type'.
"""
if __debug__:
from .scanner import Scanner
assert isinstance(type, basestring)
assert issubclass(scanner, Scanner)
validate (type)
__types [type]['scanner'] = scanner
def get_scanner (type, prop_set):
""" Returns a scanner instance appropriate to 'type' and 'property_set'.
"""
if __debug__:
from .property_set import PropertySet
assert isinstance(type, basestring)
assert isinstance(prop_set, PropertySet)
if registered (type):
scanner_type = __types [type]['scanner']
if scanner_type:
@@ -157,12 +170,13 @@ def get_scanner (type, prop_set):
def base(type):
"""Returns a base type for the given type or nothing in case the given type is
not derived."""
assert isinstance(type, basestring)
return __types[type]['base']
def all_bases (type):
""" Returns type and all of its bases, in the order of their distance from type.
"""
assert isinstance(type, basestring)
result = []
while type:
result.append (type)
@@ -173,6 +187,7 @@ def all_bases (type):
def all_derived (type):
""" Returns type and all classes that derive from it, in the order of their distance from type.
"""
assert isinstance(type, basestring)
result = [type]
for d in __types [type]['derived']:
result.extend (all_derived (d))
@@ -182,6 +197,8 @@ def all_derived (type):
def is_derived (type, base):
""" Returns true if 'type' is 'base' or has 'base' as its direct or indirect base.
"""
assert isinstance(type, basestring)
assert isinstance(base, basestring)
# TODO: this isn't very efficient, especially for bases close to type
if base in all_bases (type):
return True
@@ -191,6 +208,8 @@ def is_derived (type, base):
def is_subtype (type, base):
""" Same as is_derived. Should be removed.
"""
assert isinstance(type, basestring)
assert isinstance(base, basestring)
# TODO: remove this method
return is_derived (type, base)
@@ -208,6 +227,9 @@ def set_generated_target_suffix (type, properties, suffix):
The 'suffix' parameter can be empty string ("") to indicate that
no suffix should be used.
"""
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(suffix, basestring)
set_generated_target_ps(1, type, properties, suffix)
@@ -216,9 +238,16 @@ def change_generated_target_suffix (type, properties, suffix):
""" Change the suffix previously registered for this type/properties
combination. If suffix is not yet specified, sets it.
"""
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(suffix, basestring)
change_generated_target_ps(1, type, properties, suffix)
def generated_target_suffix(type, properties):
if __debug__:
from .property_set import PropertySet
assert isinstance(type, basestring)
assert isinstance(properties, PropertySet)
return generated_target_ps(1, type, properties)
# Sets a target prefix that should be used when generating targets of 'type'
@@ -236,16 +265,31 @@ def set_generated_target_prefix(type, properties, prefix):
# Change the prefix previously registered for this type/properties combination.
# If prefix is not yet specified, sets it.
def change_generated_target_prefix(type, properties, prefix):
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(prefix, basestring)
change_generated_target_ps(0, type, properties, prefix)
def generated_target_prefix(type, properties):
if __debug__:
from .property_set import PropertySet
assert isinstance(type, basestring)
assert isinstance(properties, PropertySet)
return generated_target_ps(0, type, properties)
def set_generated_target_ps(is_suffix, type, properties, val):
assert isinstance(is_suffix, (int, bool))
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(val, basestring)
properties.append ('<target-type>' + type)
__prefixes_suffixes[is_suffix].insert (properties, val)
def change_generated_target_ps(is_suffix, type, properties, val):
assert isinstance(is_suffix, (int, bool))
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
assert isinstance(val, basestring)
properties.append ('<target-type>' + type)
prev = __prefixes_suffixes[is_suffix].find_replace(properties, val)
if not prev:
@@ -256,7 +300,9 @@ def change_generated_target_ps(is_suffix, type, properties, val):
# If no prefix/suffix is specified for 'type', returns prefix/suffix for
# base type, if any.
def generated_target_ps_real(is_suffix, type, properties):
assert isinstance(is_suffix, (int, bool))
assert isinstance(type, basestring)
assert is_iterable_typed(properties, basestring)
result = ''
found = False
while type and not found:
@@ -278,6 +324,11 @@ def generated_target_ps(is_suffix, type, prop_set):
with the specified properties. If not suffix were specified for
'type', returns suffix for base type, if any.
"""
if __debug__:
from .property_set import PropertySet
assert isinstance(is_suffix, (int, bool))
assert isinstance(type, basestring)
assert isinstance(prop_set, PropertySet)
key = (is_suffix, type, prop_set)
v = __target_suffixes_cache.get(key, None)
@@ -292,6 +343,7 @@ def type(filename):
tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and
"so" will be tried.
"""
assert isinstance(filename, basestring)
while 1:
filename, suffix = os.path.splitext (filename)
if not suffix: return None
@@ -306,6 +358,10 @@ def register_type (type, suffixes, base_type = None, os = []):
if os is not specified. This rule is injected into each of the type
modules for the sake of convenience.
"""
assert isinstance(type, basestring)
assert is_iterable_typed(suffixes, basestring)
assert isinstance(base_type, basestring) or base_type is None
assert is_iterable_typed(os, basestring)
if registered (type):
return

View File

@@ -67,7 +67,7 @@ import os.path
import string
import types
from b2.util import path, utility, set
from b2.util import path, utility, set, is_iterable_typed
from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, get_value
from b2.util.sequence import unique
from b2.tools import common
@@ -110,6 +110,7 @@ class VirtualTargetRegistry:
and equal action. If such target is found it is retured and 'target' is not registered.
Otherwise, 'target' is registered and returned.
"""
assert isinstance(target, VirtualTarget)
if target.path():
signature = target.path() + "-" + target.name()
else:
@@ -156,6 +157,11 @@ class VirtualTargetRegistry:
for the project, and use that path to determine if the target was already created.
TODO: passing project with all virtual targets starts to be annoying.
"""
if __debug__:
from .targets import ProjectTarget
assert isinstance(file, basestring)
assert isinstance(file_location, basestring)
assert isinstance(project, ProjectTarget)
# Check if we've created a target corresponding to this file.
path = os.path.join(os.getcwd(), file_location, file)
path = os.path.normpath(path)
@@ -192,6 +198,8 @@ class VirtualTargetRegistry:
return [t for t in targets if b2.build.type.is_sybtype(t.type(), type)]
def register_actual_name (self, actual_name, virtual_target):
assert isinstance(actual_name, basestring)
assert isinstance(virtual_target, VirtualTarget)
if self.actual_.has_key (actual_name):
cs1 = self.actual_ [actual_name].creating_subvariant ()
cs2 = virtual_target.creating_subvariant ()
@@ -238,6 +246,9 @@ class VirtualTargetRegistry:
""" Appends the suffix appropriate to 'type/property_set' combination
to the specified name and returns the result.
"""
assert isinstance(specified_name, basestring)
assert isinstance(file_type, basestring)
assert isinstance(prop_set, property_set.PropertySet)
suffix = b2.build.type.generated_target_suffix (file_type, prop_set)
if suffix:
@@ -254,6 +265,10 @@ class VirtualTarget:
project: project to which this target belongs.
"""
def __init__ (self, name, project):
if __debug__:
from .targets import ProjectTarget
assert isinstance(name, basestring)
assert isinstance(project, ProjectTarget)
self.name_ = name
self.project_ = project
self.dependencies_ = []
@@ -302,6 +317,9 @@ class VirtualTarget:
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_:
@@ -373,6 +391,9 @@ class AbstractFileTarget (VirtualTarget):
type: optional type of this target.
"""
def __init__ (self, name, type, project, action = None, exact=False):
assert isinstance(type, basestring) or type is None
assert action is None or isinstance(action, Action)
assert isinstance(exact, (int, bool))
VirtualTarget.__init__ (self, name, project)
self.type_ = type
@@ -402,6 +423,7 @@ class AbstractFileTarget (VirtualTarget):
""" Sets the path. When generating target name, it will override any path
computation from properties.
"""
assert isinstance(path, basestring)
self.path_ = os.path.normpath(path)
def action (self):
@@ -413,6 +435,7 @@ class AbstractFileTarget (VirtualTarget):
""" Sets/gets the 'root' flag. Target is root is it directly correspods to some
variant of a main target.
"""
assert isinstance(set, (int, bool, type(None)))
if set:
self.root_ = True
return self.root_
@@ -425,6 +448,7 @@ class AbstractFileTarget (VirtualTarget):
s: If specified, specified the value to set,
which should be instance of 'subvariant' class.
"""
assert s is None or isinstance(s, Subvariant)
if s and not self.creating_subvariant ():
if self.creating_subvariant ():
raise BaseException ("Attempt to change 'dg'")
@@ -435,6 +459,7 @@ class AbstractFileTarget (VirtualTarget):
return self.creating_subvariant_
def actualize_action (self, target):
assert isinstance(target, basestring)
if self.action_:
self.action_.actualize ()
@@ -513,7 +538,7 @@ class AbstractFileTarget (VirtualTarget):
If not <tag> property is specified, or the rule specified by
<tag> returns nothing, returns the result of calling
virtual-target.add-suffix"""
assert isinstance(specified_name, basestring)
if self.action_:
ps = self.action_.properties()
else:
@@ -627,6 +652,9 @@ class FileTarget (AbstractFileTarget):
- the suffix which correspond to the target's type.
"""
def __init__ (self, name, type, project, action = None, path=None, exact=False):
assert isinstance(type, basestring) or type is None
assert action is None or isinstance(action, Action)
assert isinstance(exact, (int, bool))
AbstractFileTarget.__init__ (self, name, type, project, action, exact)
self.path_ = path
@@ -638,10 +666,12 @@ class FileTarget (AbstractFileTarget):
return self.name_
def clone_with_different_type(self, new_type):
assert isinstance(new_type, basestring)
return FileTarget(self.name_, new_type, self.project_,
self.action_, self.path_, exact=True)
def actualize_location (self, target):
assert isinstance(target, basestring)
engine = self.project_.manager_.engine ()
if self.action_:
@@ -714,6 +744,7 @@ class FileTarget (AbstractFileTarget):
class NotFileTarget(AbstractFileTarget):
def __init__(self, name, project, action):
assert isinstance(action, Action)
AbstractFileTarget.__init__(self, name, None, project, action)
def path(self):
@@ -721,6 +752,7 @@ class NotFileTarget(AbstractFileTarget):
return None
def actualize_location(self, target):
assert isinstance(target, basestring)
bjam.call("NOTFILE", target)
bjam.call("ALWAYS", target)
bjam.call("NOUPDATE", target)
@@ -735,8 +767,9 @@ class Action:
not establish dependency relationship, but should do everything else.
"""
def __init__ (self, manager, sources, action_name, prop_set):
assert is_iterable_typed(sources, VirtualTarget)
assert isinstance(action_name, basestring) or action_name is None
assert(isinstance(prop_set, property_set.PropertySet))
assert type(sources) == types.ListType
self.sources_ = sources
self.action_name_ = action_name
if not prop_set:
@@ -758,11 +791,14 @@ class Action:
def add_targets (self, targets):
assert is_iterable_typed(targets, VirtualTarget)
self.targets_ += targets
def replace_targets (old_targets, new_targets):
self.targets_ = [t for t in targets if not t in old_targets] + new_targets
def replace_targets(self, old_targets, new_targets):
assert is_iterable_typed(old_targets, VirtualTarget)
assert is_iterable_typed(new_targets, VirtualTarget)
self.targets_ = [t for t in self.targets_ if not t in old_targets] + new_targets
def targets (self):
return self.targets_
@@ -826,6 +862,8 @@ class Action:
For each passed source, actualizes it with the appropriate scanner.
Returns the actualized virtual targets.
"""
assert is_iterable_typed(sources, VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
result = []
for i in sources:
scanner = None
@@ -852,6 +890,8 @@ class Action:
New values will be *appended* to the variables. They may be non-empty,
if caller wants it.
"""
assert is_iterable_typed(sources, VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
dependencies = self.properties_.get ('<dependency>')
self.dependency_only_sources_ += self.actualize_source_type (dependencies, prop_set)
@@ -879,6 +919,7 @@ class Action:
to get generated headers correctly. Default implementation returns
its argument.
"""
assert isinstance(prop_set, property_set.PropertySet)
return prop_set
@@ -889,6 +930,7 @@ class NullAction (Action):
actions which create them.
"""
def __init__ (self, manager, prop_set):
assert isinstance(prop_set, property_set.PropertySet)
Action.__init__ (self, manager, [], None, prop_set)
def actualize (self):
@@ -908,7 +950,8 @@ class NonScanningAction(Action):
Action.__init__(self, b2.manager.get_manager(), sources, action_name, property_set)
def actualize_source_type(self, sources, property_set):
assert is_iterable_typed(sources, VirtualTarget)
assert isinstance(property_set, property_set.PropertySet)
result = []
for s in sources:
result.append(s.actualize())
@@ -920,6 +963,9 @@ def traverse (target, include_roots = False, include_sources = False):
found during traversal, it's either included or not, dependencing of the
value of 'include_roots'. In either case, sources of root are not traversed.
"""
assert isinstance(target, VirtualTarget)
assert isinstance(include_roots, (int, bool))
assert isinstance(include_sources, (int, bool))
result = []
if target.action ():
@@ -951,7 +997,12 @@ def clone_action (action, new_project, new_action_name, new_properties):
and all produced target. The rule-name and properties are set
to 'new-rule-name' and 'new-properties', if those are specified.
Returns the cloned action."""
if __debug__:
from .targets import ProjectTarget
assert isinstance(action, Action)
assert isinstance(new_project, ProjectTarget)
assert isinstance(new_action_name, basestring)
assert isinstance(new_properties, property_set.PropertySet)
if not new_action_name:
new_action_name = action.action_name()
@@ -990,6 +1041,14 @@ class Subvariant:
sources_usage_requirements: Properties propagated from sources
created_targets: Top-level created targets
"""
if __debug__:
from .targets import AbstractTarget
assert isinstance(main_target, AbstractTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, VirtualTarget)
assert isinstance(build_properties, property_set.PropertySet)
assert isinstance(sources_usage_requirements, property_set.PropertySet)
assert is_iterable_typed(created_targets, VirtualTarget)
self.main_target_ = main_target
self.properties_ = prop_set
self.sources_ = sources
@@ -1028,6 +1087,7 @@ class Subvariant:
return self.sources_usage_requirements_
def set_usage_requirements (self, usage_requirements):
assert isinstance(usage_requirements, property_set.PropertySet)
self.usage_requirements_ = usage_requirements
def usage_requirements (self):
@@ -1038,7 +1098,9 @@ class Subvariant:
either directly or indirectly, and either as sources,
or as dependency properties. Targets referred with
dependency property are returned a properties, not targets."""
if __debug__:
from .targets import GenerateResult
assert isinstance(result, GenerateResult)
# Find directly referenced targets.
deps = self.build_properties().dependency()
all_targets = self.sources_ + deps
@@ -1071,7 +1133,8 @@ class Subvariant:
if 'target_type' is not specified), the result will contain
<$(feature)>path-to-that-target.
"""
assert isinstance(feature, basestring)
assert isinstance(target_type, basestring)
if not target_type:
key = feature
else:
@@ -1088,6 +1151,7 @@ class Subvariant:
return result
def all_target_directories(self, target_type = None):
assert isinstance(target_type, (basestring, type(None)))
# TODO: does not appear to use target_type in deciding
# if we've computed this already.
if not self.target_directories_:
@@ -1095,6 +1159,7 @@ class Subvariant:
return self.target_directories_
def compute_target_directories(self, target_type=None):
assert isinstance(target_type, (basestring, type(None)))
result = []
for t in self.created_targets():
if not target_type or b2.build.type.is_derived(t.type(), target_type):

View File

@@ -206,6 +206,7 @@ int anyhow = 0;
extern PyObject * bjam_variable ( PyObject * self, PyObject * args );
extern PyObject * bjam_backtrace ( PyObject * self, PyObject * args );
extern PyObject * bjam_caller ( PyObject * self, PyObject * args );
int python_optimize = 1; /* Set Python optimzation on by default */
#endif
void regex_done();
@@ -235,7 +236,13 @@ int main( int argc, char * * argv, char * * arg_environ )
--argc;
++argv;
if ( getoptions( argc, argv, "-:l:m:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
#ifdef HAVE_PYTHON
#define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qvz"
#else
#define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qv"
#endif
if ( getoptions( argc, argv, OPTSTRING, optv ) < 0 )
{
err_printf( "\nusage: %s [ options ] targets...\n\n", progname );
@@ -253,6 +260,9 @@ int main( int argc, char * * argv, char * * arg_environ )
err_printf( "-sx=y Set variable x=y, overriding environment.\n" );
err_printf( "-tx Rebuild x, even if it is up-to-date.\n" );
err_printf( "-v Print the version of jam and exit.\n" );
#ifdef HAVE_PYTHON
err_printf( "-z Disable Python Optimization and enable asserts\n" );
#endif
err_printf( "--x Option is ignored.\n\n" );
exit( EXITBAD );
@@ -318,6 +328,11 @@ int main( int argc, char * * argv, char * * arg_environ )
if ( ( s = getoptval( optv, 'm', 0 ) ) )
globs.max_buf = atoi( s ) * 1024; /* convert to kb */
#ifdef HAVE_PYTHON
if ( ( s = getoptval( optv, 'z', 0 ) ) )
python_optimize = 0; /* disable python optimization */
#endif
/* Turn on/off debugging */
for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n )
{
@@ -364,6 +379,7 @@ int main( int argc, char * * argv, char * * arg_environ )
#ifdef HAVE_PYTHON
{
PROFILE_ENTER( MAIN_PYTHON );
Py_OptimizeFlag = python_optimize;
Py_Initialize();
{
static PyMethodDef BjamMethods[] = {

View File

@@ -13,7 +13,7 @@ import b2.build.targets as targets
import sys
from b2.build import feature, property, virtual_target, generators, type, property_set, scanner
from b2.util.utility import *
from b2.util import path, regex, bjam_signature
from b2.util import path, regex, bjam_signature, is_iterable_typed
import b2.tools.types
from b2.manager import get_manager
@@ -413,9 +413,12 @@ class LibGenerator (generators.Generator):
def __init__(self, id, composing = True, source_types = [], target_types_and_names = ['LIB'], requirements = []):
generators.Generator.__init__(self, id, composing, source_types, target_types_and_names, requirements)
def run(self, project, name, prop_set, sources):
def run(self, project, name, prop_set, sources):
assert isinstance(project, targets.ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
# The lib generator is composing, and can be only invoked with
# explicit name. This check is present in generator.run (and so in
# builtin.LinkingGenerator), but duplicate it here to avoid doing
@@ -451,7 +454,11 @@ generators.override("builtin.prebuilt", "builtin.lib-generator")
def lib(names, sources=[], requirements=[], default_build=[], usage_requirements=[]):
"""The implementation of the 'lib' rule. Beyond standard syntax that rule allows
simplified: 'lib a b c ;'."""
assert is_iterable_typed(names, basestring)
assert is_iterable_typed(sources, basestring)
assert is_iterable_typed(requirements, basestring)
assert is_iterable_typed(default_build, basestring)
assert is_iterable_typed(usage_requirements, basestring)
if len(names) > 1:
if any(r.startswith('<name>') for r in requirements):
get_manager().errors()("When several names are given to the 'lib' rule\n" +
@@ -490,8 +497,12 @@ class SearchedLibGenerator (generators.Generator):
# is make sure SearchedLibGenerator is not invoked deep in transformation
# search.
generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
def run(self, project, name, prop_set, sources):
assert isinstance(project, targets.ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
if not name:
return None
@@ -525,9 +536,14 @@ generators.register (SearchedLibGenerator ())
class PrebuiltLibGenerator(generators.Generator):
def __init__(self, id, composing, source_types, target_types_and_names, requirements):
generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
def run(self, project, name, properties, sources):
assert isinstance(project, targets.ProjectTarget)
assert isinstance(name, basestring)
assert isinstance(properties, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
f = properties.get("file")
return f + sources
@@ -546,6 +562,7 @@ class CompileAction (virtual_target.Action):
i.e. which belong to the same main target, add their directories
to include path.
"""
assert isinstance(prop_set, property_set.PropertySet)
s = self.targets () [0].creating_subvariant ()
return prop_set.add_raw (s.implicit_includes ('include', 'H'))
@@ -576,6 +593,10 @@ class LinkingGenerator (generators.Generator):
generators.Generator.__init__ (self, id, composing, source_types, target_types_and_names, requirements)
def run (self, project, name, prop_set, sources):
assert isinstance(project, targets.ProjectTarget)
assert isinstance(name, basestring) or name is None
assert isinstance(prop_set, property_set.PropertySet)
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
sources.extend(prop_set.get('<library>'))
@@ -623,7 +644,8 @@ class LinkingGenerator (generators.Generator):
return (ur, result)
def extra_usage_requirements (self, created_targets, prop_set):
assert is_iterable_typed(created_targets, virtual_target.VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
result = property_set.empty ()
extra = []
@@ -659,7 +681,10 @@ class LinkingGenerator (generators.Generator):
return result
def generated_targets (self, sources, prop_set, project, name):
assert is_iterable_typed(sources, virtual_target.VirtualTarget)
assert isinstance(prop_set, property_set.PropertySet)
assert isinstance(project, targets.ProjectTarget)
assert isinstance(name, basestring)
# sources to pass to inherited rule
sources2 = []
# sources which are libraries

View File

@@ -25,18 +25,22 @@
# > cast, as defining a new target type + generator for that type is somewhat
# > simpler than defining a main target rule.
import b2.build.targets as targets
import b2.build.virtual_target as virtual_target
from b2.build import targets, virtual_target, property_set
from b2.manager import get_manager
from b2.util import bjam_signature
from b2.util import bjam_signature, is_iterable_typed
class CastTargetClass(targets.TypedTarget):
def construct(name, source_targets, ps):
def construct(self, name, source_targets, ps):
assert isinstance(name, basestring)
assert is_iterable_typed(source_targets, virtual_target.VirtualTarget)
assert isinstance(ps, property_set.PropertySet)
result = []
for s in source_targets:
if not isinstance(s, virtual_targets.FileTarget):
if not isinstance(s, virtual_target.FileTarget):
get_manager().errors()("Source to the 'cast' metatager is not a file")
if s.action():
@@ -46,8 +50,8 @@ class CastTargetClass(targets.TypedTarget):
r = s.clone_with_different_type(self.type())
result.append(get_manager().virtual_targets().register(r))
return result
return property_set.empty(), result
@bjam_signature((["name", "type"], ["sources", "*"], ["requirements", "*"],
["default_build", "*"], ["usage_requirements", "*"]))

View File

@@ -21,7 +21,7 @@ import sys
import b2.build.virtual_target
from b2.build import feature, type
from b2.util.utility import *
from b2.util import path
from b2.util import path, is_iterable_typed
__re__before_first_dash = re.compile ('([^-]*)-')
@@ -112,6 +112,7 @@ class Configurations(object):
Returns True if the configuration has been added and False if
it already exists. Reports an error if the configuration is 'used'.
"""
assert isinstance(id, basestring)
if id in self.used_:
#FIXME
errors.error("common: the configuration '$(id)' is in use")
@@ -132,6 +133,7 @@ class Configurations(object):
'used' and False if it the state wasn't changed. Reports an error
if the configuration isn't known.
"""
assert isinstance(id, basestring)
if id not in self.all_:
#FIXME:
errors.error("common: the configuration '$(id)' is not known")
@@ -154,10 +156,15 @@ class Configurations(object):
def get(self, id, param):
""" Returns the value of a configuration parameter. """
assert isinstance(id, basestring)
assert isinstance(param, basestring)
return self.params_.get(param, {}).get(id)
def set (self, id, param, value):
""" Sets the value of a configuration parameter. """
assert isinstance(id, basestring)
assert isinstance(param, basestring)
assert is_iterable_typed(value, basestring)
self.params_.setdefault(param, {})[id] = value
# Ported from trunk@47174
@@ -174,14 +181,11 @@ def check_init_parameters(toolset, requirement, *args):
The return value from this rule is a condition to be used for flags settings.
"""
assert isinstance(toolset, basestring)
assert is_iterable_typed(requirement, basestring)
from b2.build import toolset as b2_toolset
if requirement is None:
requirement = []
# The type checking here is my best guess about
# what the types should be.
assert(isinstance(toolset, str))
# iterable and not a string, allows for future support of sets
assert(not isinstance(requirement, basestring) and hasattr(requirement, '__contains__'))
sig = toolset
condition = replace_grist(toolset, '<toolset>')
subcondition = []
@@ -290,15 +294,12 @@ def get_invocation_command_nodefault(
find the tool, a warning is issued. If 'path-last' is specified, PATH is
checked after 'additional-paths' when searching for 'tool'.
"""
assert(isinstance(toolset, str))
assert(isinstance(tool, str))
assert(isinstance(user_provided_command, list))
if additional_paths is not None:
assert(isinstance(additional_paths, list))
assert(all([isinstance(path, str) for path in additional_paths]))
assert(all(isinstance(path, str) for path in additional_paths))
assert(isinstance(path_last, bool))
assert isinstance(toolset, basestring)
assert isinstance(tool, basestring)
assert is_iterable_typed(user_provided_command, basestring)
assert is_iterable_typed(additional_paths, basestring) or additional_paths is None
assert isinstance(path_last, (int, bool))
if not user_provided_command:
command = find_tool(tool, additional_paths, path_last)
if not command and __debug_configuration:
@@ -307,13 +308,13 @@ def get_invocation_command_nodefault(
#print "warning: initialized from" [ errors.nearest-user-location ] ;
else:
command = check_tool(user_provided_command)
assert(isinstance(command, list))
command=' '.join(command)
if not command and __debug_configuration:
print "warning: toolset", toolset, "initialization:"
print "warning: can't find user-provided command", user_provided_command
#FIXME
#ECHO "warning: initialized from" [ errors.nearest-user-location ]
command = []
command = ' '.join(command)
assert(isinstance(command, str))
@@ -325,14 +326,11 @@ def get_invocation_command(toolset, tool, user_provided_command = [],
""" Same as get_invocation_command_nodefault, except that if no tool is found,
returns either the user-provided-command, if present, or the 'tool' parameter.
"""
assert(isinstance(toolset, str))
assert(isinstance(tool, str))
assert(isinstance(user_provided_command, list))
if additional_paths is not None:
assert(isinstance(additional_paths, list))
assert(all([isinstance(path, str) for path in additional_paths]))
assert(isinstance(path_last, bool))
assert isinstance(toolset, basestring)
assert isinstance(tool, basestring)
assert is_iterable_typed(user_provided_command, basestring)
assert is_iterable_typed(additional_paths, basestring) or additional_paths is None
assert isinstance(path_last, (int, bool))
result = get_invocation_command_nodefault(toolset, tool,
user_provided_command,
@@ -356,6 +354,7 @@ def get_absolute_tool_path(command):
return the absolute path to the command. This works even if commnad
has not path element and is present in PATH.
"""
assert isinstance(command, basestring)
if os.path.dirname(command):
return os.path.dirname(command)
else:
@@ -376,9 +375,9 @@ def find_tool(name, additional_paths = [], path_last = False):
Otherwise, returns the empty string. If 'path_last' is specified,
path is checked after 'additional_paths'.
"""
assert(isinstance(name, str))
assert(isinstance(additional_paths, list))
assert(isinstance(path_last, bool))
assert isinstance(name, basestring)
assert is_iterable_typed(additional_paths, basestring)
assert isinstance(path_last, (int, bool))
programs = path.programs_path()
match = path.glob(programs, [name, name + '.exe'])
@@ -407,7 +406,7 @@ def check_tool_aux(command):
""" Checks if 'command' can be found either in path
or is a full name to an existing file.
"""
assert(isinstance(command, str))
assert isinstance(command, basestring)
dirname = os.path.dirname(command)
if dirname:
if os.path.exists(command):
@@ -430,8 +429,7 @@ def check_tool(command):
If comand is absolute path, check that it exists. Returns 'command'
if ok and empty string otherwise.
"""
assert(isinstance(command, list))
assert(all(isinstance(c, str) for c in command))
assert is_iterable_typed(command, basestring)
#FIXME: why do we check the first and last elements????
if check_tool_aux(command[0]) or check_tool_aux(command[-1]):
return command
@@ -449,11 +447,10 @@ def handle_options(tool, condition, command, options):
"""
from b2.build import toolset
assert(isinstance(tool, str))
assert(isinstance(condition, list))
assert(isinstance(command, str))
assert(isinstance(options, list))
assert(command)
assert isinstance(tool, basestring)
assert is_iterable_typed(condition, basestring)
assert command and isinstance(command, basestring)
assert is_iterable_typed(options, basestring)
toolset.flags(tool, 'CONFIG_COMMAND', condition, [command])
toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('<compileflags>', options))
toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('<cflags>', options))
@@ -490,8 +487,8 @@ def variable_setting_command(variable, value):
words, on Unix systems, the variable is exported, which is consistent with the
only possible behavior on Windows systems.
"""
assert(isinstance(variable, str))
assert(isinstance(value, str))
assert isinstance(variable, basestring)
assert isinstance(value, basestring)
if os_name() == 'NT':
return "set " + variable + "=" + value + os.linesep
@@ -533,8 +530,8 @@ def path_variable_setting_command(variable, paths):
Returns a command to sets a named shell path variable to the given NATIVE
paths on the current platform.
"""
assert(isinstance(variable, str))
assert(isinstance(paths, list))
assert isinstance(variable, basestring)
assert is_iterable_typed(paths, basestring)
sep = os.path.pathsep
return variable_setting_command(variable, sep.join(paths))
@@ -542,7 +539,9 @@ def prepend_path_variable_command(variable, paths):
"""
Returns a command that prepends the given paths to the named path variable on
the current platform.
"""
"""
assert isinstance(variable, basestring)
assert is_iterable_typed(paths, basestring)
return path_variable_setting_command(variable,
paths + os.environ.get(variable, "").split(os.pathsep))
@@ -562,6 +561,7 @@ __mkdir_set = set()
__re_windows_drive = re.compile(r'^.*:\$')
def mkdir(engine, target):
assert isinstance(target, basestring)
# If dir exists, do not update it. Do this even for $(DOT).
bjam.call('NOUPDATE', target)
@@ -642,9 +642,12 @@ def format_name(format, name, target_type, prop_set):
The returned name also has the target type specific prefix and suffix which
puts it in a ready form to use as the value from a custom tag rule.
"""
assert(isinstance(format, list))
assert(isinstance(name, str))
assert(isinstance(target_type, str) or not type)
if __debug__:
from ..build.property_set import PropertySet
assert is_iterable_typed(format, basestring)
assert isinstance(name, basestring)
assert isinstance(target_type, basestring)
assert isinstance(prop_set, PropertySet)
# assert(isinstance(prop_set, property_set.PropertySet))
if type.is_derived(target_type, 'LIB'):
result = "" ;
@@ -690,6 +693,8 @@ def format_name(format, name, target_type, prop_set):
return result
def join_tag(joiner, tag):
assert isinstance(joiner, basestring)
assert isinstance(tag, basestring)
if tag:
if not joiner: joiner = '-'
return joiner + tag
@@ -698,6 +703,11 @@ def join_tag(joiner, tag):
__re_toolset_version = re.compile(r"<toolset.*version>(\d+)[.](\d*)")
def toolset_tag(name, target_type, prop_set):
if __debug__:
from ..build.property_set import PropertySet
assert isinstance(name, basestring)
assert isinstance(target_type, basestring)
assert isinstance(prop_set, PropertySet)
tag = ''
properties = prop_set.raw()
@@ -708,7 +718,7 @@ def toolset_tag(name, target_type, prop_set):
elif tools.startswith('como'): tag += 'como'
elif tools.startswith('cw'): tag += 'cw'
elif tools.startswith('darwin'): tag += 'xgcc'
elif tools.startswith('edg'): tag += edg
elif tools.startswith('edg'): tag += 'edg'
elif tools.startswith('gcc'):
flavor = prop_set.get('<toolset-gcc:flavor>')
''.find
@@ -764,6 +774,11 @@ def toolset_tag(name, target_type, prop_set):
def threading_tag(name, target_type, prop_set):
if __debug__:
from ..build.property_set import PropertySet
assert isinstance(name, basestring)
assert isinstance(target_type, basestring)
assert isinstance(prop_set, PropertySet)
tag = ''
properties = prop_set.raw()
if '<threading>multi' in properties: tag = 'mt'
@@ -772,6 +787,11 @@ def threading_tag(name, target_type, prop_set):
def runtime_tag(name, target_type, prop_set ):
if __debug__:
from ..build.property_set import PropertySet
assert isinstance(name, basestring)
assert isinstance(target_type, basestring)
assert isinstance(prop_set, PropertySet)
tag = ''
properties = prop_set.raw()

View File

@@ -45,7 +45,7 @@ import b2.build_system as build_system
from b2.manager import get_manager
from b2.util import stem, bjam_signature
from b2.util import stem, bjam_signature, is_iterable_typed
from b2.util.sequence import unique
import bjam
@@ -88,7 +88,10 @@ __all_tests = []
# Helper rule. Create a test target, using basename of first source if no target
# name is explicitly passed. Remembers the created target in a global variable.
def make_test(target_type, sources, requirements, target_name=None):
assert isinstance(target_type, basestring)
assert is_iterable_typed(sources, basestring)
assert is_iterable_typed(requirements, basestring)
assert isinstance(target_type, basestring) or target_type is None
if not target_name:
target_name = stem(os.path.basename(sources[0]))
@@ -189,7 +192,7 @@ __ln1 = re.compile("/(tools|libs)/(.*)/(test|example)")
__ln2 = re.compile("/(tools|libs)/(.*)$")
__ln3 = re.compile("(/status$)")
def get_library_name(path):
assert isinstance(path, basestring)
path = path.replace("\\", "/")
match1 = __ln1.match(path)
match2 = __ln2.match(path)
@@ -216,6 +219,7 @@ __out_xml = option.get("out-xml", False, True)
# - relative location of all source from the project root.
#
def dump_test(target):
assert isinstance(target, targets.AbstractTarget)
type = target.type()
name = target.name()
project = target.project()
@@ -298,7 +302,11 @@ generators.register_composing("testing.time", [], ["TIME"])
# contained in testing-aux.jam, which we load into Jam module named 'testing'
def run_path_setup(target, sources, ps):
if __debug__:
from ..build.property_set import PropertySet
assert is_iterable_typed(target, basestring) or isinstance(target, basestring)
assert is_iterable_typed(sources, basestring)
assert isinstance(ps, PropertySet)
# For testing, we need to make sure that all dynamic libraries needed by the
# test are found. So, we collect all paths from dependency libraries (via
# xdll-path property) and add whatever explicit dll-path user has specified.
@@ -313,7 +321,12 @@ def run_path_setup(target, sources, ps):
common.shared_library_path_variable(), dll_paths))
def capture_output_setup(target, sources, ps):
run_path_setup(target, sources, ps)
if __debug__:
from ..build.property_set import PropertySet
assert is_iterable_typed(target, basestring)
assert is_iterable_typed(sources, basestring)
assert isinstance(ps, PropertySet)
run_path_setup(target[0], sources, ps)
if ps.get('preserve-test-targets') == ['off']:
bjam.call("set-target-variable", target, "REMOVE_TEST_TARGETS", "1")

View File

@@ -6,14 +6,153 @@ import types
from itertools import groupby
def safe_isinstance(value, types=None, class_names=None):
"""To prevent circular imports, this extends isinstance()
by checking also if `value` has a particular class name (or inherits from a
particular class name). This check is safe in that an AttributeError is not
raised in case `value` doesn't have a __class__ attribute.
"""
# inspect is being imported here because I seriously doubt
# that this function will be used outside of the type
# checking below.
import inspect
result = False
if types is not None:
result = result or isinstance(value, types)
if class_names is not None and not result:
# this doesn't work with inheritance, but normally
# either the class will already be imported within the module,
# or the class doesn't have any subclasses. For example: PropertySet
if isinstance(class_names, basestring):
class_names = [class_names]
# this is the part that makes it "safe".
try:
base_names = [class_.__name__ for class_ in inspect.getmro(value.__class__)]
for name in class_names:
if name in base_names:
return True
except AttributeError:
pass
return result
def is_iterable_typed(values, type_):
return is_iterable(values) and all(isinstance(v, type_) for v in values)
def is_iterable(value):
"""Returns whether value is iterable and not a string."""
return not isinstance(value, basestring) and hasattr(value, '__iter__')
def is_iterable_or_none(value):
return is_iterable(value) or value is None
def is_single_value(value):
# some functions may specify a bjam signature
# that is a string type, but still allow a
# PropertySet to be passed in
return safe_isinstance(value, (basestring, type(None)), 'PropertySet')
if __debug__:
from textwrap import dedent
message = dedent(
"""The parameter "{}" was passed in a wrong type for the "{}()" function.
Actual:
\ttype: {}
\tvalue: {}
Expected:
\t{}
"""
)
bjam_types = {
'*': is_iterable_or_none,
'+': is_iterable_or_none,
'?': is_single_value,
'': is_single_value,
}
bjam_to_python = {
'*': 'iterable',
'+': 'iterable',
'?': 'single value',
'': 'single value',
}
def get_next_var(field):
it = iter(field)
var = it.next()
type_ = None
yield_var = False
while type_ not in bjam_types:
try:
# the first value has already
# been consumed outside of the loop
type_ = it.next()
except StopIteration:
# if there are no more values, then
# var still needs to be returned
yield_var = True
break
if type_ not in bjam_types:
# type_ is not a type and is
# another variable in the same field.
yield var, ''
# type_ is the next var
var = type_
else:
# otherwise, type_ is a type for var
yield var, type_
try:
# the next value should be a var
var = it.next()
except StopIteration:
# if not, then we're done with
# this field
break
if yield_var:
yield var, ''
# Decorator the specifies bjam-side prototype for a Python function
def bjam_signature(s):
if __debug__:
from inspect import getcallargs
def decorator(fn):
function_name = fn.__module__ + '.' + fn.__name__
def wrapper(*args, **kwargs):
callargs = getcallargs(fn, *args, **kwargs)
for field in s:
for var, type_ in get_next_var(field):
try:
value = callargs[var]
except KeyError:
raise Exception(
'Bjam Signature specifies a variable named "{}"\n'
'but is not found within the python function signature\n'
'for function {}()'.format(var, function_name)
)
if not bjam_types[type_](value):
raise TypeError(
message.format(var, function_name, type(type_), repr(value),
bjam_to_python[type_])
)
return fn(*args, **kwargs)
wrapper.__name__ = fn.__name__
wrapper.bjam_signature = s
return wrapper
return decorator
else:
def decorator(f):
f.bjam_signature = s
return f
def wrap(f):
f.bjam_signature = s
return f
return wrap
return decorator
def metatarget(f):

View File

@@ -40,6 +40,7 @@ def make (native):
# TODO: make os selection here.
return make_UNIX (native)
@bjam_signature([['native']])
def make_UNIX (native):
# VP: I have no idea now 'native' can be empty here! But it can!

View File

@@ -5,7 +5,11 @@
import operator
from b2.util import is_iterable
def unique (values, stable=False):
assert is_iterable(values)
if stable:
s = set()
r = []
@@ -21,6 +25,8 @@ def max_element (elements, ordered = None):
""" Returns the maximum number in 'elements'. Uses 'ordered' for comparisons,
or '<' is none is provided.
"""
assert is_iterable(elements)
assert callable(ordered) or ordered is None
if not ordered: ordered = operator.lt
max = elements [0]
@@ -34,6 +40,8 @@ def select_highest_ranked (elements, ranks):
""" Returns all of 'elements' for which corresponding element in parallel
list 'rank' is equal to the maximum value in 'rank'.
"""
assert is_iterable(elements)
assert is_iterable(ranks)
if not elements:
return []

View File

@@ -3,11 +3,15 @@
# 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 utility import to_seq
from b2.util import is_iterable
from .utility import to_seq
def difference (b, a):
""" Returns the elements of B that are not in A.
"""
assert is_iterable(b)
assert is_iterable(a)
result = []
for element in b:
if not element in a:
@@ -18,6 +22,8 @@ def difference (b, a):
def intersection (set1, set2):
""" Removes from set1 any items which don't appear in set2 and returns the result.
"""
assert is_iterable(set1)
assert is_iterable(set2)
result = []
for v in set1:
if v in set2:
@@ -39,4 +45,6 @@ def equal (a, b):
""" Returns True iff 'a' contains the same elements as 'b', irrespective of their order.
# TODO: Python 2.4 has a proper set class.
"""
assert is_iterable(a)
assert is_iterable(b)
return contains (a, b) and contains (b, a)

View File

@@ -11,6 +11,7 @@ import re
import os
import bjam
from b2.exceptions import *
from b2.util import is_iterable_typed
__re_grist_and_value = re.compile (r'(<[^>]*>)(.*)')
__re_grist_content = re.compile ('^<(.*)>$')
@@ -40,13 +41,13 @@ def add_grist (features):
features: one string or a sequence of strings
return: the gristed string, if features is a string, or a sequence of gristed strings, if features is a sequence
"""
assert is_iterable_typed(features, basestring) or isinstance(features, basestring)
def grist_one (feature):
if feature [0] != '<' and feature [len (feature) - 1] != '>':
return '<' + feature + '>'
else:
return feature
if isinstance (features, str):
return grist_one (features)
else:
@@ -56,6 +57,8 @@ def replace_grist (features, new_grist):
""" Replaces the grist of a string by a new one.
Returns the string with the new grist.
"""
assert is_iterable_typed(features, basestring) or isinstance(features, basestring)
assert isinstance(new_grist, basestring)
def replace_grist_one (name, new_grist):
split = __re_grist_and_value.match (name)
if not split:
@@ -71,12 +74,14 @@ def replace_grist (features, new_grist):
def get_value (property):
""" Gets the value of a property, that is, the part following the grist, if any.
"""
assert is_iterable_typed(property, basestring) or isinstance(property, basestring)
return replace_grist (property, '')
def get_grist (value):
""" Returns the grist of a string.
If value is a sequence, does it for every value and returns the result as a sequence.
"""
assert is_iterable_typed(value, basestring) or isinstance(value, basestring)
def get_grist_one (name):
split = __re_grist_and_value.match (name)
if not split:
@@ -93,6 +98,7 @@ def ungrist (value):
""" Returns the value without grist.
If value is a sequence, does it for every value and returns the result as a sequence.
"""
assert is_iterable_typed(value, basestring) or isinstance(value, basestring)
def ungrist_one (value):
stripped = __re_grist_content.match (value)
if not stripped:
@@ -109,12 +115,15 @@ def replace_suffix (name, new_suffix):
""" Replaces the suffix of name by new_suffix.
If no suffix exists, the new one is added.
"""
assert isinstance(name, basestring)
assert isinstance(new_suffix, basestring)
split = os.path.splitext (name)
return split [0] + new_suffix
def forward_slashes (s):
""" Converts all backslashes to forward slashes.
"""
assert isinstance(s, basestring)
return __re_backslash.sub ('/', s)
@@ -122,6 +131,7 @@ def split_action_id (id):
""" Splits an id in the toolset and specific rule parts. E.g.
'gcc.compile.c++' returns ('gcc', 'compile.c++')
"""
assert isinstance(id, basestring)
split = id.split ('.', 1)
toolset = split [0]
name = ''