summaryrefslogtreecommitdiff
path: root/jam-files/boost-build/build/targets.py
diff options
context:
space:
mode:
Diffstat (limited to 'jam-files/boost-build/build/targets.py')
-rw-r--r--jam-files/boost-build/build/targets.py1401
1 files changed, 0 insertions, 1401 deletions
diff --git a/jam-files/boost-build/build/targets.py b/jam-files/boost-build/build/targets.py
deleted file mode 100644
index a35612ce..00000000
--- a/jam-files/boost-build/build/targets.py
+++ /dev/null
@@ -1,1401 +0,0 @@
-# Status: ported.
-# Base revision: 64488
-
-# Copyright Vladimir Prus 2002-2007.
-# Copyright Rene Rivera 2006.
-#
-# Distributed under the Boost Software License, Version 1.0.
-# (See accompanying file LICENSE_1_0.txt or copy at
-# http://www.boost.org/LICENSE_1_0.txt)
-
-# Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
-#
-# Abstract targets are represented by classes derived from 'AbstractTarget' class.
-# The first abstract target is 'project_target', which is created for each
-# Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
-# (see project.jam).
-#
-# Project targets keep a list of 'MainTarget' instances.
-# A main target is what the user explicitly defines in a Jamfile. It is
-# possible to have several definitions for a main target, for example to have
-# different lists of sources for different platforms. So, main targets
-# keep a list of alternatives.
-#
-# Each alternative is an instance of 'AbstractTarget'. When a main target
-# subvariant is defined by some rule, that rule will decide what class to
-# use, create an instance of that class and add it to the list of alternatives
-# for the main target.
-#
-# Rules supplied by the build system will use only targets derived
-# from 'BasicTarget' class, which will provide some default behaviour.
-# There will be two classes derived from it, 'make-target', created by the
-# 'make' rule, and 'TypedTarget', created by rules such as 'exe' and 'dll'.
-
-#
-# +------------------------+
-# |AbstractTarget |
-# +========================+
-# |name |
-# |project |
-# | |
-# |generate(properties) = 0|
-# +-----------+------------+
-# |
-# ^
-# / \
-# +-+-+
-# |
-# |
-# +------------------------+------+------------------------------+
-# | | |
-# | | |
-# +----------+-----------+ +------+------+ +------+-------+
-# | project_target | | MainTarget | | BasicTarget |
-# +======================+ 1 * +=============+ alternatives +==============+
-# | generate(properties) |o-----------+ generate |<>------------->| generate |
-# | main-target | +-------------+ | construct = 0|
-# +----------------------+ +--------------+
-# |
-# ^
-# / \
-# +-+-+
-# |
-# |
-# ...--+----------------+------------------+----------------+---+
-# | | | |
-# | | | |
-# ... ---+-----+ +------+-------+ +------+------+ +--------+-----+
-# | | TypedTarget | | make-target | | stage-target |
-# . +==============+ +=============+ +==============+
-# . | construct | | construct | | construct |
-# +--------------+ +-------------+ +--------------+
-
-import re
-import os.path
-import sys
-
-from b2.manager import get_manager
-
-from b2.util.utility import *
-import property, project, virtual_target, property_set, feature, generators, toolset
-from virtual_target import Subvariant
-from b2.exceptions import *
-from b2.util.sequence import unique
-from b2.util import path, bjam_signature
-from b2.build.errors import user_error_checkpoint
-
-import b2.build.build_request as build_request
-
-import b2.util.set
-_re_separate_target_from_properties = re.compile (r'^([^<]*)(/(<.*))?$')
-
-class TargetRegistry:
-
- def __init__ (self):
- # All targets that are currently being built.
- # Only the key is id (target), the value is the actual object.
- self.targets_being_built_ = {}
-
- # Current indent for debugging messages
- self.indent_ = ""
-
- 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'.
- """
- target.project ().add_alternative (target)
- return target
-
- def main_target_sources (self, sources, main_target_name, no_renaming=0):
- """Return the list of sources to use, if main target rule is invoked
- with 'sources'. If there are any objects in 'sources', they are treated
- 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."""
- result = []
-
- for t in sources:
-
- t = b2.util.jam_to_value_maybe(t)
-
- if isinstance (t, AbstractTarget):
- name = t.name ()
-
- if not no_renaming:
- name = main_target_name + '__' + name
- t.rename (name)
-
- # Inline targets are not built by default.
- p = t.project()
- p.mark_targets_as_explicit([name])
- result.append(name)
-
- else:
- result.append (t)
-
- return result
-
-
- def main_target_requirements(self, specification, project):
- """Returns the requirement to use when declaring a main target,
- which are obtained by
- - translating all specified property paths, and
- - refining project requirements with the one specified for the target
-
- 'specification' are the properties xplicitly specified for a
- main target
- 'project' is the project where the main taret is to be declared."""
-
- specification.extend(toolset.requirements())
-
- requirements = property_set.refine_from_user_input(
- project.get("requirements"), specification,
- project.project_module(), project.get("location"))
-
- return requirements
-
- def main_target_usage_requirements (self, specification, project):
- """ Returns the use requirement to use when declaraing a main target,
- which are obtained by
- - translating all specified property paths, and
- - adding project's usage requirements
- specification: Use-properties explicitly specified for a main target
- project: Project where the main target is to be declared
- """
- project_usage_requirements = project.get ('usage-requirements')
-
- # We don't use 'refine-from-user-input' because I'm not sure if:
- # - removing of parent's usage requirements makes sense
- # - refining of usage requirements is not needed, since usage requirements
- # are always free.
- usage_requirements = property_set.create_from_user_input(
- specification, project.project_module(), project.get("location"))
-
- return project_usage_requirements.add (usage_requirements)
-
- def main_target_default_build (self, specification, project):
- """ Return the default build value to use when declaring a main target,
- which is obtained by using specified value if not empty and parent's
- default build attribute otherwise.
- specification: Default build explicitly specified for a main target
- project: Project where the main target is to be declared
- """
- if specification:
- return property_set.create_with_validation(specification)
- else:
- return project.get ('default-build')
-
- def start_building (self, main_target_instance):
- """ Helper rules to detect cycles in main target references.
- """
- if self.targets_being_built_.has_key(id(main_target_instance)):
- names = []
- for t in self.targets_being_built_.values() + [main_target_instance]:
- names.append (t.full_name())
-
- get_manager().errors()("Recursion in main target references\n")
-
- self.targets_being_built_[id(main_target_instance)] = main_target_instance
-
- def end_building (self, main_target_instance):
- assert (self.targets_being_built_.has_key (id (main_target_instance)))
- del self.targets_being_built_ [id (main_target_instance)]
-
- def create_typed_target (self, type, project, name, sources, requirements, default_build, usage_requirements):
- """ Creates a TypedTarget with the specified properties.
- The 'name', 'sources', 'requirements', 'default_build' and
- 'usage_requirements' are assumed to be in the form specified
- by the user in Jamfile corresponding to 'project'.
- """
- return self.main_target_alternative (TypedTarget (name, project, type,
- self.main_target_sources (sources, name),
- self.main_target_requirements (requirements, project),
- self.main_target_default_build (default_build, project),
- self.main_target_usage_requirements (usage_requirements, project)))
-
- def increase_indent(self):
- self.indent_ += " "
-
- def decrease_indent(self):
- self.indent_ = self.indent_[0:-4]
-
- def logging(self):
- return self.debug_building_
-
- def log(self, message):
- 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):
- if not targets:
- targets = []
-
- 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 ()
-
- def usage_requirements (self):
- return self.__usage_requirements
-
- def targets (self):
- return self.__targets
-
- def extend (self, other):
- assert (isinstance (other, GenerateResult))
-
- self.__usage_requirements = self.__usage_requirements.add (other.usage_requirements ())
- self.__targets.extend (other.targets ())
-
-class AbstractTarget:
- """ Base class for all abstract targets.
- """
- def __init__ (self, name, project, manager = None):
- """ manager: the Manager object
- name: name of the target
- project: the project target to which this one belongs
- manager:the manager object. If none, uses project.manager ()
- """
- 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
- # messages which should name problematic targets. Another is setting correct
- # paths for sources and generated files.
-
- # Why allow manager to be specified? Because otherwise project target could not derive
- # from this class.
- if manager:
- self.manager_ = manager
- else:
- self.manager_ = project.manager ()
-
- self.name_ = name
- self.project_ = project
-
- def manager (self):
- return self.manager_
-
- def name (self):
- """ Returns the name of this target.
- """
- return self.name_
-
- def project (self):
- """ Returns the project for this target.
- """
- return self.project_
-
- def location (self):
- """ Return the location where the target was declared.
- """
- return self.location_
-
- def full_name (self):
- """ Returns a user-readable name for this target.
- """
- location = self.project ().get ('location')
- return location + '/' + self.name_
-
- def generate (self, property_set):
- """ Takes a property set. Generates virtual targets for this abstract
- target, using the specified properties, unless a different value of some
- feature is required by the target.
- On success, returns a GenerateResult instance with:
- - a property_set with the usage requirements to be
- applied to dependents
- - a list of produced virtual targets, which may be
- empty.
- If 'property_set' is empty, performs default build of this
- target, in a way specific to derived class.
- """
- raise BaseException ("method should be defined in derived classes")
-
- def rename (self, new_name):
- self.name_ = new_name
-
-class ProjectTarget (AbstractTarget):
- """ Project target class (derived from 'AbstractTarget')
-
- This class these responsibilities:
- - maintaining a list of main target in this project and
- building it
-
- Main targets are constructed in two stages:
- - When Jamfile is read, a number of calls to 'add_alternative' is made.
- At that time, alternatives can also be renamed to account for inline
- targets.
- - The first time 'main-target' or 'has-main-target' rule is called,
- all alternatives are enumerated an main targets are created.
- """
- def __init__ (self, manager, name, project_module, parent_project, requirements, default_build):
- AbstractTarget.__init__ (self, name, self, manager)
-
- self.project_module_ = project_module
- self.location_ = manager.projects().attribute (project_module, 'location')
- self.requirements_ = requirements
- self.default_build_ = default_build
-
- self.build_dir_ = None
-
- # A cache of IDs
- self.ids_cache_ = {}
-
- # True is main targets have already been built.
- self.built_main_targets_ = False
-
- # A list of the registered alternatives for this project.
- self.alternatives_ = []
-
- # A map from main target name to the target corresponding
- # to it.
- self.main_target_ = {}
-
- # Targets marked as explicit.
- self.explicit_targets_ = set()
-
- # Targets marked as always
- self.always_targets_ = set()
-
- # The constants defined for this project.
- self.constants_ = {}
-
- # Whether targets for all main target are already created.
- self.built_main_targets_ = 0
-
- if parent_project:
- self.inherit (parent_project)
-
-
- # TODO: This is needed only by the 'make' rule. Need to find the
- # way to make 'make' work without this method.
- def project_module (self):
- return self.project_module_
-
- def get (self, attribute):
- return self.manager().projects().attribute(
- self.project_module_, attribute)
-
- def build_dir (self):
- if not self.build_dir_:
- self.build_dir_ = self.get ('build-dir')
- if not self.build_dir_:
- self.build_dir_ = os.path.join(self.project_.get ('location'), 'bin')
-
- return self.build_dir_
-
- def generate (self, ps):
- """ Generates all possible targets contained in this project.
- """
- self.manager_.targets().log(
- "Building project '%s' with '%s'" % (self.name (), str(ps)))
- self.manager_.targets().increase_indent ()
-
- result = GenerateResult ()
-
- for t in self.targets_to_build ():
- g = t.generate (ps)
- result.extend (g)
-
- self.manager_.targets().decrease_indent ()
- return result
-
- def targets_to_build (self):
- """ Computes and returns a list of AbstractTarget instances which
- must be built when this project is built.
- """
- result = []
-
- if not self.built_main_targets_:
- self.build_main_targets ()
-
- # Collect all main targets here, except for "explicit" ones.
- for n, t in self.main_target_.iteritems ():
- if not t.name () in self.explicit_targets_:
- result.append (t)
-
- # 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 + "/"))
-
- return result
-
- def mark_targets_as_explicit (self, target_names):
- """Add 'target' to the list of targets in this project
- that should be build only by explicit request."""
-
- # Record the name of the target, not instance, since this
- # rule is called before main target instaces are created.
- self.explicit_targets_.update(target_names)
-
- def mark_targets_as_always(self, target_names):
- self.always_targets_.update(target_names)
-
- def add_alternative (self, target_instance):
- """ Add new target alternative.
- """
- 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):
- if not self.built_main_targets_:
- self.build_main_targets()
-
- return self.main_target_[name]
-
- def has_main_target (self, name):
- """Tells if a main target with the specified name exists."""
- if not self.built_main_targets_:
- self.build_main_targets()
-
- return self.main_target_.has_key(name)
-
- def create_main_target (self, name):
- """ Returns a 'MainTarget' class instance corresponding to the 'name'.
- """
- if not self.built_main_targets_:
- self.build_main_targets ()
-
- return self.main_targets_.get (name, None)
-
-
- def find_really(self, id):
- """ Find and return the target with the specified id, treated
- relative to self.
- """
- result = None
- current_location = self.get ('location')
-
- __re_split_project_target = re.compile (r'(.*)//(.*)')
- split = __re_split_project_target.match (id)
-
- project_part = None
- target_part = None
-
- if split:
- project_part = split.group (1)
- target_part = split.group (2)
-
- project_registry = self.project_.manager ().projects ()
-
- extra_error_message = ''
- if project_part:
- # There's explicit project part in id. Looks up the
- # project and pass the request to it.
- pm = project_registry.find (project_part, current_location)
-
- if pm:
- project_target = project_registry.target (pm)
- result = project_target.find (target_part, no_error=1)
-
- else:
- extra_error_message = "error: could not find project '$(project_part)'"
-
- else:
- # Interpret target-name as name of main target
- # Need to do this before checking for file. Consider this:
- #
- # exe test : test.cpp ;
- # install s : test : <location>. ;
- #
- # After first build we'll have target 'test' in Jamfile and file
- # 'test' on the disk. We need target to override the file.
-
- result = None
- if self.has_main_target(id):
- result = self.main_target(id)
-
- if not result:
- result = FileReference (self.manager_, id, self.project_)
- if not result.exists ():
- # File actually does not exist.
- # Reset 'target' so that an error is issued.
- result = None
-
-
- if not result:
- # Interpret id as project-id
- project_module = project_registry.find (id, current_location)
- if project_module:
- result = project_registry.target (project_module)
-
- return result
-
- def find (self, id, no_error = False):
- v = self.ids_cache_.get (id, None)
-
- if not v:
- v = self.find_really (id)
- self.ids_cache_ [id] = v
-
- if v or no_error:
- return v
-
- raise BaseException ("Unable to find file or target named '%s'\nreferred from project at '%s'" % (id, self.get ('location')))
-
-
- def build_main_targets (self):
- self.built_main_targets_ = True
-
- for a in self.alternatives_:
- name = a.name ()
- 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)
-
- def add_constant(self, name, value, path=0):
- """Adds a new constant for this project.
-
- The constant will be available for use in Jamfile
- module for this project. If 'path' is true,
- the constant will be interpreted relatively
- to the location of project.
- """
-
- if path:
- 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)
-
- self.constants_[name] = value
- bjam.call("set-variable", self.project_module(), name, value)
-
- def inherit(self, parent_project):
- for c in parent_project.constants_:
- # No need to pass the type. Path constants were converted to
- # absolute paths already by parent.
- self.add_constant(c, parent_project.constants_[c])
-
- # Import rules from parent
- this_module = self.project_module()
- parent_module = parent_project.project_module()
-
- rules = bjam.call("RULENAMES", parent_module)
- if not rules:
- rules = []
- user_rules = [x for x in rules
- if x not in self.manager().projects().project_rules().all_names()]
- if user_rules:
- bjam.call("import-rules-from-parent", parent_module, this_module, user_rules)
-
-class MainTarget (AbstractTarget):
- """ A named top-level target in Jamfile.
- """
- def __init__ (self, name, project):
- AbstractTarget.__init__ (self, name, project)
- self.alternatives_ = []
- self.default_build_ = property_set.empty ()
-
- def add_alternative (self, target):
- """ Add a new alternative for this target.
- """
- d = target.default_build ()
-
- if self.alternatives_ and self.default_build_ != d:
- get_manager().errors()("default build must be identical in all alternatives\n"
- "main target is '%s'\n"
- "with '%s'\n"
- "differing from previous default build: '%s'" % (self.full_name (), d.raw (), self.default_build_.raw ()))
-
- else:
- self.default_build_ = d
-
- self.alternatives_.append (target)
-
- 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)?
- """
- # When selecting alternatives we have to consider defaults,
- # for example:
- # 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 ()
-
- # The algorithm: we keep the current best viable alternative.
- # When we've got new best viable alternative, we compare it
- # with the current one.
- best = None
- best_properties = None
-
- if len (self.alternatives_) == 0:
- return None
-
- if len (self.alternatives_) == 1:
- return self.alternatives_ [0]
-
- if debug:
- print "Property set for selection:", property_set
-
- for v in self.alternatives_:
- properties = v.match (property_set, debug)
-
- if properties is not None:
- if not best:
- best = v
- best_properties = properties
-
- else:
- if b2.util.set.equal (properties, best_properties):
- return None
-
- elif b2.util.set.contains (properties, best_properties):
- # Do nothing, this alternative is worse
- pass
-
- elif b2.util.set.contains (best_properties, properties):
- best = v
- best_properties = properties
-
- else:
- return None
-
- return best
-
- def apply_default_build (self, property_set):
- return apply_default_build(property_set, self.default_build_)
-
- def generate (self, ps):
- """ Select an alternative for this main target, by finding all alternatives
- which requirements are satisfied by 'properties' and picking the one with
- longest requirements set.
- Returns the result of calling 'generate' on that alternative.
- """
- self.manager_.targets ().start_building (self)
-
- # We want composite properties in build request act as if
- # all the properties it expands too are explicitly specified.
- ps = ps.expand ()
-
- all_property_sets = self.apply_default_build (ps)
-
- result = GenerateResult ()
-
- for p in all_property_sets:
- result.extend (self.__generate_really (p))
-
- self.manager_.targets ().end_building (self)
-
- return result
-
- def __generate_really (self, prop_set):
- """ Generates the main target with the given property set
- and returns a list which first element is property_set object
- containing usage_requirements of generated target and with
- generated virtual target in other elements. It's possible
- that no targets are generated.
- """
- best_alternative = self.__select_alternatives (prop_set, debug=0)
-
- if not best_alternative:
- # FIXME: revive.
- # self.__select_alternatives(prop_set, debug=1)
- self.manager_.errors()(
- "No best alternative for '%s'.\n"
- % (self.full_name(),))
-
- result = best_alternative.generate (prop_set)
-
- # Now return virtual targets for the only alternative
- return result
-
- def rename(self, new_name):
- AbstractTarget.rename(self, new_name)
- for a in self.alternatives_:
- a.rename(new_name)
-
-class FileReference (AbstractTarget):
- """ Abstract target which refers to a source file.
- This is artificial creature; it's usefull so that sources to
- a target can be represented as list of abstract target instances.
- """
- def __init__ (self, manager, file, project):
- AbstractTarget.__init__ (self, file, project)
- self.file_location_ = None
-
- def generate (self, properties):
- return GenerateResult (None, [
- self.manager_.virtual_targets ().from_file (
- self.name_, self.location(), self.project_) ])
-
- def exists (self):
- """ Returns true if the referred file really exists.
- """
- if self.location ():
- return True
- else:
- return False
-
- def location (self):
- # Returns the location of target. Needed by 'testing.jam'
- if not self.file_location_:
- source_location = self.project_.get('source-location')
-
- for src_dir in source_location:
- location = os.path.join(src_dir, self.name())
- if os.path.isfile(location):
- self.file_location_ = src_dir
- self.file_path = location
- break
-
- return self.file_location_
-
-def resolve_reference(target_reference, project):
- """ Given a target_reference, made in context of 'project',
- returns the AbstractTarget instance that is referred to, as well
- as properties explicitly specified for this reference.
- """
- # Separate target name from properties override
- split = _re_separate_target_from_properties.match (target_reference)
- if not split:
- raise BaseException ("Invalid reference: '%s'" % target_reference)
-
- id = split.group (1)
-
- sproperties = []
-
- if split.group (3):
- sproperties = property.create_from_strings(feature.split(split.group(3)))
- sproperties = feature.expand_composites(sproperties)
-
- # Find the target
- target = project.find (id)
-
- return (target, property_set.create(sproperties))
-
-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
- - usage requirements
- - generated virtual targets, if any
- target_reference: Target reference
- project: Project where the reference is made
- property_set: Properties of the main target that makes the reference
- """
- target, sproperties = resolve_reference(target_reference, project)
-
- # Take properties which should be propagated and refine them
- # with source-specific requirements.
- propagated = property_set.propagated()
- rproperties = propagated.refine(sproperties)
-
- return target.generate(rproperties)
-
-
-
-class BasicTarget (AbstractTarget):
- """ Implements the most standard way of constructing main target
- alternative from sources. Allows sources to be either file or
- other main target and handles generation of those dependency
- targets.
- """
- def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None):
- AbstractTarget.__init__ (self, name, project)
-
- for s in sources:
- if get_grist (s):
- raise InvalidSource ("property '%s' found in the 'sources' parameter for '%s'" % (s, name))
-
- self.sources_ = sources
-
- if not requirements: requirements = property_set.empty ()
- self.requirements_ = requirements
-
- if not default_build: default_build = property_set.empty ()
- self.default_build_ = default_build
-
- if not usage_requirements: usage_requirements = property_set.empty ()
- self.usage_requirements_ = usage_requirements
-
- # A cache for resolved references
- self.source_targets_ = None
-
- # A cache for generated targets
- self.generated_ = {}
-
- # A cache for build requests
- self.request_cache = {}
-
- # Result of 'capture_user_context' has everything. For example, if this
- # target is declare as result of loading Jamfile which was loaded when
- # building target B which was requested from A, then we'll have A, B and
- # Jamroot location in context. We only care about Jamroot location, most
- # of the times.
- self.user_context_ = self.manager_.errors().capture_user_context()[-1:]
-
- 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-tests'
- feature of the test system.
- """
- if self.source_targets_ == None:
- self.source_targets_ = []
- for s in self.sources_:
- self.source_targets_.append(resolve_reference(s, self.project_)[0])
-
- return self.source_targets_
-
- def requirements (self):
- return self.requirements_
-
- def default_build (self):
- return self.default_build_
-
- def common_properties (self, build_request, requirements):
- """ Given build request and requirements, return properties
- common to dependency build request and target build
- properties.
- """
- # For optimization, we add free unconditional requirements directly,
- # without using complex algorithsm.
- # This gives the complex algorithm better chance of caching results.
- # The exact effect of this "optimization" is no longer clear
- free_unconditional = []
- other = []
- for p in requirements.all():
- if p.feature().free() and not p.condition() and p.feature().name() != 'conditional':
- free_unconditional.append(p)
- else:
- other.append(p)
- other = property_set.create(other)
-
- key = (build_request, other)
- if not self.request_cache.has_key(key):
- self.request_cache[key] = self.__common_properties2 (build_request, other)
-
- return self.request_cache[key].add_raw(free_unconditional)
-
- # Given 'context' -- a set of already present properties, and 'requirements',
- # decide which extra properties should be applied to 'context'.
- # For conditional requirements, this means evaluating condition. For
- # indirect conditional requirements, this means calling a rule. Ordinary
- # requirements are always applied.
- #
- # Handles situation where evaluating one conditional requirements affects
- # condition of another conditional requirements, for example:
- #
- # <toolset>gcc:<variant>release <variant>release:<define>RELEASE
- #
- # If 'what' is 'refined' returns context refined with new requirements.
- # If 'what' is 'added' returns just the requirements that must be applied.
- def evaluate_requirements(self, requirements, context, what):
- # Apply non-conditional requirements.
- # It's possible that that further conditional requirement change
- # a value set by non-conditional requirements. For example:
- #
- # exe a : a.cpp : <threading>single <toolset>foo:<threading>multi ;
- #
- # I'm not sure if this should be an error, or not, especially given that
- #
- # <threading>single
- #
- # might come from project's requirements.
- unconditional = feature.expand(requirements.non_conditional())
-
- context = context.refine(property_set.create(unconditional))
-
- # We've collected properties that surely must be present in common
- # properties. We now try to figure out what other properties
- # should be added in order to satisfy rules (4)-(6) from the docs.
-
- conditionals = property_set.create(requirements.conditional())
-
- # It's supposed that #conditionals iterations
- # should be enough for properties to propagate along conditions in any
- # direction.
- max_iterations = len(conditionals.all()) +\
- len(requirements.get("<conditional>")) + 1
-
- added_requirements = []
- current = context
-
- # It's assumed that ordinary conditional requirements can't add
- # <indirect-conditional> properties, and that rules referred
- # by <indirect-conditional> properties can't add new
- # <indirect-conditional> properties. So the list of indirect conditionals
- # does not change.
- indirect = requirements.get("<conditional>")
-
- ok = 0
- for i in range(0, max_iterations):
-
- e = conditionals.evaluate_conditionals(current).all()[:]
-
- # Evaluate indirect conditionals.
- for i in indirect:
- i = b2.util.jam_to_value_maybe(i)
- if callable(i):
- # This is Python callable, yeah.
- e.extend(i(current))
- else:
- # Name of bjam function. Because bjam is unable to handle
- # list of Property, pass list of strings.
- br = b2.util.call_jam_function(i[1:], [str(p) for p in current.all()])
- if br:
- e.extend(property.create_from_strings(br))
-
- if e == added_requirements:
- # If we got the same result, we've found final properties.
- ok = 1
- break
- else:
- # Oops, results of evaluation of conditionals has changed.
- # Also 'current' contains leftover from previous evaluation.
- # Recompute 'current' using initial properties and conditional
- # requirements.
- added_requirements = e
- current = context.refine(property_set.create(feature.expand(e)))
-
- if not ok:
- self.manager().errors()("Can't evaluate conditional properties "
- + str(conditionals))
-
-
- if what == "added":
- return property_set.create(unconditional + added_requirements)
- elif what == "refined":
- return current
- else:
- self.manager().errors("Invalid value of the 'what' parameter")
-
- def __common_properties2(self, build_request, requirements):
- # This guarantees that default properties are present
- # in result, unless they are overrided by some requirement.
- # TODO: There is possibility that we've added <foo>bar, which is composite
- # and expands to <foo2>bar2, but default value of <foo2> is not bar2,
- # in which case it's not clear what to do.
- #
- 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.
- # It could be clearer/faster to expand only newly added properties
- # but that's not critical.
- build_request = build_request.expand()
-
- return self.evaluate_requirements(requirements, build_request,
- "refined")
-
- def match (self, property_set, debug):
- """ Returns the alternative condition for this alternative, if
- the condition is satisfied by 'property_set'.
- """
- # The condition is composed of all base non-conditional properties.
- # It's not clear if we should expand 'self.requirements_' or not.
- # For one thing, it would be nice to be able to put
- # <toolset>msvc-6.0
- # in requirements.
- # 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.
- 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 debug:
- print " matched"
-
- return condition
-
- else:
- return None
-
-
- def generate_dependency_targets (self, target_ids, property_set):
- targets = []
- usage_requirements = []
- for id in target_ids:
-
- result = generate_from_reference(id, self.project_, property_set)
- targets += result.targets()
- usage_requirements += result.usage_requirements().all()
-
- return (targets, usage_requirements)
-
- def generate_dependency_properties(self, properties, ps):
- """ Takes a target reference, which might be either target id
- or a dependency property, and generates that target using
- 'property_set' as build request.
-
- Returns a tuple (result, usage_requirements).
- """
- result_properties = []
- usage_requirements = []
- for p in properties:
-
- result = generate_from_reference(p.value(), self.project_, ps)
-
- for t in result.targets():
- result_properties.append(property.Property(p.feature(), t))
-
- usage_requirements += result.usage_requirements().all()
-
- return (result_properties, usage_requirements)
-
-
-
-
- @user_error_checkpoint
- def generate (self, ps):
- """ Determines final build properties, generates sources,
- and calls 'construct'. This method should not be
- overridden.
- """
- self.manager_.errors().push_user_context(
- "Generating target " + self.full_name(), self.user_context_)
-
- if self.manager().targets().logging():
- self.manager().targets().log(
- "Building target '%s'" % self.name_)
- self.manager().targets().increase_indent ()
- self.manager().targets().log(
- "Build request: '%s'" % str (ps.raw ()))
- cf = self.manager().command_line_free_features()
- self.manager().targets().log(
- "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):
-
- # Apply free features form the command line. If user
- # said
- # define=FOO
- # he most likely want this define to be set for all compiles.
- ps = ps.refine(self.manager().command_line_free_features())
- rproperties = self.common_properties (ps, self.requirements_)
-
- self.manager().targets().log(
- "Common properties are '%s'" % str (rproperties))
-
- if rproperties.get("<build>") != ["no"]:
-
- result = GenerateResult ()
-
- properties = rproperties.non_dependency ()
-
- (p, u) = self.generate_dependency_properties (rproperties.dependency (), rproperties)
- properties += p
- assert all(isinstance(p, property.Property) for p in properties)
- usage_requirements = u
-
- (source_targets, u) = self.generate_dependency_targets (self.sources_, rproperties)
- usage_requirements += u
-
- self.manager_.targets().log(
- "Usage requirements for '%s' are '%s'" % (self.name_, usage_requirements))
-
- # FIXME:
-
- rproperties = property_set.create(properties + usage_requirements)
- usage_requirements = property_set.create (usage_requirements)
-
- self.manager_.targets().log(
- "Build properties: '%s'" % str(rproperties))
-
- source_targets += rproperties.get('<source>')
-
- # We might get duplicate sources, for example if
- # we link to two library which have the same <library> in
- # usage requirements.
- # 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)
-
- if result:
- assert len(result) == 2
- 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,
- source_targets, rproperties, usage_requirements)
- self.manager().virtual_targets().clear_recent_targets()
-
- ur = self.compute_usage_requirements (s)
- ur = ur.add (gur)
- s.set_usage_requirements (ur)
-
- self.manager_.targets().log (
- "Usage requirements from '%s' are '%s'" %
- (self.name(), str(rproperties)))
-
- self.generated_[ps] = GenerateResult (ur, result)
- else:
- self.generated_[ps] = GenerateResult (property_set.empty(), [])
- else:
- # If we just see <build>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.
-
- # If this target fails to build, add <build>no to properties
- # to cause any parent target to fail to build. Except that it
- # - does not work now, since we check for <build>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(["<build>no"]), [])
- else:
- self.manager().targets().log ("Already built")
-
- self.manager().targets().pop_target()
- self.manager().targets().decrease_indent()
-
- return self.generated_[ps]
-
- def compute_usage_requirements (self, subvariant):
- """ Given the set of generated targets, and refined build
- properties, determines and sets appripriate usage requirements
- on those targets.
- """
- rproperties = subvariant.build_properties ()
- xusage_requirements =self.evaluate_requirements(
- self.usage_requirements_, rproperties, "added")
-
- # We generate all dependency properties and add them,
- # as well as their usage requirements, to result.
- (r1, r2) = self.generate_dependency_properties(xusage_requirements.dependency (), rproperties)
- extra = r1 + r2
-
- result = property_set.create (xusage_requirements.non_dependency () + extra)
-
- # Propagate usage requirements we've got from sources, except
- # for the <pch-header> and <pch-file> features.
- #
- # That feature specifies which pch file to use, and should apply
- # only to direct dependents. Consider:
- #
- # pch pch1 : ...
- # lib lib1 : ..... pch1 ;
- # pch pch2 :
- # lib lib2 : pch2 lib1 ;
- #
- # Here, lib2 should not get <pch-header> property from pch1.
- #
- # Essentially, when those two features are in usage requirements,
- # they are propagated only to direct dependents. We might need
- # a more general mechanism, but for now, only those two
- # features are special.
- raw = subvariant.sources_usage_requirements().raw()
- raw = property.change(raw, "<pch-header>", None);
- raw = property.change(raw, "<pch-file>", None);
- result = result.add(property_set.create(raw))
-
- return result
-
- def create_subvariant (self, root_targets, all_targets,
- build_request, sources,
- rproperties, usage_requirements):
- """Creates a new subvariant-dg instances for 'targets'
- - 'root-targets' the virtual targets will be returned to dependents
- - 'all-targets' all virtual
- targets created while building this main target
- - 'build-request' is property-set instance with
- requested build properties"""
-
- for e in root_targets:
- e.root (True)
-
- s = Subvariant (self, build_request, sources,
- rproperties, usage_requirements, all_targets)
-
- for v in all_targets:
- if not v.creating_subvariant():
- v.creating_subvariant(s)
-
- return s
-
- def construct (self, name, source_targets, properties):
- """ Constructs the virtual targets for this abstract targets and
- the dependecy graph. Returns a tuple consisting of the properties and the list of virtual targets.
- Should be overrided in derived classes.
- """
- raise BaseException ("method should be defined in derived classes")
-
-
-class TypedTarget (BasicTarget):
- import generators
-
- def __init__ (self, name, project, type, sources, requirements, default_build, usage_requirements):
- BasicTarget.__init__ (self, name, project, sources, requirements, default_build, usage_requirements)
- self.type_ = type
-
- def __jam_repr__(self):
- return b2.util.value_to_jam(self)
-
- def type (self):
- return self.type_
-
- def construct (self, name, source_targets, prop_set):
-
- r = generators.construct (self.project_, name, self.type_,
- prop_set.add_raw(['<main-target-type>' + self.type_]),
- source_targets, True)
-
- if not r:
- print "warning: Unable to construct '%s'" % self.full_name ()
-
- # Are there any top-level generators for this type/property set.
- if not generators.find_viable_generators (self.type_, prop_set):
- print "error: no generators were found for type '" + self.type_ + "'"
- print "error: and the requested properties"
- print "error: make sure you've configured the needed tools"
- print "See http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
-
- print "To debug this problem, try the --debug-generators option."
- sys.exit(1)
-
- 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:
- #
- # <toolset>msvc <toolset-msvc:version>7.1 <threading>multi
- #
- # from being expanded into:
- #
- # <toolset-msvc:version>7.1/<threading>multi
- # <toolset>msvc/<toolset-msvc:version>7.1/<threading>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):
-
- from b2.manager import get_manager
- t = get_manager().targets()
-
- project = get_manager().projects().current()
-
- return t.main_target_alternative(
- TypedTarget(name, project, type,
- t.main_target_sources(sources, name),
- t.main_target_requirements(requirements, project),
- t.main_target_default_build(default_build, project),
- t.main_target_usage_requirements(usage_requirements, project)))
-
-
-def create_metatarget(klass, name, sources, requirements=[], default_build=[], usage_requirements=[]):
- from b2.manager import get_manager
- t = get_manager().targets()
-
- project = get_manager().projects().current()
-
- return t.main_target_alternative(
- klass(name, project,
- t.main_target_sources(sources, name),
- t.main_target_requirements(requirements, project),
- t.main_target_default_build(default_build, project),
- t.main_target_usage_requirements(usage_requirements, project)))
-
-def metatarget_function_for_class(class_):
-
- @bjam_signature((["name"], ["sources", "*"], ["requirements", "*"],
- ["default_build", "*"], ["usage_requirements", "*"]))
- def create_metatarget(name, sources, requirements = [], default_build = None, usage_requirements = []):
-
- from b2.manager import get_manager
- t = get_manager().targets()
-
- project = get_manager().projects().current()
-
- return t.main_target_alternative(
- class_(name, project,
- t.main_target_sources(sources, name),
- t.main_target_requirements(requirements, project),
- t.main_target_default_build(default_build, project),
- t.main_target_usage_requirements(usage_requirements, project)))
-
- return create_metatarget