From eb262a3d350bbb1c1c5cb2ea52c80523abd7505c Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 2 Aug 2010 14:15:50 +0000 Subject: [PATCH] Partially upgrade build/generators.py [SVN r64539] --- v2/build/generators.py | 134 ++++++++++++++++++++++++++++----- v2/build/type.py | 13 ++++ v2/test/generator_selection.py | 22 ++++++ v2/util/logger.py | 2 +- 4 files changed, 150 insertions(+), 21 deletions(-) diff --git a/v2/build/generators.py b/v2/build/generators.py index 6b6d36d0f..39696def3 100644 --- a/v2/build/generators.py +++ b/v2/build/generators.py @@ -1,5 +1,5 @@ # Status: being ported by Vladimir Prus -# Base revision: 41557 +# Base revision: 48649 # TODO: replace the logging with dout # Copyright Vladimir Prus 2002. @@ -59,6 +59,7 @@ from b2.util import set from b2.util.sequence import unique import b2.util.sequence as sequence from b2.manager import get_manager +import b2.build.type def reset (): """ Clear the module state. This is mainly for testing purposes. @@ -66,6 +67,7 @@ def reset (): global __generators, __type_to_generators, __generators_for_toolset, __construct_stack global __overrides, __active_generators global __viable_generators_cache, __viable_source_types_cache + global __vstg_cached_generators, __vst_cached_types __generators = {} __type_to_generators = {} @@ -78,6 +80,9 @@ def reset (): __viable_source_types_cache = {} __active_generators = [] + __vstg_cached_generators = [] + __vst_cached_types = [] + reset () _re_separate_types_prefix_and_postfix = re.compile ('([^\\(]*)(\\((.*)%(.*)\\))?') @@ -101,6 +106,54 @@ def decrease_indent(): global __indent __indent = __indent[0:-4] + +# Updated cached viable source target type information as needed after a new +# derived target type gets added. This is needed because if a target type is a +# viable source target type for some generator then all of the target type's +# derived target types are automatically viable as source target types for the +# same generator. Does nothing if a non-derived target type is passed to it. +# +def update_cached_information_with_a_new_type(type): + + base_type = b2.build.type.base(type) + + if base_type: + for g in __vstg_cached_generators: + if base_type in __viable_source_types_cache.get(g, []): + __viable_source_types_cache[g].append(type) + + for t in __vst_cached_types: + if base_type in __viable_source_types_cache.get(t, []): + __viable_source_types_cache[t].append(type) + +# Clears cached viable source target type information except for target types +# and generators with all source types listed as viable. Should be called when +# something invalidates those cached values by possibly causing some new source +# types to become viable. +# +def invalidate_extendable_viable_source_target_type_cache(): + + global __vstg_cached_generators + generators_with_cached_source_types = __vstg_cached_generators + __vstg_cached_generators = [] + + for g in generators_with_cached_source_types: + if __viable_source_types_cache.has_key(g): + if __viable_source_types_cache[g] == ["*"]: + __vstg_cached_generators.append(g) + else: + del __viable_source_types_cache[g] + + global __vst_cached_types + types_with_cached_sources_types = __vst_cached_types + __vst_cached_types = [] + for t in types_with_cached_sources_types: + if __viable_source_types_cache.has_key(t): + if __viable_source_types_cache[t] == ["*"]: + __vst_cached_types.append(t) + else: + del __viable_source_types_cache[t] + def dout(message): if debug(): print __indent + message @@ -589,6 +642,24 @@ def register (g): __generators_for_toolset.setdefault(base, []).append(g) + # After adding a new generator that can construct new target types, we need + # to clear the related cached viable source target type information for + # constructing a specific target type or using a specific generator. Cached + # viable source target type lists affected by this are those containing any + # of the target types constructed by the new generator or any of their base + # target types. + # + # A more advanced alternative to clearing that cached viable source target + # type information would be to expand it with additional source types or + # even better - mark it as needing to be expanded on next use. + # + # For now we just clear all the cached viable source target type information + # that does not simply state 'all types' and may implement a more detailed + # algorithm later on if it becomes needed. + + invalidate_extendable_viable_source_target_type_cache() + + def register_standard (id, source_types, target_types, requirements = []): """ Creates new instance of the 'generator' class and registers it. Returns the creates instance. @@ -632,11 +703,19 @@ def __viable_source_types_real (target_type): of calling itself recusrively on source types. """ generators = [] - - t = type.all_bases (target_type) + + # 't0' is the initial list of target types we need to process to get a list + # of their viable source target types. New target types will not be added to + # this list. + t0 = type.all_bases (target_type) + + + # 't' is the list of target types which have not yet been processed to get a + # list of their viable source target types. This list will get expanded as + # we locate more target types to process. + t = t0 result = [] - # 't' is the list of types which are not yet processed while t: # Find all generators for current type. # Unlike 'find_viable_generators' we don't care about prop_set. @@ -658,19 +737,29 @@ def __viable_source_types_real (target_type): all = type.all_derived (source_type) for n in all: if not n in result: - t.append (n) + + # Here there is no point in adding target types to + # the list of types to process in case they are or + # have already been on that list. We optimize this + # check by realizing that we only need to avoid the + # original target type's base types. Other target + # types that are or have been on the list of target + # types to process have been added to the 'result' + # list as well and have thus already been eliminated + # by the previous if. + if not n in t0: + t.append (n) result.append (n) - - result = unique (result) - + return result def viable_source_types (target_type): """ Helper rule, caches the result of '__viable_source_types_real'. """ - if not __viable_source_types_cache.has_key (target_type): - __viable_source_types_cache [target_type] = __viable_source_types_real (target_type) + 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) return __viable_source_types_cache [target_type] def viable_source_types_for_generator_real (generator): @@ -691,20 +780,22 @@ def viable_source_types_for_generator_real (generator): else: result = [] for s in source_types: - result += type.all_derived (s) + viable_source_types (s) - result = unique (result) - if "*" in result: - result = ["*"] - return result + viable_sources = viable_source_types(s) + if viable_sources == "*": + result = ["*"] + break + else: + result.extend(type.all_derived(s) + viable_sources) + return unique(result) def viable_source_types_for_generator (generator): """ Caches the result of 'viable_source_types_for_generator'. """ - key = str (generator) - if not __viable_source_types_cache.has_key (key): - __viable_source_types_cache [key] = viable_source_types_for_generator_real (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) - return __viable_source_types_cache [key] + return __viable_source_types_cache[generator] def try_one_generator_really (project, name, generator, target_type, properties, sources): """ Returns usage requirements + list of created targets. @@ -861,6 +952,8 @@ def find_viable_generators (target_type, prop_set): key = target_type + '.' + str (prop_set) l = __viable_generators_cache.get (key, None) + if not l: + l = [] if not l: l = find_viable_generators_aux (target_type, prop_set) @@ -899,6 +992,7 @@ def find_viable_generators (target_type, prop_set): for g in viable_generators: if not g.id () in overriden_ids: result.append (g) + return result @@ -909,7 +1003,7 @@ def __construct_really (project, name, target_type, prop_set, sources): viable_generators = find_viable_generators (target_type, prop_set) result = [] - + project.manager ().logger ().log (__name__, "*** %d viable generators" % len (viable_generators)) generators_that_succeeded = [] diff --git a/v2/build/type.py b/v2/build/type.py index 74d709a02..b04547458 100644 --- a/v2/build/type.py +++ b/v2/build/type.py @@ -16,6 +16,7 @@ from b2.exceptions import * from b2.build import feature, property, scanner from b2.util import bjam_signature + __re_hyphen = re.compile ('-') def __register_features (): @@ -98,6 +99,12 @@ def register (type, suffixes = [], base_type = None): feature.compose ('' + type, replace_grist (base_type, '')) feature.compose ('' + type, '' + base_type) + import b2.build.generators as generators + # Adding a new derived type affects generator selection so we need to + # make the generator selection module update any of its cached + # information related to a new derived type being defined. + generators.update_cached_information_with_a_new_type(type) + # FIXME: resolving recursive dependency. from b2.manager import get_manager get_manager().projects().project_rules().add_rule_for_type(type) @@ -142,6 +149,12 @@ def get_scanner (type, prop_set): return None +def base(type): + """Returns a base type for the given type or nothing in case the given type is + not derived.""" + + return __types[type]['base'] + def all_bases (type): """ Returns type and all of its bases, in the order of their distance from type. """ diff --git a/v2/test/generator_selection.py b/v2/test/generator_selection.py index 9616bd5bc..e10cb0d3b 100755 --- a/v2/test/generator_selection.py +++ b/v2/test/generator_selection.py @@ -51,6 +51,28 @@ else } """) + t.write("Other/mygen.py", """ +import b2.build.generators as generators +import b2.build.type as type + +from b2.manager import get_manager + +import os + + +type.register('MY_TYPE', ['extension']) +generators.register_standard('mygen.generate-a-cpp-file', ['MY_TYPE'], ['CPP']) +if os.name == 'nt': + action = 'echo void g() {} > "$(<)"' +else: + action = 'echo "void g() {}" > "$(<)"' +def f(*args): + print "Generating a CPP file..." + +get_manager().engine().register_action("mygen.generate-a-cpp-file", + action, function=f) +""") + t.write("Other/jamfile.jam", """ import mygen ; obj other-obj : source.extension ; diff --git a/v2/util/logger.py b/v2/util/logger.py index a0d7a6e37..de6521290 100644 --- a/v2/util/logger.py +++ b/v2/util/logger.py @@ -30,7 +30,7 @@ class NullLogger: return False def on (self): - return False + return True class TextLogger (NullLogger): def __init__ (self):