Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r64458 - in trunk/tools/build/v2: build tools
From: ghost_at_[hidden]
Date: 2010-07-30 03:38:19


Author: vladimir_prus
Date: 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
New Revision: 64458
URL: http://svn.boost.org/trac/boost/changeset/64458

Log:
Port tools/stage.jam.

Added:
   trunk/tools/build/v2/tools/stage.py (contents, props changed)
Text files modified:
   trunk/tools/build/v2/build/property_set.py | 17 ++++++++++++-
   trunk/tools/build/v2/build/targets.py | 15 +++++++-----
   trunk/tools/build/v2/build/type.py | 5 +++
   trunk/tools/build/v2/build/virtual_target.py | 48 +++++++++++++++++++++++++--------------
   trunk/tools/build/v2/tools/builtin.py | 2 +
   5 files changed, 61 insertions(+), 26 deletions(-)

Modified: trunk/tools/build/v2/build/property_set.py
==============================================================================
--- trunk/tools/build/v2/build/property_set.py (original)
+++ trunk/tools/build/v2/build/property_set.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -366,7 +366,7 @@
             # change the location of generated targets
             l = self.get ('<location>')
             if l:
- computed = l
+ computed = l[0]
                 is_relative = False
 
             else:
@@ -394,7 +394,7 @@
                 is_relative = True
 
             self.target_path_ = (computed, is_relative)
-
+
         return self.target_path_
                     
     def add (self, ps):
@@ -428,6 +428,19 @@
         
         return self.feature_map_.get(feature, [])
 
+ # FIXME: make this cached
+ def get_properties(self, feature):
+ """Returns all contained properties associated with 'feature'"""
+
+ if not isinstance(feature, b2.build.feature.Feature):
+ feature = b2.build.feature.get(feature)
+
+ result = []
+ for p in self.all_:
+ if p.feature() == feature:
+ result.append(p)
+ return result
+
     def __contains__(self, item):
         for p in self.all_set_:
             if p.feature().name() == "toolset":

Modified: trunk/tools/build/v2/build/targets.py
==============================================================================
--- trunk/tools/build/v2/build/targets.py (original)
+++ trunk/tools/build/v2/build/targets.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -346,9 +346,6 @@
         self.default_build_ = default_build
        
         self.build_dir_ = None
-
- if parent_project:
- self.inherit (parent_project)
         
         # A cache of IDs
         self.ids_cache_ = {}
@@ -372,6 +369,10 @@
         # 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):
@@ -563,7 +564,7 @@
             value = os.path.join(self.location_, 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)
 
@@ -571,7 +572,7 @@
         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(parent_project.constants_[c])
+ self.add_constant(c, parent_project.constants_[c])
         
         # Import rules from parent
         this_module = self.project_module()
@@ -1122,7 +1123,9 @@
                 # usage requirements.
                 source_targets = unique (source_targets)
 
- result = self.construct (self.name_, source_targets, rproperties)
+ # 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]

Modified: trunk/tools/build/v2/build/type.py
==============================================================================
--- trunk/tools/build/v2/build/type.py (original)
+++ trunk/tools/build/v2/build/type.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -14,6 +14,7 @@
 from b2.util.utility import replace_grist, os_name
 from b2.exceptions import *
 from b2.build import feature, property, scanner
+from b2.util import bjam_signature
 
 __re_hyphen = re.compile ('-')
 
@@ -53,7 +54,7 @@
     
 reset ()
 
-
+@bjam_signature((["type"], ["suffixes", "*"], ["base_type"]))
 def register (type, suffixes = [], base_type = None):
     """ Registers a target type, possibly derived from a 'base-type'.
         If 'suffixes' are provided, they list all the suffixes that mean a file is of 'type'.
@@ -175,6 +176,7 @@
     # TODO: remove this method
     return is_derived (type, base)
 
+@bjam_signature((["type"], ["properties", "*"], ["suffix"]))
 def set_generated_target_suffix (type, properties, suffix):
     """ Sets a target suffix that should be used when generating target
         of 'type' with the specified properties. Can be called with
@@ -209,6 +211,7 @@
 # should be used.
 #
 # Usage example: library names use the "lib" prefix on unix.
