| |
| import bjam |
| import re |
| import types |
| |
| from itertools import groupby |
| |
| |
| # Decorator the specifies bjam-side prototype for a Python function |
| def bjam_signature(s): |
| |
| def wrap(f): |
| f.bjam_signature = s |
| return f |
| |
| return wrap |
| |
| def metatarget(f): |
| |
| f.bjam_signature = (["name"], ["sources", "*"], ["requirements", "*"], |
| ["default_build", "*"], ["usage_requirements", "*"]) |
| return f |
| |
| class cached(object): |
| |
| def __init__(self, function): |
| self.function = function |
| self.cache = {} |
| |
| def __call__(self, *args): |
| try: |
| return self.cache[args] |
| except KeyError: |
| v = self.function(*args) |
| self.cache[args] = v |
| return v |
| |
| def __get__(self, instance, type): |
| return types.MethodType(self, instance, type) |
| |
| def unquote(s): |
| if s and s[0] == '"' and s[-1] == '"': |
| return s[1:-1] |
| else: |
| return s |
| |
| _extract_jamfile_and_rule = re.compile("(Jamfile<.*>)%(.*)") |
| |
| def qualify_jam_action(action_name, context_module): |
| |
| if action_name.startswith("###"): |
| # Callable exported from Python. Don't touch |
| return action_name |
| elif _extract_jamfile_and_rule.match(action_name): |
| # Rule is already in indirect format |
| return action_name |
| else: |
| ix = action_name.find('.') |
| if ix != -1 and action_name[:ix] == context_module: |
| return context_module + '%' + action_name[ix+1:] |
| |
| return context_module + '%' + action_name |
| |
| |
| def set_jam_action(name, *args): |
| |
| m = _extract_jamfile_and_rule.match(name) |
| if m: |
| args = ("set-update-action-in-module", m.group(1), m.group(2)) + args |
| else: |
| args = ("set-update-action", name) + args |
| |
| return bjam.call(*args) |
| |
| |
| def call_jam_function(name, *args): |
| |
| m = _extract_jamfile_and_rule.match(name) |
| if m: |
| args = ("call-in-module", m.group(1), m.group(2)) + args |
| return bjam.call(*args) |
| else: |
| return bjam.call(*((name,) + args)) |
| |
| __value_id = 0 |
| __python_to_jam = {} |
| __jam_to_python = {} |
| |
| def value_to_jam(value, methods=False): |
| """Makes a token to refer to a Python value inside Jam language code. |
| |
| The token is merely a string that can be passed around in Jam code and |
| eventually passed back. For example, we might want to pass PropertySet |
| instance to a tag function and it might eventually call back |
| to virtual_target.add_suffix_and_prefix, passing the same instance. |
| |
| For values that are classes, we'll also make class methods callable |
| from Jam. |
| |
| Note that this is necessary to make a bit more of existing Jamfiles work. |
| This trick should not be used to much, or else the performance benefits of |
| Python port will be eaten. |
| """ |
| |
| global __value_id |
| |
| r = __python_to_jam.get(value, None) |
| if r: |
| return r |
| |
| exported_name = '###_' + str(__value_id) |
| __value_id = __value_id + 1 |
| __python_to_jam[value] = exported_name |
| __jam_to_python[exported_name] = value |
| |
| if methods and type(value) == types.InstanceType: |
| for field_name in dir(value): |
| field = getattr(value, field_name) |
| if callable(field) and not field_name.startswith("__"): |
| bjam.import_rule("", exported_name + "." + field_name, field) |
| |
| return exported_name |
| |
| def record_jam_to_value_mapping(jam_value, python_value): |
| __jam_to_python[jam_value] = python_value |
| |
| def jam_to_value_maybe(jam_value): |
| |
| if type(jam_value) == type(""): |
| return __jam_to_python.get(jam_value, jam_value) |
| else: |
| return jam_value |
| |
| def stem(filename): |
| i = filename.find('.') |
| if i != -1: |
| return filename[0:i] |
| else: |
| return filename |
| |
| |
| def abbreviate_dashed(s): |
| """Abbreviates each part of string that is delimited by a '-'.""" |
| r = [] |
| for part in s.split('-'): |
| r.append(abbreviate(part)) |
| return '-'.join(r) |
| |
| |
| def abbreviate(s): |
| """Apply a set of standard transformations to string to produce an |
| abbreviation no more than 4 characters long. |
| """ |
| if not s: |
| return '' |
| # check the cache |
| if s in abbreviate.abbreviations: |
| return abbreviate.abbreviations[s] |
| # anything less than 4 characters doesn't need |
| # an abbreviation |
| if len(s) < 4: |
| # update cache |
| abbreviate.abbreviations[s] = s |
| return s |
| # save the first character in case it's a vowel |
| s1 = s[0] |
| s2 = s[1:] |
| if s.endswith('ing'): |
| # strip off the 'ing' |
| s2 = s2[:-3] |
| # reduce all doubled characters to one |
| s2 = ''.join(c for c, _ in groupby(s2)) |
| # remove all vowels |
| s2 = s2.translate(None, "AEIOUaeiou") |
| # shorten remaining consonants to 4 characters |
| # and add the first char back to the front |
| s2 = s1 + s2[:4] |
| # update cache |
| abbreviate.abbreviations[s] = s2 |
| return s2 |
| # maps key to its abbreviated form |
| abbreviate.abbreviations = {} |