From a4095787faa9359a1271f49f6f6f7630c2e3579b Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 2 Aug 2010 11:38:54 +0000 Subject: [PATCH] Complete porting of build/targets.jam [SVN r64537] --- src/build/generators.py | 23 +++++- src/build/targets.py | 165 ++++++++++++++++++++++++++-------------- src/util/sequence.py | 24 +++--- 3 files changed, 139 insertions(+), 73 deletions(-) diff --git a/src/build/generators.py b/src/build/generators.py index b1e62d2a9..6b6d36d0f 100644 --- a/src/build/generators.py +++ b/src/build/generators.py @@ -853,7 +853,7 @@ def find_viable_generators_aux (target_type, prop_set): m = g.match_rank(prop_set) if m: dout(" is viable") - viable_generators.append(g) + viable_generators.append(g) return viable_generators @@ -873,6 +873,8 @@ def find_viable_generators (target_type, prop_set): # TODO: is this really used? if not g in __active_generators: viable_generators.append (g) + else: + dout(" generator %s is active, discarning" % g.id()) # Generators which override 'all'. all_overrides = [] @@ -941,7 +943,7 @@ def __construct_really (project, name, target_type, prop_set, sources): return result; -def construct (project, name, target_type, prop_set, sources): +def construct (project, name, target_type, prop_set, sources, top_level=False): """ Attempts to create target of 'target-type' with 'properties' from 'sources'. The 'sources' are treated as a collection of *possible* ingridients -- i.e. it is not required to consume @@ -951,9 +953,19 @@ def construct (project, name, target_type, prop_set, sources): Returns a list of target. When this invocation is first instance of 'construct' in stack, returns only targets of requested 'target-type', otherwise, returns also unused sources and additionally generated - targets. + targets. + + If 'top-level' is set, does not suppress generators that are already + used in the stack. This may be useful in cases where a generator + has to build a metatargets -- for example a target corresponding to + built tool. """ - # TODO: Why is global needed here? + + global __active_generators + if top_level: + saved_active = __active_generators + __active_generators = [] + global __construct_stack if __construct_stack: __ensure_type (sources) @@ -976,5 +988,8 @@ def construct (project, name, target_type, prop_set, sources): __construct_stack = __construct_stack [1:] + if top_level: + __active_generators = saved_active + return result diff --git a/src/build/targets.py b/src/build/targets.py index 6be8ffec3..4cc3f0a98 100644 --- a/src/build/targets.py +++ b/src/build/targets.py @@ -1,7 +1,5 @@ -# Status: being ported by Vladimir Prus -# Still to do: call toolset.requirements when those are ported. -# Remember the location of target. -# Base revision: 40480 +# Status: ported. +# Base revision: 64488 # Copyright Vladimir Prus 2002-2007. # Copyright Rene Rivera 2006. @@ -104,6 +102,8 @@ class TargetRegistry: self.debug_building_ = "--debug-building" in bjam.variable("ARGV") + self.targets_ = [] + def main_target_alternative (self, target): """ Registers the specified target as a main target alternatives. Returns 'target'. @@ -232,6 +232,16 @@ class TargetRegistry: if self.debug_building_: print self.indent_ + message + def push_target(self, target): + self.targets_.append(target) + + def pop_target(self): + self.targets_ = self.targets_[:-1] + + def current(self): + return self.targets_[0] + + class GenerateResult: def __init__ (self, ur=None, targets=None): @@ -363,6 +373,9 @@ class ProjectTarget (AbstractTarget): # Targets marked as explicit. self.explicit_targets_ = set() + # Targets marked as always + self.always_targets_ = set() + # The constants defined for this project. self.constants_ = {} @@ -423,7 +436,7 @@ class ProjectTarget (AbstractTarget): # Collect all projects referenced via "projects-to-build" attribute. self_location = self.get ('location') for pn in self.get ('projects-to-build'): - result.append (self.find(pn)) + result.append (self.find(pn + "/")) return result @@ -434,6 +447,9 @@ class ProjectTarget (AbstractTarget): # Record the name of the target, not instance, since this # rule is called before main target instaces are created. self.explicit_targets_.add(target_name) + + def mark_target_as_always(self, target_name): + self.always_targets_.add(target_name) def add_alternative (self, target_instance): """ Add new target alternative. @@ -548,6 +564,9 @@ class ProjectTarget (AbstractTarget): if not self.main_target_.has_key (name): t = MainTarget (name, self.project_) self.main_target_ [name] = t + + if name in self.always_targets_: + a.always() self.main_target_ [name].add_alternative (a) @@ -561,7 +580,16 @@ class ProjectTarget (AbstractTarget): """ if path: - value = os.path.join(self.location_, value) + l = self.location_ + if not l: + # Project corresponding to config files do not have + # 'location' attribute, but do have source location. + # 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') + + value = os.path.join(l, value) # Now make the value absolute path value = os.path.join(os.getcwd(), value) @@ -660,46 +688,7 @@ class MainTarget (AbstractTarget): return best def apply_default_build (self, property_set): - # 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()) - - defaults_to_apply = [] - for d in self.default_build_.all(): - if not d.feature() in specified_features: - defaults_to_apply.append(d) - - # 2. If there's any defaults to be applied, form the new - # build request. Pass it throw 'expand-no-defaults', since - # default_build might contain "release debug", which will - # result in two property_sets. - result = [] - if defaults_to_apply: - - # We have to compress subproperties here to prevent - # property lists like: - # - # msvc 7.1 multi - # - # from being expanded into: - # - # 7.1/multi - # msvc/7.1/multi - # - # due to cross-product property combination. That may - # be an indication that - # build_request.expand-no-defaults is the wrong rule - # to use here. - compressed = feature.compress_subproperties(property_set.all()) - - result = build_request.expand_no_defaults( - b2.build.property_set.create([p]) for p in (compressed + defaults_to_apply)) - - else: - result.append (property_set) - - return result + return apply_default_build(property_set, self.default_build_) def generate (self, ps): """ Select an alternative for this main target, by finding all alternatives @@ -821,11 +810,16 @@ class BasicTarget (AbstractTarget): self.request_cache = {} self.user_context_ = self.manager_.errors().capture_user_context() + + self.always_ = False + + def always(self): + self.always_ = True def sources (self): """ Returns the list of AbstractTargets which are used as sources. The extra properties specified for sources are not represented. - The only used of this rule at the moment is the '--dump-test' + The only used of this rule at the moment is the '--dump-tests' feature of the test system. """ if self.source_targets_ == None: @@ -1084,6 +1078,8 @@ class BasicTarget (AbstractTarget): "Command line free features: '%s'" % str (cf.raw ())) self.manager().targets().log( "Target requirements: %s'" % str (self.requirements().raw ())) + + self.manager().targets().push_target(self) if not self.generated_.has_key(ps): @@ -1127,7 +1123,10 @@ class BasicTarget (AbstractTarget): # We might get duplicate sources, for example if # we link to two library which have the same in # usage requirements. - source_targets = unique (source_targets) + # Use stable sort, since for some targets the order is + # important. E.g. RUN_PY target need python source to come + # first. + source_targets = unique(source_targets, stable=True) # FIXME: figure why this call messes up source_targets in-place result = self.construct (self.name_, source_targets[:], rproperties) @@ -1137,6 +1136,10 @@ class BasicTarget (AbstractTarget): gur = result [0] result = result [1] + if self.always_: + for t in result: + t.always() + s = self.create_subvariant ( result, self.manager().virtual_targets().recent_targets(), ps, @@ -1155,17 +1158,25 @@ class BasicTarget (AbstractTarget): else: self.generated_[ps] = GenerateResult (property_set.empty(), []) else: - self.manager().targets().log( - "Skipping build: no in common properties") + # If we just see no, we cannot produce any reasonable + # diagnostics. The code that adds this property is expected + # to explain why a target is not built, for example using + # the configure.log-component-configuration function. - # We're here either because there's error computing - # properties, or there's no in properties. - # In the latter case we don't want any diagnostic. - # In the former case, we need diagnostics. TODOo - self.generated_[ps] = GenerateResult (rproperties, []) + # If this target fails to build, add no to properties + # to cause any parent target to fail to build. Except that it + # - does not work now, since we check for no only in + # common properties, but not in properties that came from + # dependencies + # - it's not clear if that's a good idea anyway. The alias + # target, for example, should not fail to build if a dependency + # fails. + self.generated_[ps] = GenerateResult( + property_set.create(["no"]), []) else: self.manager().targets().log ("Already built") + self.manager().targets().pop_target() self.manager().targets().decrease_indent() return self.generated_[ps] @@ -1273,7 +1284,7 @@ class TypedTarget (BasicTarget): r = generators.construct (self.project_, name, self.type_, prop_set.add_raw(['' + self.type_]), - source_targets) + source_targets, True) if not r: print "warning: Unable to construct '%s'" % self.full_name () @@ -1290,6 +1301,48 @@ class TypedTarget (BasicTarget): return r +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()) + + defaults_to_apply = [] + for d in default_build.all(): + if not d.feature() in specified_features: + defaults_to_apply.append(d) + + # 2. If there's any defaults to be applied, form the new + # build request. Pass it throw 'expand-no-defaults', since + # default_build might contain "release debug", which will + # result in two property_sets. + result = [] + if defaults_to_apply: + + # We have to compress subproperties here to prevent + # property lists like: + # + # msvc 7.1 multi + # + # from being expanded into: + # + # 7.1/multi + # msvc/7.1/multi + # + # due to cross-product property combination. That may + # be an indication that + # build_request.expand-no-defaults is the wrong rule + # to use here. + compressed = feature.compress_subproperties(property_set.all()) + + result = build_request.expand_no_defaults( + b2.build.property_set.create([p]) for p in (compressed + defaults_to_apply)) + + else: + result.append (property_set) + + return result + def create_typed_metatarget(name, type, sources, requirements, default_build, usage_requirements): diff --git a/src/util/sequence.py b/src/util/sequence.py index a0160107d..1d32efd2e 100644 --- a/src/util/sequence.py +++ b/src/util/sequence.py @@ -5,19 +5,17 @@ import operator -def unique (values): - # TODO: is this the most efficient way? - # consider using a set from Python 2.4. - return list(set(values)) -# cache = {} -# result = [] -# for v in values: -# if not cache.has_key(v): -# cache[v] = None -# result.append(v) -# return result - - +def unique (values, stable=False): + if stable: + s = set() + r = [] + for v in values: + if not v in s: + r.append(v) + s.add(v) + return r + else: + return list(set(values)) def max_element (elements, ordered = None): """ Returns the maximum number in 'elements'. Uses 'ordered' for comparisons,