+@bjam_signature((["type"], ["properties", "*"], ["suffix"]))
 def set_generated_target_prefix(type, properties, prefix):
     set_generated_target_ps(0, type, properties, prefix)
 

Modified: trunk/tools/build/v2/build/virtual_target.py
==============================================================================
--- trunk/tools/build/v2/build/virtual_target.py (original)
+++ trunk/tools/build/v2/build/virtual_target.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -65,6 +65,7 @@
 import re
 import os.path
 import string
+import types
 
 from b2.util import path, utility, set
 from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, get_value
@@ -73,7 +74,8 @@
 from b2.exceptions import *
 import b2.build.type
 import b2.build.property_set as property_set
-import type
+
+import b2.build.property as property
 
 __re_starts_with_at = re.compile ('^@(.*)')
 
@@ -158,7 +160,7 @@
         if self.files_.has_key (path):
             return self.files_ [path]
 
- file_type = type.type (file)
+ file_type = b2.build.type.type (file)
 
         result = FileTarget (file, file_type, project,
                              None, file_location)
@@ -184,7 +186,7 @@
     # 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)]
+ return [t for t in targets if b2.build.type.is_sybtype(t.type(), type)]
 
     def register_actual_name (self, actual_name, virtual_target):
         if self.actual_.has_key (actual_name):
@@ -231,7 +233,7 @@
         """ 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)
+ suffix = b2.build.type.generated_target_suffix (file_type, prop_set)
 
         if suffix:
             return specified_name + '.' + suffix
@@ -616,7 +618,10 @@
         self.path_ = path
 
     def __str__(self):
- return self.name_ + "." + self.type_
+ if self.type_:
+ return self.name_ + "." + self.type_
+ else:
+ return self.name_
 
     def clone_with_different_type(self, new_type):
         return FileTarget(self.name_, new_type, self.project_,
@@ -677,13 +682,13 @@
         """
         if not self.path_:
             if self.action_:
- p = self.action_.properties ()
- target_path = p.target_path ()
+ p = self.action_.properties ()
+ (target_path, relative_to_build_dir) = p.target_path ()
                 
- if target_path [1] == True:
+ if relative_to_build_dir:
                     # Indicates that the path is relative to
                     # build dir.
- target_path = os.path.join (self.project_.build_dir (), target_path [0])
+ target_path = os.path.join (self.project_.build_dir (), target_path)
                                 
                 # Store the computed path, so that it's not recomputed
                 # any more
