| # Status: being ported by Vladimir Prus |
| # Essentially ported, minor fixme remain. |
| # Base revision: 40480 |
| # |
| # Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and |
| # distribute this software is granted provided this copyright notice appears in |
| # all copies. This software is provided "as is" without express or implied |
| # warranty, and with no claim as to its suitability for any purpose. |
| |
| # Implements virtual targets, which correspond to actual files created during |
| # build, but are not yet targets in Jam sense. They are needed, for example, |
| # when searching for possible transormation sequences, when it's not known |
| # if particular target should be created at all. |
| # |
| # |
| # +--------------------------+ |
| # | VirtualTarget | |
| # +==========================+ |
| # | actualize | |
| # +--------------------------+ |
| # | actualize_action() = 0 | |
| # | actualize_location() = 0 | |
| # +----------------+---------+ |
| # | |
| # ^ |
| # / \ |
| # +-+-+ |
| # | |
| # +---------------------+ +-------+--------------+ |
| # | Action | | AbstractFileTarget | |
| # +=====================| * +======================+ |
| # | action_name | +--+ action | |
| # | properties | | +----------------------+ |
| # +---------------------+--+ | actualize_action() | |
| # | actualize() |0..1 +-----------+----------+ |
| # | path() | | |
| # | adjust_properties() | sources | |
| # | actualize_sources() | targets | |
| # +------+--------------+ ^ |
| # | / \ |
| # ^ +-+-+ |
| # / \ | |
| # +-+-+ +-------------+-------------+ |
| # | | | |
| # | +------+---------------+ +--------+-------------+ |
| # | | FileTarget | | SearchedLibTarget | |
| # | +======================+ +======================+ |
| # | | actualize-location() | | actualize-location() | |
| # | +----------------------+ +----------------------+ |
| # | |
| # +-+------------------------------+ |
| # | | |
| # +----+----------------+ +---------+-----------+ |
| # | CompileAction | | LinkAction | |
| # +=====================+ +=====================+ |
| # | adjust_properties() | | adjust_properties() | |
| # +---------------------+ | actualize_sources() | |
| # +---------------------+ |
| # |
| # The 'CompileAction' and 'LinkAction' classes are defined not here, |
| # but in builtin.jam modules. They are shown in the diagram to give |
| # the big picture. |
| |
| import re |
| import os.path |
| import string |
| |
| from b2.util import path, utility, set |
| 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 |
| from b2.exceptions import * |
| import b2.build.type |
| import type |
| |
| __re_starts_with_at = re.compile ('^@(.*)') |
| |
| class VirtualTargetRegistry: |
| def __init__ (self, manager): |
| self.manager_ = manager |
| |
| # A cache for FileTargets |
| self.files_ = {} |
| |
| # A cache for targets. |
| self.cache_ = {} |
| |
| # A map of actual names to virtual targets. |
| # Used to make sure we don't associate same |
| # actual target to two virtual targets. |
| self.actual_ = {} |
| |
| self.recent_targets_ = [] |
| |
| # All targets ever registed |
| self.all_targets_ = [] |
| |
| self.next_id_ = 0 |
| |
| def register (self, target): |
| """ Registers a new virtual target. Checks if there's already registered target, with the same |
| name, type, project and subvariant properties, and also with the same sources |
| and equal action. If such target is found it is retured and 'target' is not registered. |
| Otherwise, 'target' is registered and returned. |
| """ |
| signature = target.path() + "-" + target.name() |
| |
| result = None |
| if not self.cache_.has_key (signature): |
| self.cache_ [signature] = [] |
| |
| for t in self.cache_ [signature]: |
| a1 = t.action () |
| a2 = target.action () |
| |
| # TODO: why are we checking for not result? |
| if not result: |
| if not a1 and not a2: |
| result = t |
| else: |
| if a1 and a2 and a1.action_name () == a2.action_name () and a1.sources () == a2.sources (): |
| ps1 = a1.properties () |
| ps2 = a2.properties () |
| p1 = ps1.base () + ps1.free () + ps1.dependency () |
| p2 = ps2.base () + ps2.free () + ps2.dependency () |
| if p1 == p2: |
| result = t |
| |
| if not result: |
| self.cache_ [signature].append (target) |
| result = target |
| |
| # TODO: Don't append if we found pre-existing target? |
| self.recent_targets_.append(result) |
| self.all_targets_.append(result) |
| |
| result.set_id(self.next_id_) |
| self.next_id_ = self.next_id_+1 |
| |
| return result |
| |
| def from_file (self, file, file_location, project): |
| """ Creates a virtual target with appropriate name and type from 'file'. |
| If a target with that name in that project was already created, returns that already |
| created target. |
| TODO: more correct way would be to compute path to the file, based on name and source location |
| 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. |
| """ |
| # 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) |
| |
| if self.files_.has_key (path): |
| return self.files_ [path] |
| |
| file_type = type.type (file) |
| |
| result = FileTarget (file, False, file_type, project, |
| None, file_location) |
| self.files_ [path] = result |
| |
| result.set_id(self.next_id_) |
| self.next_id_ = self.next_id_+1 |
| |
| return result |
| |
| def recent_targets(self): |
| """Each target returned by 'register' is added to a list of |
| 'recent-target', returned by this function. So, this allows |
| us to find all targets created when building a given main |
| target, even if the target.""" |
| |
| return self.recent_targets_ |
| |
| def clear_recent_targets(self): |
| self.recent_targets_ = [] |
| |
| def all_targets(self): |
| # Returns all virtual targets ever created |
| return self.all_targets_ |
| |
| # Returns all targets from 'targets' with types |
| # equal to 'type' or derived from it. |
| def select_by_type(self, type, targets): |
| return [t for t in targets if type.is_sybtype(t.type(), type)] |
| |
| def register_actual_name (self, actual_name, virtual_target): |
| if self.actual_.has_key (actual_name): |
| cs1 = self.actual_ [actual_name].creating_subvariant () |
| cs2 = virtual_target.creating_subvariant () |
| cmt1 = cs1.main_target () |
| cmt2 = cs2.main_target () |
| |
| action1 = self.actual_ [actual_name].action () |
| action2 = virtual_target.action () |
| |
| properties_added = [] |
| properties_removed = [] |
| if action1 and action2: |
| p1 = action1.properties () |
| p1 = p1.raw () |
| p2 = action2.properties () |
| p2 = p2.raw () |
| |
| properties_removed = set.difference (p1, p2) |
| if not properties_removed: properties_removed = "none" |
| |
| properties_added = set.difference (p2, p1) |
| if not properties_added: properties_added = "none" |
| |
| # FIXME: Revive printing of real location. |
| raise BaseException ("Duplicate name of actual target: '%s'\n" |
| "previous virtual target '%s'\n" |
| "created from '%s'\n" |
| "another virtual target '%s'\n" |
| "created from '%s'\n" |
| "added properties: '%s'\n" |
| "removed properties: '%s'\n" % (actual_name, |
| self.actual_ [actual_name], "loc", #cmt1.location (), |
| virtual_target, |
| "loc", #cmt2.location (), |
| properties_added, properties_removed)) |
| |
| else: |
| self.actual_ [actual_name] = virtual_target |
| |
| |
| def add_suffix (self, specified_name, file_type, prop_set): |
| """ Appends the suffix appropriate to 'type/property_set' combination |
| to the specified name and returns the result. |
| """ |
| suffix = type.generated_target_suffix (file_type, prop_set) |
| |
| if suffix: |
| return specified_name + '.' + suffix |
| |
| else: |
| return specified_name |
| |
| class VirtualTarget: |
| """ Potential target. It can be converted into jam target and used in |
| building, if needed. However, it can be also dropped, which allows |
| to search for different transformation and select only one. |
| name: name of this target. |
| project: project to which this target belongs. |
| """ |
| def __init__ (self, name, project): |
| self.name_ = name |
| self.project_ = project |
| self.dependencies_ = [] |
| |
| # Caches if dapendencies for scanners have already been set. |
| self.made_ = {} |
| |
| def manager(self): |
| return self.project_.manager() |
| |
| def virtual_targets(self): |
| return self.manager().virtual_targets() |
| |
| def name (self): |
| """ Name of this target. |
| """ |
| return self.name_ |
| |
| def project (self): |
| """ Project of this target. |
| """ |
| return self.project_ |
| |
| def set_id(self, id): |
| self.id_ = id |
| |
| def __hash__(self): |
| return self.id_ |
| |
| def __cmp__(self, other): |
| return self.id_ - other.id_ |
| |
| def depends (self, d): |
| """ Adds additional instances of 'VirtualTarget' that this |
| one depends on. |
| """ |
| self.dependencies_ = unique (self.dependencies_ + d).sort () |
| |
| def dependencies (self): |
| return self.dependencies_ |
| |
| def actualize (self, scanner = None): |
| """ Generates all the actual targets and sets up build actions for |
| this target. |
| |
| If 'scanner' is specified, creates an additional target |
| with the same location as actual target, which will depend on the |
| actual target and be associated with 'scanner'. That additional |
| target is returned. See the docs (#dependency_scanning) for rationale. |
| Target must correspond to a file if 'scanner' is specified. |
| |
| If scanner is not specified, then actual target is returned. |
| """ |
| actual_name = self.actualize_no_scanner () |
| |
| if not scanner: |
| return actual_name |
| |
| else: |
| # Add the scanner instance to the grist for name. |
| g = '-'.join ([ungrist(get_grist(actual_name)), str(id(scanner))]) |
| |
| name = replace_grist (actual_name, '<' + g + '>') |
| |
| if not self.made_.has_key (name): |
| self.made_ [name] = True |
| |
| self.project_.manager ().engine ().add_dependency (name, actual_name) |
| |
| self.actualize_location (name) |
| |
| self.project_.manager ().scanners ().install (scanner, name, str (self)) |
| |
| return name |
| |
| # private: (overridables) |
| |
| def actualize_action (self, target): |
| """ Sets up build actions for 'target'. Should call appropriate rules |
| and set target variables. |
| """ |
| raise BaseException ("method should be defined in derived classes") |
| |
| def actualize_location (self, target): |
| """ Sets up variables on 'target' which specify its location. |
| """ |
| raise BaseException ("method should be defined in derived classes") |
| |
| def path (self): |
| """ If the target is generated one, returns the path where it will be |
| generated. Otherwise, returns empty list. |
| """ |
| raise BaseException ("method should be defined in derived classes") |
| |
| def actual_name (self): |
| """ Return that actual target name that should be used |
| (for the case where no scanner is involved) |
| """ |
| raise BaseException ("method should be defined in derived classes") |
| |
| |
| class AbstractFileTarget (VirtualTarget): |
| """ Target which correspond to a file. The exact mapping for file |
| is not yet specified in this class. (TODO: Actually, the class name |
| could be better...) |
| |
| May be a source file (when no action is specified), or |
| derived file (otherwise). |
| |
| The target's grist is concatenation of project's location, |
| properties of action (for derived files), and, optionally, |
| value identifying the main target. |
| |
| exact: If non-empty, the name is exactly the name |
| created file should have. Otherwise, the '__init__' |
| method will add suffix obtained from 'type' by |
| calling 'type.generated-target-suffix'. |
| |
| type: optional type of this target. |
| """ |
| def __init__ (self, name, exact, type, project, action = None): |
| VirtualTarget.__init__ (self, name, project) |
| |
| self.type_ = type |
| |
| self.action_ = action |
| self.exact_ = exact |
| |
| if action: |
| action.add_targets ([self]) |
| |
| if self.type and not exact: |
| self.__adjust_name (name) |
| |
| |
| self.actual_name_ = None |
| self.path_ = None |
| self.intermediate_ = False |
| self.creating_subvariant_ = None |
| |
| # True if this is a root target. |
| self.root_ = False |
| |
| def type (self): |
| return self.type_ |
| |
| def set_path (self, path): |
| """ Sets the path. When generating target name, it will override any path |
| computation from properties. |
| """ |
| self.path_ = path |
| |
| def action (self): |
| """ Returns the action. |
| """ |
| return self.action_ |
| |
| def root (self, set = None): |
| """ Sets/gets the 'root' flag. Target is root is it directly correspods to some |
| variant of a main target. |
| """ |
| if set: |
| self.root_ = True |
| return self.root_ |
| |
| def creating_subvariant (self, s = None): |
| """ Gets or sets the subvariant which created this target. Subvariant |
| is set when target is brought into existance, and is never changed |
| after that. In particual, if target is shared by subvariant, only |
| the first is stored. |
| s: If specified, specified the value to set, |
| which should be instance of 'subvariant' class. |
| """ |
| if s and not self.creating_subvariant (): |
| if self.creating_subvariant (): |
| raise BaseException ("Attempt to change 'dg'") |
| |
| else: |
| self.creating_subvariant_ = s |
| |
| return self.creating_subvariant_ |
| |
| def actualize_action (self, target): |
| if self.action_: |
| self.action_.actualize () |
| |
| # Return a human-readable representation of this target |
| # |
| # If this target has an action, that's: |
| # |
| # { <action-name>-<self.name>.<self.type> <action-sources>... } |
| # |
| # otherwise, it's: |
| # |
| # { <self.name>.<self.type> } |
| # |
| def str(self): |
| a = self.action() |
| |
| name_dot_type = self.name_ + "." + self.type_ |
| |
| if a: |
| action_name = a.action_name() |
| ss = [ s.str() for s in a.sources()] |
| |
| return "{ %s-%s %s}" % (action_name, name_dot_type, str(ss)) |
| else: |
| return "{ " + name_dot_type + " }" |
| |
| # private: |
| |
| def actual_name (self): |
| if not self.actual_name_: |
| self.actual_name_ = '<' + self.grist() + '>' + self.name_ |
| |
| return self.actual_name_ |
| |
| def grist (self): |
| """Helper to 'actual_name', above. Compute unique prefix used to distinguish |
| this target from other targets with the same name which create different |
| file. |
| """ |
| # Depending on target, there may be different approaches to generating |
| # unique prefixes. We'll generate prefixes in the form |
| # <one letter approach code> <the actual prefix> |
| path = self.path () |
| |
| if path: |
| # The target will be generated to a known path. Just use the path |
| # for identification, since path is as unique as it can get. |
| return 'p' + path |
| |
| else: |
| # File is either source, which will be searched for, or is not a file at |
| # all. Use the location of project for distinguishing. |
| project_location = self.project_.get ('location') |
| path_components = b2.util.path.split(project_location) |
| location_grist = '!'.join (path_components) |
| |
| if self.action_: |
| ps = self.action_.properties () |
| property_grist = ps.as_path () |
| # 'property_grist' can be empty when 'ps' is an empty |
| # property set. |
| if property_grist: |
| location_grist = location_grist + '/' + property_grist |
| |
| return 'l' + location_grist |
| |
| def __adjust_name(self, specified_name): |
| """Given the target name specified in constructor, returns the |
| name which should be really used, by looking at the <tag> properties. |
| The tag properties come in two flavour: |
| - <tag>value, |
| - <tag>@rule-name |
| In the first case, value is just added to name |
| In the second case, the specified rule is called with specified name, |
| target type and properties and should return the new name. |
| If not <tag> property is specified, or the rule specified by |
| <tag> returns nothing, returns the result of calling |
| virtual-target.add-suffix""" |
| |
| if self.action_: |
| ps = self.action_.properties() |
| else: |
| ps = property_set.empty() |
| |
| # FIXME: I'm not sure how this is used, need to check with |
| # Rene to figure out how to implement |
| #~ We add ourselves to the properties so that any tag rule can get |
| #~ more direct information about the target than just that available |
| #~ through the properties. This is useful in implementing |
| #~ name changes based on the sources of the target. For example to |
| #~ make unique names of object files based on the source file. |
| #~ --grafik |
| #ps = property_set.create(ps.raw() + ["<target>%s" % "XXXX"]) |
| #ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ; |
| |
| tag = ps.get("<tag>") |
| |
| if tag: |
| |
| rule_names = [t[:1] for t in tag if t[0] == '@'] |
| if rule_names: |
| if len(tag) > 1: |
| self.manager_.errors()( |
| """<tag>@rulename is present but is not the only <tag> feature""") |
| |
| self.name_ = bjam.call(rule_names[0], specified_name, self.type_, ps) |
| else: |
| self.manager_.errors()( |
| """The value of the <tag> feature must be '@rule-nane'""") |
| |
| # If there's no tag or the tag rule returned nothing. |
| if not tag or not self.name_: |
| self.name_ = add_prefix_and_suffix(specified_name, self.type_, ps) |
| |
| def actualize_no_scanner(self): |
| name = self.actual_name() |
| |
| # Do anything only on the first invocation |
| if not self.made_: |
| self.made_[name] = True |
| |
| if self.action_: |
| # For non-derived target, we don't care if there |
| # are several virtual targets that refer to the same name. |
| # One case when this is unavoidable is when file name is |
| # main.cpp and two targets have types CPP (for compiling) |
| # and MOCCABLE_CPP (for convertion to H via Qt tools). |
| self.virtual_targets().register_actual_name(name, self) |
| |
| for i in self.dependencies_: |
| self.manager_.engine().add_dependency(name, i.actualize()) |
| |
| self.actualize_location(name) |
| self.actualize_action(name) |
| |
| return name |
| |
| def add_prefix_and_suffix(specified_name, type, property_set): |
| """Appends the suffix appropriate to 'type/property-set' combination |
| to the specified name and returns the result.""" |
| |
| suffix = b2.build.type.generated_target_suffix(type, property_set) |
| |
| # Handle suffixes for which no leading dot is desired. Those are |
| # specified by enclosing them in <...>. Needed by python so it |
| # can create "_d.so" extensions, for example. |
| if get_grist(suffix): |
| suffix = ungrist(suffix) |
| elif suffix: |
| suffix = "." + suffix |
| |
| prefix = b2.build.type.generated_target_prefix(type, property_set) |
| |
| if specified_name.startswith(prefix): |
| prefix = "" |
| |
| if not prefix: |
| prefix = "" |
| if not suffix: |
| suffix = "" |
| return prefix + specified_name + suffix |
| |
| |
| class FileTarget (AbstractFileTarget): |
| """ File target with explicitly known location. |
| |
| The file path is determined as |
| - value passed to the 'set_path' method, if any |
| - for derived files, project's build dir, joined with components |
| that describe action's properties. If the free properties |
| are not equal to the project's reference properties |
| an element with name of main target is added. |
| - for source files, project's source dir |
| |
| The file suffix is |
| - the value passed to the 'suffix' method, if any, or |
| - the suffix which correspond to the target's type. |
| """ |
| def __init__ (self, name, exact, type, project, action = None, path=None): |
| AbstractFileTarget.__init__ (self, name, exact, type, project, action) |
| |
| self.path_ = path |
| |
| def clone_with_different_type(self, new_type): |
| return FileTarget(self.name_, 1, new_type, self.project_, |
| self.action_, self.path_) |
| |
| def actualize_location (self, target): |
| engine = self.project_.manager_.engine () |
| |
| if self.action_: |
| # This is a derived file. |
| path = self.path () |
| engine.set_target_variable (target, 'LOCATE', path) |
| |
| # Make sure the path exists. |
| engine.add_dependency (target, path) |
| common.mkdir(engine, path) |
| |
| # It's possible that the target name includes a directory |
| # too, for example when installing headers. Create that |
| # directory. |
| d = os.path.dirname(get_value(target)) |
| if d: |
| d = os.path.join(path, d) |
| engine.add_dependency(target, d) |
| common.mkdir(engine, d) |
| |
| # For real file target, we create a fake target that |
| # depends on the real target. This allows to run |
| # |
| # bjam hello.o |
| # |
| # without trying to guess the name of the real target. |
| # Note the that target has no directory name, and a special |
| # grist <e>. |
| # |
| # First, that means that "bjam hello.o" will build all |
| # known hello.o targets. |
| # Second, the <e> grist makes sure this target won't be confused |
| # with other targets, for example, if we have subdir 'test' |
| # with target 'test' in it that includes 'test.o' file, |
| # then the target for directory will be just 'test' the target |
| # for test.o will be <ptest/bin/gcc/debug>test.o and the target |
| # we create below will be <e>test.o |
| engine.add_dependency("<e>%s" % get_value(target), target) |
| |
| else: |
| # This is a source file. |
| engine.set_target_variable (target, 'SEARCH', self.project_.get ('source-location')) |
| |
| |
| def path (self): |
| """ Returns the directory for this target. |
| """ |
| if not self.path_: |
| if self.action_: |
| p = self.action_.properties () |
| target_path = p.target_path () |
| |
| if target_path [1] == True: |
| # Indicates that the path is relative to |
| # build dir. |
| target_path = os.path.join (self.project_.build_dir (), target_path [0]) |
| |
| # Store the computed path, so that it's not recomputed |
| # any more |
| self.path_ = target_path |
| |
| return self.path_ |
| |
| |
| class NotFileTarget(AbstractFileTarget): |
| |
| def __init__(self, name, project): |
| AbstractFileTarget.__init__(name, project) |
| |
| def path(self): |
| """Returns nothing, to indicate that target path is not known.""" |
| return None |
| |
| def actualize_location(self, target): |
| bjam.call("NOTFILE", target) |
| bjam.call("ALWAYS", taget) |
| |
| |
| class Action: |
| """ Class which represents an action. |
| Both 'targets' and 'sources' should list instances of 'VirtualTarget'. |
| Action name should name a rule with this prototype |
| rule action_name ( targets + : sources * : properties * ) |
| Targets and sources are passed as actual jam targets. The rule may |
| not establish dependency relationship, but should do everything else. |
| """ |
| def __init__ (self, manager, sources, action_name, prop_set): |
| self.sources_ = sources |
| self.action_name_ = action_name |
| if not prop_set: |
| prop_set = property_set.empty() |
| self.properties_ = prop_set |
| |
| self.manager_ = manager |
| self.engine_ = self.manager_.engine () |
| self.targets_ = [] |
| |
| # Indicates whether this has been actualized or not. |
| self.actualized_ = False |
| |
| self.dependency_only_sources_ = [] |
| self.actual_sources_ = [] |
| |
| |
| def add_targets (self, targets): |
| self.targets_ += targets |
| |
| def targets (self): |
| return self.targets_ |
| |
| def sources (self): |
| return self.sources_ |
| |
| def action_name (self): |
| return self.action_name_ |
| |
| def properties (self): |
| return self.properties_ |
| |
| def actualize (self): |
| """ Generates actual build instructions. |
| """ |
| if self.actualized_: |
| return |
| |
| self.actualized_ = True |
| |
| ps = self.properties () |
| properties = self.adjust_properties (ps) |
| actual_targets = [] |
| |
| for i in self.targets (): |
| actual_targets.append (i.actualize ()) |
| |
| self.actualize_sources (self.sources (), properties) |
| |
| self.engine_.add_dependency (actual_targets, self.actual_sources_ + self.dependency_only_sources_) |
| |
| raw_properties = properties.raw () |
| |
| # FIXME: check the comment below. Was self.action_name_ [1] |
| # Action name can include additional argument to rule, which should not |
| # be passed to 'set-target-variables' |
| # FIXME: breaking circular dependency |
| import toolset |
| toolset.set_target_variables (self.manager_, self.action_name_, actual_targets, raw_properties) |
| |
| engine = self.manager_.engine () |
| |
| self.manager_.engine ().set_update_action (self.action_name_, actual_targets, self.actual_sources_, |
| properties) |
| |
| # Since we set up creating action here, we also set up |
| # action for cleaning up |
| self.manager_.engine ().set_update_action ('common.Clean', 'clean-all', |
| actual_targets, None) |
| |
| return actual_targets |
| |
| def actualize_source_type (self, sources, prop_set): |
| """ Helper for 'actualize_sources'. |
| For each passed source, actualizes it with the appropriate scanner. |
| Returns the actualized virtual targets. |
| """ |
| result = [] |
| for i in sources: |
| scanner = None |
| |
| # FIXME: what's this? |
| # if isinstance (i, str): |
| # i = self.manager_.get_object (i) |
| |
| if i.type (): |
| scanner = type.get_scanner (i.type (), prop_set) |
| |
| result.append (i.actualize (scanner)) |
| |
| return result |
| |
| def actualize_sources (self, sources, prop_set): |
| """ Creates actual jam targets for sources. Initializes two member |
| variables: |
| 'self.actual_sources_' -- sources which are passed to updating action |
| 'self.dependency_only_sources_' -- sources which are made dependencies, but |
| are not used otherwise. |
| |
| New values will be *appended* to the variables. They may be non-empty, |
| if caller wants it. |
| """ |
| dependencies = self.properties_.get ('<dependency>') |
| |
| self.dependency_only_sources_ += self.actualize_source_type (dependencies, prop_set) |
| self.actual_sources_ += self.actualize_source_type (sources, prop_set) |
| |
| # This is used to help bjam find dependencies in generated headers |
| # in other main targets. |
| # Say: |
| # |
| # make a.h : ....... ; |
| # exe hello : hello.cpp : <implicit-dependency>a.h ; |
| # |
| # However, for bjam to find the dependency the generated target must |
| # be actualized (i.e. have the jam target). In the above case, |
| # if we're building just hello ("bjam hello"), 'a.h' won't be |
| # actualized unless we do it here. |
| implicit = self.properties_.get("<implicit-dependency>") |
| for i in implicit: |
| i.actualize() |
| |
| def adjust_properties (self, prop_set): |
| """ Determines real properties when trying building with 'properties'. |
| This is last chance to fix properties, for example to adjust includes |
| to get generated headers correctly. Default implementation returns |
| its argument. |
| """ |
| return prop_set |
| |
| |
| class NullAction (Action): |
| """ Action class which does nothing --- it produces the targets with |
| specific properties out of nowhere. It's needed to distinguish virtual |
| targets with different properties that are known to exist, and have no |
| actions which create them. |
| """ |
| def __init__ (self, manager, prop_set): |
| Action.__init__ (self, manager, None, None, prop_set) |
| |
| def actualize (self): |
| if not self.actualized_: |
| self.actualized_ = True |
| |
| for i in self.targets (): |
| i.actualize () |
| |
| class NonScanningAction(Action): |
| """Class which acts exactly like 'action', except that the sources |
| are not scanned for dependencies.""" |
| |
| def __init__(self, sources, action_name, property_set): |
| #FIXME: should the manager parameter of Action.__init__ |
| #be removed? -- Steven Watanabe |
| Action.__init__(b2.manager.get_manager(), sources, action_name, property_set) |
| |
| def actualize_source_type(self, sources, property_set): |
| |
| return [x for source in sources for x in i.actualize()] |
| |
| def traverse (target, include_roots = False, include_sources = False): |
| """ Traverses the dependency graph of 'target' and return all targets that will |
| be created before this one is created. If root of some dependency graph is |
| 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. |
| """ |
| result = [] |
| |
| if target.action (): |
| action = target.action () |
| |
| # This includes 'target' as well |
| result += action.targets () |
| |
| for t in action.sources (): |
| |
| # FIXME: |
| # TODO: see comment in Manager.register_object () |
| #if not isinstance (t, VirtualTarget): |
| # t = target.project_.manager_.get_object (t) |
| |
| if not t.root (): |
| result += traverse (t, include_roots, include_sources) |
| |
| elif include_roots: |
| result.append (t) |
| |
| elif include_sources: |
| result.append (target) |
| |
| return result |
| |
| def clone_action (action, new_project, new_action_name, new_properties): |
| """Takes an 'action' instances and creates new instance of it |
| 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 not new_action_name: |
| new_action_name = action.action_name() |
| |
| if not new_properties: |
| new_properties = action.properties() |
| |
| closed_action = action.__class__(action.sources(), new_action_name, |
| new_properties) |
| |
| cloned_targets = [] |
| for target in action.targets(): |
| |
| n = target.name() |
| # Don't modify the name of the produced targets. Strip the directory f |
| cloned_target = FileTarget(n, 1, target.type(), new_project, |
| cloned_action) |
| |
| d = target.dependencies() |
| if d: |
| cloned_target.depends(d) |
| cloned_target.root(target.root()) |
| cloned_target.creating_subvariant(target.creating_subvariant()) |
| |
| cloned_targets.append(cloned_target) |
| |
| return cloned_action |
| |
| class Subvariant: |
| |
| def __init__ (self, main_target, prop_set, sources, build_properties, sources_usage_requirements, created_targets): |
| """ |
| main_target: The instance of MainTarget class |
| prop_set: Properties requested for this target |
| sources: |
| build_properties: Actually used properties |
| sources_usage_requirements: Properties propagated from sources |
| created_targets: Top-level created targets |
| """ |
| self.main_target_ = main_target |
| self.properties_ = prop_set |
| self.sources_ = sources |
| self.build_properties_ = build_properties |
| self.sources_usage_requirements_ = sources_usage_requirements |
| self.created_targets_ = created_targets |
| |
| self.usage_requirements_ = None |
| |
| # Pre-compose the list of other dependency graphs, on which this one |
| # depends |
| deps = build_properties.get ('<implicit-dependency>') |
| |
| self.other_dg_ = [] |
| for d in deps: |
| # FIXME: the property must have the actual object here, not a string. |
| value = replace_grist (d, '') |
| self.other_dg_.append (value.creating_subvariant ()) |
| |
| self.other_dg_ = unique (self.other_dg_) |
| |
| self.implicit_includes_cache_ = {} |
| self.target_directories_ = None |
| |
| def main_target (self): |
| return self.main_target_ |
| |
| def created_targets (self): |
| return self.created_targets_ |
| |
| def requested_properties (self): |
| return self.properties_ |
| |
| def build_properties (self): |
| return self.build_properties_ |
| |
| def sources_usage_requirements (self): |
| return self.sources_usage_requirements_ |
| |
| def set_usage_requirements (self, usage_requirements): |
| self.usage_requirements_ = usage_requirements |
| |
| def usage_requirements (self): |
| return self.usage_requirements_ |
| |
| def all_referenced_targets(self): |
| """Returns all targets referenced by this subvariant, |
| either directly or indirectly, and either as sources, |
| or as dependency properties. Targets referred with |
| dependency property are returned a properties, not targets.""" |
| |
| # Find directly referenced targets. |
| deps = self.build_properties().dependency() |
| all_targets = self.sources_ + deps |
| |
| # Find other subvariants. |
| r = [] |
| for t in all_targets: |
| r.append(t.creating_subvariant) |
| r = unique(r) |
| |
| for s in r: |
| if s != self: |
| all_targets.extend(s.all_referenced_targets()) |
| |
| return all_targets |
| |
| def implicit_includes (self, feature, target_type): |
| """ Returns the properties which specify implicit include paths to |
| generated headers. This traverses all targets in this subvariant, |
| and subvariants referred by <implcit-dependecy>properties. |
| For all targets which are of type 'target-type' (or for all targets, |
| if 'target_type' is not specified), the result will contain |
| <$(feature)>path-to-that-target. |
| """ |
| |
| if not target_type: |
| key = feature |
| else: |
| key = feature + "-" + target_type |
| |
| |
| result = self.implicit_includes_cache_.get(key) |
| if not result: |
| target_paths = self.all_target_directories(target_type) |
| target_paths = unique(target_paths) |
| result = ["<%s>%s" % (feature, p) for p in target_paths] |
| self.implicit_includes_cache_[key] = result |
| |
| return result |
| |
| def all_target_directories(self, target_type = None): |
| # TODO: does not appear to use target_type in deciding |
| # if we've computed this already. |
| if not self.target_directories_: |
| self.target_directories_ = self.compute_target_directories(target_type) |
| return self.target_directories_ |
| |
| def compute_target_directories(self, target_type=None): |
| result = [] |
| for t in self.created_targets(): |
| if not target_type or type.is_derived(t.type(), target_type): |
| result.append(t.path()) |
| |
| for d in self.other_dg_: |
| result.extend(d.all_target_directories(target_type)) |
| |
| result = unique(result) |
| return result |