@@ -717,6 +722,7 @@
     """
     def __init__ (self, manager, sources, action_name, prop_set):
         assert(isinstance(prop_set, property_set.PropertySet))
+ assert type(sources) == types.ListType
         self.sources_ = sources
         self.action_name_ = action_name
         if not prop_set:
@@ -815,7 +821,7 @@
 # i = self.manager_.get_object (i)
                 
             if i.type ():
- scanner = type.get_scanner (i.type (), prop_set)
+ scanner = b2.build.type.get_scanner (i.type (), prop_set)
 
             r = i.actualize (scanner)
             result.append (r)
@@ -938,7 +944,7 @@
     if not new_properties:
         new_properties = action.properties()
 
- closed_action = action.__class__(action.sources(), new_action_name,
+ cloned_action = action.__class__(action.manager_, action.sources(), new_action_name,
                                      new_properties)
                           
     cloned_targets = []
@@ -1018,17 +1024,25 @@
         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:
- if not t in result:
- result.add(t)
- r.append(t.creating_subvariant)
+ for e in all_targets:
+ if not e in result:
+ result.add(e)
+ if isinstance(e, property.Property):
+ t = e.value()
+ else:
+ t = e
+
+ # FIXME: how can this be?
+ cs = t.creating_subvariant()
+ if cs:
+ r.append(cs)
         r = unique(r)
         for s in r:
             if s != self:
@@ -1069,7 +1083,7 @@
     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):
+ if not target_type or b2.build.type.is_derived(t.type(), target_type):
                 result.append(t.path())
 
         for d in self.other_dg_:

Modified: trunk/tools/build/v2/tools/builtin.py
==============================================================================
--- trunk/tools/build/v2/tools/builtin.py (original)
+++ trunk/tools/build/v2/tools/builtin.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -724,3 +724,5 @@
 ###
 
 get_manager().projects().add_rule("variant", variant)
+
+import stage

Added: trunk/tools/build/v2/tools/stage.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/stage.py 2010-07-30 03:38:13 EDT (Fri, 30 Jul 2010)
@@ -0,0 +1,349 @@
+# Status: ported.
+# Base revision 64444.
+#
+# Copyright 2003 Dave Abrahams
+# Copyright 2005, 2006 Rene Rivera
+# Copyright 2002, 2003, 2004, 2005, 2006, 2010 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+# This module defines the 'install' rule, used to copy a set of targets to a
+# single location.
+
+import b2.build.feature as feature
+import b2.build.targets as targets
+import b2.build.property as property
+import b2.build.property_set as property_set
+import b2.build.generators as generators
+import b2.build.virtual_target as virtual_target
+
+from b2.manager import get_manager
+from b2.util.sequence import unique
+from b2.util import bjam_signature
+
+import b2.build.type
+
+import os.path
+import re
+import types
+
+feature.feature('install-dependencies', ['off', 'on'], ['incidental'])
+feature.feature('install-type', [], ['free', 'incidental'])
+feature.feature('install-source-root', [], ['free', 'path'])
+feature.feature('so-version', [], ['free', 'incidental'])
+
+# If 'on', version symlinks for shared libraries will not be created. Affects
+# Unix builds only.
+feature.feature('install-no-version-symlinks', ['on'], ['optional', 'incidental'])
+
+class InstallTargetClass(targets.BasicTarget):
+
+ def update_location(self, ps):
+ """If <location> is not set, sets it based on the project data."""
+
+ loc = ps.get('location')
+ if not loc:
+ loc = os.path.join(self.project().get('location'), self.name())
+ ps = ps.add_raw(["<location>" + loc])
+
+ return ps
+
+ def adjust_properties(self, target, build_ps):
+ a = target.action()
+ properties = []
+ if a:
+ ps = a.properties()
+ properties = ps.all()
+
+ # Unless <hardcode-dll-paths>true is in properties, which can happen
+ # only if the user has explicitly requested it, nuke all <dll-path>
+ # properties.
+
+ if build_ps.get('hardcode-dll-paths') != ['true']:
+ properties = [p for p in properties if p.feature().name() != 'dll-path']
+
+ # If any <dll-path> properties were specified for installing, add
+ # them.
+ properties.extend(build_ps.get_properties('dll-path'))
+
+ # Also copy <linkflags> feature from current build set, to be used
+ # for relinking.
+ properties.extend(build_ps.get_properties('linkflags'))
+
+ # Remove the <tag> feature on original targets.
+ # And <location>. If stage target has another stage target in
+ # sources, then we shall get virtual targets with the <location>
+ # property set.
+ properties = [p for p in properties
+ if not p.feature().name() in ['tag', 'location']]
+
+ properties.extend(build_ps.get_properties('dependency'))
+
+ properties.extend(build_ps.get_properties('location'))
+
+
+ properties.extend(build_ps.get_properties('install-no-version-symlinks'))
+
+ d = build_ps.get_properties('install-source-root')
+
+ # Make the path absolute: we shall use it to compute relative paths and
+ # making the path absolute will help.
+ if d:
+ p = d[0]
+ properties.append(property.Property(p.feature(), os.path.abspath(p.value())))
+
+ return property_set.create(properties)
+
+
+ def construct(self, name, source_targets, ps):
+
+ source_targets = self.targets_to_stage(source_targets, ps)
+ ps = self.update_location(ps)
+
+ ename = ps.get('name')
+ if ename:
+ ename = ename[0]
+ if ename and len(source_targets) > 1:
+ get_manager().errors()("When <name> property is used in 'install', only one source is allowed")
+
+ result = []
+
+ for i in source_targets:
+
+ staged_targets = []
+ new_ps = self.adjust_properties(i, ps)
+
+ # See if something special should be done when staging this type. It
+ # is indicated by the presence of a special "INSTALLED_" type.
+ t = i.type()
+ if t and b2.build.type.registered("INSTALLED_" + t):
+
+ if ename:
+ get_manager().errors()("In 'install': <name> property specified with target that requires relinking.")
+ else:
+ (r, targets) = generators.construct(self.project(), name, "INSTALLED_" + t,
+ new_ps, [i])
+ assert isinstance(r, property_set.PropertySet)
+ staged_targets.extend(targets)
+
+ else:
+ staged_targets.append(copy_file(self.project(), ename, i, new_ps))
+
+ if not staged_targets:
+ get_manager().errors()("Unable to generate staged version of " + i)
+
+ result.extend(get_manager().virtual_targets().register(t) for t in staged_targets)
+
+ return (property_set.empty(), result)
+
+ def targets_to_stage(self, source_targets, ps):
+ """Given the list of source targets explicitly passed to 'stage', returns the
+ list of targets which must be staged."""
+
+ result = []
+
+ # Traverse the dependencies, if needed.
+ if ps.get('install-dependencies') == ['on']:
+ source_targets = self.collect_targets(source_targets)
+
+ # Filter the target types, if needed.
+ included_types = ps.get('install-type')
+ for r in source_targets:
+ ty = r.type()
+ if ty:
+ # Do not stage searched libs.
+ if ty != "SEARCHED_LIB":
+ if included_types:
+ if self.include_type(ty, included_types):
+ result.append(r)
+ else:
+ result.append(r)
+ elif not included_types:
+ # Don't install typeless target if there is an explicit list of
+ # allowed types.
+ result.append(r)
+
+ return result
+
+ # CONSIDER: figure out why we can not use virtual-target.traverse here.
+ #
+ def collect_targets(self, targets):
+
+ s = [t.creating_subvariant() for t in targets]
+ s = unique(s)
+
+ result = set(targets)
+ for i in s:
+ i.all_referenced_targets(result)
+
+ result2 = []
+ for r in result:
+ if isinstance(r, property.Property):
+
+ if r.feature().name() != 'use':
+ result2.append(r.value())
+ else:
+ result2.append(r)
+ result2 = unique(result2)
+ return result2
+
+ # Returns true iff 'type' is subtype of some element of 'types-to-include'.
+ #
+ def include_type(self, type, types_to_include):
+ return any(b2.build.type.is_subtype(type, ti) for ti in types_to_include)
+
+# Creates a copy of target 'source'. The 'properties' object should have a
+# <location> property which specifies where the target must be placed.
+#
+def copy_file(project, name, source, ps):
+
+ if not name:
+ name = source.name()
+
+ relative = ""
+
+ new_a = virtual_target.NonScanningAction([source], "common.copy", ps)
+ source_root = ps.get('install-source-root')
+ if source_root:
+ source_root = source_root[0]
+ # Get the real path of the target. We probably need to strip relative
+ # path from the target name at construction.
+ path = os.path.join(source.path(), os.path.dirname(name))
+ # Make the path absolute. Otherwise, it would be hard to compute the
+ # relative path. The 'source-root' is already absolute, see the
+ # 'adjust-properties' method above.
+ path = os.path.abspath(path)
+
+ relative = os.path.relpath(path, source_root)
+
+ name = os.path.join(relative, os.path.basename(name))
+ return virtual_target.FileTarget(name, source.type(), project, new_a, exact=True)
+
+def symlink(name, project, source, ps):
+ a = virtual_target.Action([source], "symlink.ln", ps)
+ return virtual_target.FileTarget(name, source.type(), project, a, exact=True)
+
+def relink_file(project, source, ps):
+ action = source.action()
+ cloned_action = virtual_target.clone_action(action, project, "", ps)
+ targets = cloned_action.targets()
+ # We relink only on Unix, where exe or shared lib is always a single file.
+ assert len(targets) == 1
+ return targets[0]
+
+
+# Declare installed version of the EXE type. Generator for this type will cause
+# relinking to the new location.
+b2.build.type.register('INSTALLED_EXE', [], 'EXE')
+
+class InstalledExeGenerator(generators.Generator):
+
+ def __init__(self):
+ generators.Generator.__init__(self, "install-exe", False, ['EXE'], ['INSTALLED_EXE'])
+
+ def run(self, project, name, ps, source):
+
+ need_relink = False;
+
+ if ps.get('os') in ['NT', 'CYGWIN'] or ps.get('target-os') in ['windows', 'cygwin']:
+ # Never relink
+ pass
+ else:
+ # See if the dll-path properties are not changed during
+ # install. If so, copy, don't relink.
+ need_relink = ps.get('dll-path') != source[0].action().properties().get('dll-path')
+
+ if need_relink:
+ return [relink_file(project, source, ps)]
+ else:
+ return [copy_file(project, None, source[0], ps)]
+
+generators.register(InstalledExeGenerator())
+
+
+# Installing a shared link on Unix might cause a creation of versioned symbolic
+# links.
+b2.build.type.register('INSTALLED_SHARED_LIB', [], 'SHARED_LIB')
+
+class InstalledSharedLibGenerator(generators.Generator):
+
+ def __init__(self):
+ generators.Generator.__init__(self, 'install-shared-lib', False, ['SHARED_LIB'], ['INSTALLED_SHARED_LIB'])
+
+ def run(self, project, name, ps, source):
+
+ source = source[0]
+ if ps.get('os') in ['NT', 'CYGWIN'] or ps.get('target-os') in ['windows', 'cygwin']:
+ copied = copy_file(project, None, source, ps)
+ return [get_manager().virtual_targets().register(copied)]
+ else:
+ a = source.action()
+ if not a:
+ # Non-derived file, just copy.
+ copied = copy_file(project, source, ps)
+ else:
+
+ need_relink = ps.get('dll-path') != source.action().properties().get('dll-path')
+
+ if need_relink:
+ # Rpath changed, need to relink.
+ copied = relink_file(project, source, ps)
+ else:
+ copied = copy_file(project, None, source, ps)
+
+ result = [get_manager().virtual_targets().register(copied)]
+ # If the name is in the form NNN.XXX.YYY.ZZZ, where all 'X', 'Y' and
+ # 'Z' are numbers, we need to create NNN.XXX and NNN.XXX.YYY
+ # symbolic links.
+ m = re.match("(.*)\\.([0123456789]+)\\.([0123456789]+)\\.([0123456789]+)$",
+ copied.name());
+ if m:
+ # Symlink without version at all is used to make
+ # -lsome_library work.
+ result.append(symlink(m.group(1), project, copied, ps))
+
+ # Symlinks of some libfoo.N and libfoo.N.M are used so that
+ # library can found at runtime, if libfoo.N.M.X has soname of
+ # libfoo.N. That happens when the library makes some binary
+ # compatibility guarantees. If not, it is possible to skip those
+ # symlinks.
+ if ps.get('install-no-version-symlinks') != ['on']:
+
+ result.append(symlink(m.group(1) + '.' + m.group(2), project, copied, ps))
+ result.append(symlink(m.group(1) + '.' + m.group(2) + '.' + m.group(3),
+ project, copied, ps))
+
+ return result
+
+generators.register(InstalledSharedLibGenerator())
+
+
+# Main target rule for 'install'.
+#
+@bjam_signature((["name"], ["sources", "*"], ["requirements", "*"],
+ ["default_build", "*"], ["usage_requirements", "*"]))
+def install(name, sources, requirements, default_build, usage_requirements):
+
+ # Unless the user has explicitly asked us to hardcode dll paths, add
+ # <hardcode-dll-paths>false in requirements, to override default value.
+ if not '<hardcode-dll-paths>true' in requirements:
+ requirements.append('<hardcode-dll-paths>false')
+
+ if any(r.startswith('<tag>') for r in requirements):
+ get_manager().errors()("The <tag> property is not allowed for the 'install' rule")
+
+ from b2.manager import get_manager
+ t = get_manager().targets()
+
+ project = get_manager().projects().current()
+
+ return t.main_target_alternative(
+ InstallTargetClass(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)))
+
+get_manager().projects().add_rule("install", install)
+get_manager().projects().add_rule("stage", install)
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk