|
Boost-Commit : |
From: ghost_at_[hidden]
Date: 2007-10-08 14:48:19
Author: vladimir_prus
Date: 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
New Revision: 39814
URL: http://svn.boost.org/trac/boost/changeset/39814
Log:
Progress on porting project.jam.
Text files modified:
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/DESIGN.txt | 13
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/TODO.txt | 62
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/project.py | 1596 +++++++++++++++++++++------------------
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/property.py | 4
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/targets.py | 15
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/type.py | 5
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/main.py | 20
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/manager.py | 15
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/builtin.py | 3
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/common.py | 2
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/make.py | 89 +-
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/util/path.py | 48 +
branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/tests/bjam/make/Jamroot | 5
13 files changed, 1054 insertions(+), 823 deletions(-)
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/DESIGN.txt
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/DESIGN.txt (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/DESIGN.txt 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -16,7 +16,8 @@
- No globals -- all data that is global in existing code will be
kepts inside Python object that are kept inside single top-level
- "manager" object
+ "manager" object. This applies only to core code -- the
+ tools layer will use globals for now.
- Build actions in Python -- in case where jam code, for a given
command line, has both a command line proper and procedural
code to setup something, said procedural code will be moved in
@@ -26,6 +27,10 @@
- Using of the property_set class. Unless required for compatibility
with Jamfiles, we'll be using property_set in all remaining cases
where raw property list is used now.
+ - Path normalization. We only need path normalization when loading
+ Jamfiles, to make sure we don't load the same Jamfile twice,
+ no matter what path is used to refer to is. Other than that,
+ all path normalization logic will be dropped.
= Design: Physical structure =
@@ -77,6 +82,12 @@
declaration. The actions, however, do need special declaration, and
there are two methods to declare a target
+= Hints =
+
+All code callable from Jam gets string *list* and *every*
+argument. When logically, only a single string is allowed,
+use [0].
+
= Bjam <-> Python interface =
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/TODO.txt
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/TODO.txt (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/TODO.txt 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -1,31 +1,4 @@
-1. Current mechanism for interfacing with jam build engine is
-nasty. Ideally, we should have this:
-
- - The virtual_target.Action class holds action id.
- - There's a map from action id into callable. Said
- callable takes list of jam targets, list of jam
- sources of a property set. When the callable is called,
- it typically just calls bjam's action.
- - A new action id should be registered with 'action registry'
- in one of those ways:
-
- - By saying that certain action id is already
- defined on jam side. In which case a callable will
- be created that just calls bjam.
- - By providing a action block to be associated
- with id. In this case, the action block will
- be sent to bjam side. The callable will just
- relay to bjam.
- - By providing an action block and a python
- function for additional setting of variables.
- The callable will the call the variable-setting
- code and relay to bjam. This is equivalent to
- jams rule/action pair, except we prefer to specify
- variable-setting code in Python, not in Jam.
-
-To implement the above, we need a way to define action block
-from Python.
2. The current way to make rules available to Jamfiles is nasty --
only functions defined in ProjectContext class are exported.
@@ -36,4 +9,37 @@
4. The error messages, so far, are truly terrible -- if Python
code throws does something wrong we get lots of backtrace.
-5. Can one have nested classes in Python.
\ No newline at end of file
+5. Can one have nested classes in Python.
+
+-> 6. Need to consider how to reimplement the 'errors' modules.
+
+-> 7. Pass bjam's argv to Python
+
+8. For some reason, if Python exports a function taking no
+arguments to bjam, and bjam calls it with no arguments, bjam
+passes 1 argument (empty list).
+
+9. What to do about 'using' and 'import'?
+
+10. In the make example, the action is called fine but
+the associated procedural code is not executed.
+
+
+Sanity checks:
+--------------
+
+- Review all FIXME in code
+- Remove all porting state comments at the top of files.
+
+
+Post-port
+---------
+
+- Use 'Metetarget' for classes in the 'targets' module.
+
+- The 'exact' parameter to FileTarget should be passed using
+keyword arguments.
+
+- Keep all information pertaining to a project inside project
+target. Kill ProjectAttributes class.
+
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/project.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/project.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/project.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -1,20 +1,20 @@
-# Copyright (C) Vladimir Prus and Rene Rivera 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.
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2002, 2005, 2006 Rene Rivera
+# Copyright 2002, 2003, 2004, 2005, 2006 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)
# Implements project representation and loading.
# Each project is represented by
# - a module where all the Jamfile content live.
-# - an instance of 'ProjectAttributes' class.
+# - an instance of 'project-attributes' class.
# (given module name, can be obtained by 'attributes' rule)
# - an instance of 'project-target' class (from targets.jam)
# (given a module name, can be obtained by 'target' rule)
#
# Typically, projects are created as result of loading Jamfile, which is
-# done by rules 'load' and 'initialize', below. First, module for Jamfile
-# is loaded and new ProjectAttributes instance is created. Some rules
+# do by rules 'load' and 'initialize', below. First, module for Jamfile
+# is loaded and new project-attributes instance is created. Some rules
# necessary for project are added to the module (see 'project-rules' module)
# at the bottom of this file.
# Default project attributes are set (inheriting attributes of parent project, if
@@ -32,773 +32,901 @@
# targets.
#
# The list of all loaded Jamfile is stored in variable .project-locations. It's possible
-# to obtain module name for a location using 'module_name' rule. The standalone projects
+# to obtain module name for a location using 'module-name' rule. The standalone projects
# are not recorded, the only way to use them is by project id.
-from boost.build.util import path, set
-from boost.build.build import property_set, property
+import boost.build.util.path
+from boost.build.build import property_set
import boost.build.build.targets
+import bjam
+import re
+
import sys
-import os.path
+import os
-class ProjectAttributes:
- """ Class keeping all the attributes of a project.
- The standard attributes are "id", "location", "project-root", "parent"
- "requirements", "default-build", "source-location" and "projects-to-build".
- """
- def __init__ (self, manager, location, parent=None):
+class ProjectRegistry:
- self.manager_ = manager
-
- # The directory of the project.
- self.location_ = location
+ def __init__(self, manager, global_build_dir):
+ self.manager = manager
+ self.global_build_dir = None
+ self.project_rules = ProjectRules()
+
+ # The project that is being loaded now
+ self.current_project = None
+
+ # The set of names of loaded project modules
+ self.jamfile_modules = {}
+
+ # Mapping from location to module name
+ self.location2module = {}
+
+ # Mapping from project id to project module
+ self.id2module = {}
+
+ # Map from Jamfile directory to parent Jamfile/Jamroot
+ # location.
+ self.dir2parent_jamfile = {}
+
+ # Map from directory to the name of Jamfile in
+ # that directory (or None).
+ self.dir2jamfile = {}
+
+ # Map from project module to attributes object.
+ self.module2attributes = {}
+
+ # Map from project module to target for the project
+ self.module2target = {}
+
+ self.saved_current_project = []
+
+ # FIXME: need to have a way to grab this
+ # from environment
+ # JAMROOT ?= [ peek : JAMROOT ] ;
+ self.JAMROOT = None
+
+ # Note the use of character groups, as opposed to listing
+ # 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate
+ # matches on windows and would have to eliminate duplicates.
+ if not self.JAMROOT:
+ self.JAMROOT = ["project-root.jam", "[Jj]amroot", "[Jj]amroot.jam"]
+
+ # Default patterns to search for the Jamfiles to use for build
+ # declarations.
+ #
+ # FIXME: need to grab from environment
+ #JAMFILE = [ modules.peek : JAMFILE ] ;
+ self.JAMFILE = None
+
+ if not self.JAMFILE:
+ self.JAMFILE = ["[Bb]uild.jam", "[Jj]amfile.v2", "[Jj]amfile",
+ "[Jj]amfile.jam"]
+
+
+ def load (self, jamfile_location):
+ """Loads jamfile at the given location. After loading, project global
+ file and jamfile needed by the loaded one will be loaded recursively.
+ If the jamfile at that location is loaded already, does nothing.
+ Returns the project module for the Jamfile."""
- # A map with all attributes. The key is the attribute name.
- self.attributes_ = {}
-
- self.attributes_['location'] = location
- self.attributes_['source-location'] = os.path.dirname(location)
- empty = property_set.empty ()
- self.attributes_['requirements'] = empty
- self.attributes_['usage-requirements'] = empty
-
- # TODO: For some reason, usage-requirements are a list, rather than a property set
- self.attributes_['default-build'] = []
-
- if parent:
- self.inherit(parent)
-
- def set (self, attribute, specification, exact = False):
- """ Set the named attribute from the specification given by the user.
- The value actually set may be different.
- exact: if True, sets the value from 'specification' without any processing.
- """
- if exact:
- self.attributes_ [attribute] = specification
- return
-
- if attribute == "requirements":
- specification = property.translate_paths (specification, self.location_)
- specification = property.expand_subfeatures_in_conditions (specification)
- specification = property.make (specification)
- result = property_set.create (specification)
-
- # If we have inherited properties, need to refine them with the
- # specified.
- if self.attributes_.has_key ('requirements'):
- result = self.attributes_ ['requirements'].refine (result)
-
- if "@error" in str (result):
- # TODO: check usage of result for error signalling
- raise BaseException ("Requirements for project at '%s'"
- "conflict with parent's.`n"
- "Explanation: %s" % (self.location_, result))
-
- else:
- self.attributes_ ['requirements'] = result
+ if "--debug-loading" in sys.argv:
+ print "Loading Jamfile at" '$(jamfile-location)'
- elif attribute == "usage-requirements":
- unconditional = []
- for p in specification:
- split = property.split_conditional (p)
- if not split:
- unconditional.append (p)
- else:
- unconditional.append (split [2])
- non_free = property.remove ('free', unconditional)
+ mname = self.module_name(jamfile_location)
+ # If Jamfile is already loaded, don't try again.
+ if not mname in self.jamfile_modules:
+
+ self.load_jamfile(jamfile_location)
+
+ # We want to make sure that child project are loaded only
+ # after parent projects. In particular, because parent projects
+ # define attributes whch are inherited by children, and we don't
+ # want children to be loaded before parents has defined everything.
+ #
+ # While "build-project" and "use-project" can potentially refer
+ # to child projects from parent projects, we don't immediately
+ # loading child projects when seing those attributes. Instead,
+ # we record the minimal information that will be used only later.
- if non_free:
- raise BaseException ("usage-requirements '%s' have non-free properties" % (specification, non_free))
-
- t = property.translate_paths (specification, self.location_)
-
- if self.attributes_.has_key ('usage-requirements'):
- self.attributes_ ['usage-requirements'] = property_set.create (
- self.get('usage-requirements').raw() + t)
-
- else:
- self.attributes_ ['usage-requirements'] = property_set.create (t)
+ self.load_used_projects(mname)
+
+ return mname
+
+ def load_used_projects(self, module_name):
+ ### FIXME:
+ # local used = [ modules.peek $(module-name) : .used-projects ] ;
+ used = []
+
+ location = self.attribute(module_name, "location")
+ while used:
+ id = used[0]
+ where = used[1]
+
+ use(id, os.path.join(location, where))
+ del used[0]
+ del used[1]
+
+ def load_parent(self, location):
+ """Loads parent of Jamfile at 'location'.
+ Issues an error if nothing is found."""
+
+ found = boost.build.util.path.glob_in_parents(
+ location, self.JAMROOT + self.JAMFILE)
+
+ if not found:
+ print "error: Could not find parent for project at '$(location)'"
+ print "error: Did not find Jamfile or project-root.jam in any parent directory."
+ sys.exit(1)
+
+ return load(os.path.dirname(found[0]))
- elif attribute == "default-build":
- self.attributes_ ['default-build'] = properties.make (specification)
+ def act_as_jamfile(self, module, location):
+ """Makes the specified 'module' act as if it were a regularly loaded Jamfile
+ at 'location'. If Jamfile is already located for that location, it's an
+ error."""
+
+ if self.module_name(location) in self.jamfile_modules:
+ # FIXME
+ print "Jamfile was already loaded for '$(location)'" ;
+ # errors.error "Jamfile was already loaded for '$(location)'" ;
+
+ # Set up non-default mapping from location to module.
+ self.location2module[location] = module
+
+ # Add the location to the list of project locations
+ # so that we don't try to load Jamfile in future
+ self.jamfile_modules.append(location)
+
+ self.initialize(module, location)
- elif attribute == "source-location":
- self.attributes_ ['source-location'] = path.root (path.make (specification), self.location_)
+ def find(self, name, current_location):
+ """Given 'name' which can be project-id or plain directory name,
+ return project module corresponding to that id or directory.
+ Returns nothing of project is not found."""
- elif attribute == "build-dir":
- self.attributes_ ['build-dir'] = path.root (specification, self.location_)
+ project_module = None
- elif not attribute in ["id", "default-build", "location", "source-location",
- "parent", "projects-to-build"]:
- raise BaseException ("Invalid project attribute '%s' specified for project at '%s'" % (attribute, self.location_))
+ # Try interpreting name as project id.
+ if name[0] == '/':
+ project_module = self.id2module.get(name)
+ if not project_module:
+ location = os.path.join(current_location, name)
+ # If no project is registered for the given location, try to
+ # load it. First see if we have Jamfile. If not we might have project
+ # root, willing to act as Jamfile. In that case, project-root
+ # must be placed in the directory referred by id.
+
+ project_module = self.module_name(location)
+ if not project_module in self.jamfile_modules and \
+ boost.build.util.path.glob(location, self.JAMROOT + self.JAMFILE):
+ project_module = self.load(location)
+
+ return project_module
+
+ def module_name(self, jamfile_location):
+ """Returns the name of module corresponding to 'jamfile-location'.
+ If no module corresponds to location yet, associates default
+ module name with that location."""
+ module = self.location2module.get(jamfile_location)
+ if not module:
+ # Root the path, so that locations are always umbiguious.
+ # Without this, we can't decide if '../../exe/program1' and '.'
+ # are the same paths, or not.
+ jamfile_location = os.path.realpath(
+ os.path.join(os.getcwd(), jamfile_location))
+ module = "Jamfile<%s>" % jamfile_location
+ self.location2module[jamfile_location] = module
+ return module
+
+ def find_jamfile (self, dir, parent_root=0, no_errors=0):
+ """Find the Jamfile at the given location. This returns the
+ exact names of all the Jamfiles in the given directory. The optional
+ parent-root argument causes this to search not the given directory
+ but the ones above it up to the directory given in it."""
+
+ # Glob for all the possible Jamfiles according to the match pattern.
+ #
+ jamfile_glob = None
+ if not parent_root:
+ parent = self.dir2parent_jamfile.get(dir)
+ if not parent:
+ parent = boost.build.util.path.glob_in_parents(dir,
+ self.JAMFILE)
+ self.dir2parent_jamfile[dir] = parent
+ jamfile_glob = parent
else:
- self.attributes_ [attribute] = specification
+ jamfile = self.dir2jamfile.get(dir)
+ if not jamfile:
+ jamfile = boost.build.util.path.glob(dir, self.JAMFILE)
+ self.dir2jamfile[dir] = jamfile
+ jamfile_glob = jamfile
+
+ if len(jamfile_glob):
+ # Multiple Jamfiles found in the same place. Warn about this.
+ # And ensure we use only one of them.
+ # As a temporary convenience measure, if there's Jamfile.v2 amount
+ # found files, suppress the warning and use it.
+ #
+ pattern = "(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)"
+ v2_jamfiles = [x for x in jamfile_glob if re.match(pattern, x)]
+ if len(v2_jamfiles) == 1:
+ jamfile_glob = v2_jamfiles
+ else:
+ print """warning: Found multiple Jamfiles at '%s'!
+Loading the first one: '%s'.""" % (dir, jamfile_glob[0])
+
+ # Could not find it, error.
+ if not no_errors and not jamfile_glob:
+ print "Unable to load Jamfile"
+ # FIXME:
+ #errors.error
+ #"Unable to load Jamfile." :
+ # "Could not find a Jamfile in directory '$(dir)'". :
+ # "Attempted to find it with pattern '"$(JAMFILE:J=" ")"'." :
+ # "Please consult the documentation at 'http://www.boost.org'." ;
- def get (self, attribute):
- if self.attributes_.has_key (attribute):
- return self.attributes_ [attribute]
+ return jamfile_glob[0]
+
+ def load_jamfile(self, dir):
+ """Load a Jamfile at the given directory. Returns nothing.
+ Will attempt to load the file as indicated by the JAMFILE patterns.
+ Effect of calling this rule twice with the same 'dir' is underfined."""
+
+ print "Loading jamfile for", dir
+
+ # See if the Jamfile is where it should be.
+
+ jamfile_to_load = boost.build.util.path.glob(dir, self.JAMROOT)
+ if not jamfile_to_load:
+ jamfile_to_load = self.find_jamfile(dir)
else:
- return ''
-
- def inherit(self, parent):
- self.attributes_['default-build'] = parent.get('default-build')
- self.attributes_['requirements'] = parent.get('requirements')
- self.attributes_['usage-requirements'] = parent.get('usage-requirements')
+ jamfile_to_load = jamfile_to_load[0]
- parent_build_dir = parent.get('build-dir')
- if parent_build_dir:
- # Have to compute relative path from parent dir to our dir
- # Convert both paths to absolute, since we cannot
- # find relative path from ".." to "."
+ print "Jamfile found at", jamfile_to_load
+
+ # The module of the jamfile.
+ dir = os.path.realpath(os.path.dirname(jamfile_to_load))
- parent_dir = path.root(parent.get('location'), path.pwd())
- our_dir = path.root(self.get('location'), path.pwd())
- self.attributes_['build-dir'] = os.path.join(
- parent_dir, path.relative(our_dir, parent_dir))
-
- def dump(self):
- for k in self.attributes_:
- print k, " : ", self.attributes_[k]
+ jamfile_module = self.module_name (dir)
+ # Initialize the jamfile module before loading.
+ #
+ self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load))
+
+ saved_project = self.current_project
+
+ # Now load the Jamfile in it's own context.
+ # Initialization might have load parent Jamfiles, which might have
+ # loaded the current Jamfile with use-project. Do a final check to make
+ # sure it's not loaded already.
+ if not jamfile_module in self.jamfile_modules:
+ self.jamfile_modules[jamfile_module] = 1
+
+ # FIXME:
+ # mark-as-user $(jamfile-module) ;
+
+ bjam.call("load", jamfile_module, jamfile_to_load)
+ basename = os.path.basename(jamfile_to_load)
+
+ # Now do some checks
+ if self.current_project != saved_project:
+ print "The value of self.current_project magically changed"
+ # FIXME:
+ #errors.error "The value of the .current-project variable"
+ #: "has magically changed after loading a Jamfile."
+ #: "This means some of the targets might be defined a the wrong project."
+ #: "after loading " $(jamfile-module)
+ #: "expected value " $(saved-project)
+ #: "actual value " $(.current-project)
+
+ if self.global_build_dir:
+ id = self.attribute(jamfile_module, "id")
+ project_root = self.attribute(jamfile_module, "project-root")
+ location = self.attribute(jamfile_module, "location")
+
+ if location and project_root == dir:
+ # This is Jamroot
+ if not id:
+ print "warning: the --build-dir option was specified"
+ print "warning: but Jamroot at '%s'" % dir
+ print "warning: specified no project id"
+ print "warning: the --build-dir option will be ignored"
+
+
+ def is_jamroot(self, basename):
+ match = [ pat for pat in self.JAMROOT if re.match(pat, basename)]
+ if match:
+ return 1
+ else:
+ return 0
-
-class ProjectModule:
- """Project abstraction.
-
- In V2/Jam, projects were identified by bjam module corresponding
- to the Jamfile. This design was moved to V2/Python, but should
- be revised after we've done with the porting. This class should
- be gone and we should be using ProjectTarget instead.
- """
-
- def __init__ (self, registry, location, parent):
-
- self.registry_ = registry
- manager = self.registry_.manager_
-
- parent_attributes = None
- parent_target = None
- if parent:
- parent_attributes = parent.attributes()
- parent_target = parent.target()
-
- self.attributes_ = ProjectAttributes (manager, location,
- parent_attributes)
-
- # The unambiguous name of the module, based on its location.
- self.module_name_ = None
-
- from targets import ProjectTarget
- self.target_ = ProjectTarget (self.module_name (),
- self,
- parent_target,
- self.attribute ('requirements'),
- [])
+ def initialize(self, module_name, location=None, basename=None):
+ """Initialize the module for a project.
- def attributes (self):
- """ Returns the project-attribute instance.
+ module-name is the name of the project module.
+ location is the location (directory) of the project to initialize.
+ If not specified, stanalone project will be initialized
"""
- return self.attributes_
-
- def attribute (self, attribute):
- """ Returns the value of the specified attribute.
- """
- return self.attributes_.get (attribute)
- def manager (self):
- return self.attributes_.manager_
+ if "--debug-loading" in sys.argv:
+ print "Initializing project '%s'" % module_name
- def target (self):
- return self.target_
+ # TODO: need to consider if standalone projects can do anything but defining
+ # prebuilt targets. If so, we need to give more sensible "location", so that
+ # source paths are correct.
+ if not location:
+ location = ""
- def module_name (self):
- if not self.module_name_:
- # Root the path, so that locations are always umbiguious.
- # Without this, we can't decide if '../../exe/program1' and '.'
- # are the same paths, or not.
- jamfile_location = path.root (self.attributes_.location_, path.pwd ())
- self.module_name_ = 'Jamfile<' + jamfile_location + '>'
+ location = boost.build.util.path.relpath(os.getcwd(), location)
- return self.module_name_
+ attributes = ProjectAttributes(location, module_name)
+ self.module2attributes[module_name] = attributes
+ if location:
+ attributes.set("source-location", location, exact=1)
+ else:
+ attributes.set("source-location", "", exact=1)
-class ProjectRegistry:
- """ A factory and a registry of project modules.
- """
- def __init__ (self, manager):
- self.manager_ = manager
+ attributes.set("requirements", property_set.empty(), exact=1)
+ attributes.set("usage-requirements", property_set.empty(), exact=1)
+ attributes.set("default-build", [], exact=1)
+ attributes.set("projects-to-build", [], exact=1)
+
+ self.project_rules.init_project(module_name)
+
+ jamroot = 0
+
+ parent_module = None;
+ if module_name == "site-config":
+ # No parent
+ pass
+ elif module_name == "user-config":
+ parent_module = "site_config"
+ elif location and not self.is_jamroot(basename):
+ # We search for parent/project-root only if jamfile was specified
+ # --- i.e
+ # if the project is not standalone.
+ parent_module = load_parent(location)
+ else:
+ # It's either jamroot, or standalone project.
+ # If it's jamroot, inherit from user-config.
+ if location:
+ parent_module = "user-config" ;
+ jamroot = 1 ;
+
+ # FIXME: do this for now until we port top-level code
+ if parent_module == "user-config":
+ parent_module = None
+
+ if parent_module:
+ self.inherit_attributes(module_name, parent_module)
+ attributes.set("parent-module", parent_module, exact=1)
+
+ if jamroot:
+ attributes.set("project-root", location, exact=1)
+
+ parent = None
+ if parent_module:
+ parent = self.target(parent_module)
+
+ if not self.module2target.has_key(module_name):
+ target = boost.build.build.targets.ProjectTarget(self.manager,
+ module_name, module_name, parent,
+ self.attribute(module_name,"requirements"),
+ # FIXME: why we need this?
+ self.attribute(module_name, "default-build"))
+ self.module2target[module_name] = target
+
+ self.current_project = self.target(module_name)
- # A map of all projects.
- self.projects_ = {}
- # A map from project id to ProjectModule instance
- self.id_to_module_ = {}
- # A map from project modules to the corresponding project target
- self.project_modules_to_targets_ = {}
-
- def create (self, location, parent=None):
- project = ProjectModule (self, location, parent)
-
- module_name = project.module_name ()
- if self.projects_.has_key (module_name):
- raise BaseException ("A project already exists in this location: '%s'" % module_name)
-
- self.projects_ [module_name] = project
+ def inherit_attributes(self, project_module, parent_module):
+ """Make 'project-module' inherit attributes of project
+ root and parent module."""
+
+ attributes = self.module2attributes[project_module]
+ pattributes = self.module2attributes[parent_module]
+
+ # Parent module might be locationless user-config.
+ # FIXME:
+ #if [ modules.binding $(parent-module) ]
+ #{
+ # $(attributes).set parent : [ path.parent
+ # [ path.make [ modules.binding $(parent-module) ] ] ] ;
+ # }
+
+ attributes.set("project-root", pattributes.get("project-root"), exact=1)
+ attributes.set("default-build", pattributes.get("default-build"))
+ attributes.set("requirements", pattributes.get("requirements"), exact=1)
+ attributes.set("usage-requirements",
+ pattributes.get("usage-requirements"), exact=1)
+
+ parent_build_dir = pattributes.get("build-dir")
+
+ if parent_build_dir:
+ # Have to compute relative path from parent dir to our dir
+ # Convert both paths to absolute, since we cannot
+ # find relative path from ".." to "."
+
+ pass
+ # FIXME:
+ #local location = [ attribute $(project-module) location ] ;
+ #local parent-location = [ attribute $(parent-module) location ] ;
+
+ #local pwd = [ path.pwd ] ;
+ #local parent-dir = [ path.root $(parent-location) $(pwd) ] ;
+ #local our-dir = [ path.root $(location) $(pwd) ] ;
+ #$(attributes).set build-dir : [ path.join $(parent-build-dir)
+ # [ path.relative $(our-dir) $(parent-dir) ] ] : exact ;
+
+ def register_id(self, id, module):
+ """Associate the given id with the given project module."""
+ self.id2module[id] = module
+
+ def current(self):
+ """Returns the project which is currently being loaded."""
+ return self.current_project
+
+ def push_current(self, project):
+ """Temporary changes the current project to 'project'. Should
+ be followed by 'pop-current'."""
+ self.saved_current_project.append(self.current_project)
+ self.current_project = project
+
+ def pop_current(self):
+ self.current_project = self.saved_current_project[-1]
+ del self.saved_current_project[-1]
+
+ def attributes(self, project):
+ """Returns the project-attribute instance for the
+ specified jamfile module."""
+ return self.module2attributes[project]
+
+ def attribute(self, project, attribute):
+ """Returns the value of the specified attribute in the
+ specified jamfile module."""
+ return self.module2attributes[project].get(attribute)
+
+ def target(self, project_module):
+ """Returns the project target corresponding to the 'project-module'."""
+ if not self.module2target[project_module]:
+ self.module2target[project_module] = \
+ ProjectTarget(project_module, project_module,
+ self.attribute(project_module, "requirements"))
+
+ return self.module2target[project_module]
+
+ def use(self, id, location):
+ # Use/load a project.
+ saved_project = self.current_project
+ project_module = self.load(location)
+ declared_id = self.attribute(project_module, "id")
+
+ if not declared_id or declared_id != id:
+ # The project at 'location' either have no id or
+ # that id is not equal to the 'id' parameter.
+ if self.id2module[id] and self.id2module[id] != project_module:
+ print "ERROR"
+ # FIXME
+ #errors.user-error
+ #"Attempt to redeclare already existing project id '$(id)'" ;
+ #}
+ self.id2module[id] = project_module
+
+ self.current_module = saved_project
+
+ def add_rule(self, name, callable):
+ """Makes rule 'name' available to all subsequently loaded Jamfiles.
+
+ Calling that rule wil relay to 'callable'."""
+ self.project_rules.add_rule(name, callable)
+
+# Defines a Boost.Build extension project. Such extensions usually
+# contain library targets and features that can be used by many people.
+# Even though extensions are really projects, they can be initialize as
+# a module would be with the "using" (project.project-rules.using)
+# mechanism.
+#rule extension ( id : options * : * )
+#{
+# # The caller is a standalone module for the extension.
+# local mod = [ CALLER_MODULE ] ;
+#
+# # We need to do the rest within the extension module.
+# module $(mod)
+# {
+# import path ;
+#
+# # Find the root project.
+# local root-project = [ project.current ] ;
+# root-project = [ $(root-project).project-module ] ;
+# while
+# [ project.attribute $(root-project) parent-module ] &&
+# [ project.attribute $(root-project) parent-module ] != user-config
+# {
+# root-project = [ project.attribute $(root-project) parent-module ] ;
+# }
+#
+# # Create the project data, and bring in the project rules
+# # into the module.
+# project.initialize $(__name__) :
+# [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ;
+#
+# # Create the project itself, i.e. the attributes.
+# # All extensions are created in the "/ext" project space.
+# project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+# local attributes = [ project.attributes $(__name__) ] ;
+#
+# # Inherit from the root project of whomever is defining us.
+# project.inherit-attributes $(__name__) : $(root-project) ;
+# $(attributes).set parent-module : $(root-project) : exact ;
+# }
+#}
+
+#rule glob-internal ( project : wildcards + : excludes * : rule-name )
+#{
+# local location = [ $(project).get source-location ] ;
+#
+# local result ;
+# local paths = [ path.$(rule-name) $(location)
+# : [ sequence.transform path.make : $(wildcards) ]
+# : [ sequence.transform path.make : $(excludes) ] ] ;
+# if $(wildcards:D) || $(rule-name) != glob
+# {
+# # The paths we've found are relative to current directory,
+# # but the names specified in sources list are assumed to
+# # be relative to source directory of the corresponding
+# # prject. So, just make the name absolute.
+# for local p in $(paths)
+# {
+# result += [ path.root $(p) [ path.pwd ] ] ;
+# }
+# }
+# else
+# {
+# # There were not directory in wildcard, so the files are all
+# # in the source directory of the project. Just drop the
+# # directory, instead of making paths absolute.
+# result = $(paths:D="") ;
+# }
+#
+# return $(result) ;
+#}
- return project
-
- def projects (self):
- return self.projects_.iteritems ()
+class ProjectAttributes:
+ """Class keeping all the attributes of a project.
-###################################################################
-# Still to port.
-# Original lines are prefixed with "# "
-#
-# import modules : peek poke ;
-# import numbers ;
-# import path ;
-# import sequence ;
-# import errors : error ;
-#
-# import print ;
-# import "class" : new ;
-# import errors ;
-# import assert ;
-# import property-set ;
-#
-# #
-# # Loads jamfile at the given location. After loading, project global
-# # file and jamfile needed by the loaded one will be loaded recursively.
-# # If the jamfile at that location is loaded already, does nothing.
-# # Returns the project module for the Jamfile.
-# #
-
-
-# # We want to make sure that child project are loaded only
-# # after parent projects. In particular, because parent projects
-# # define attributes whch are inherited by children, and we don't
-# # want children to be loaded before parents has defined everything.
-# # So, for "build-project" and "use-project" we only record relevant
-# # information, and actually load the mentioned projects after
-# # loading the current Jamfile.
-# for local p in [ attribute $(module_name) projects-to-build ]
-# {
-# load [ path.join $(jamfile-location) $(p) ] ;
-# }
-#
-# local used = [ modules.peek $(module_name) : .used-projects ] ;
-# import project ;
-# while $(used)
-# {
-# local id = $(used[1]) ;
-# local where = $(used[2]) ;
-#
-# project.use $(id) : [ path.root
-# [ path.make $(where) ] $(jamfile-location) ] ;
-# used = $(used[3-]) ;
-# }
-# }
-# return $(module_name) ;
-# }
-#
-
-
-
-# # Loads parent of Jamfile at 'location'. Issues an error if nothing is found.
-# rule load-parent ( location )
-# {
-# local found = [ path.glob-in-parents $(location) :
-# $(JAMROOT) $(JAMFILE) ] ;
-#
-# if ! $(found)
-# {
-# ECHO "error: Could not find parent for project at '$(location)'" ;
-# ECHO "error: Did not find Jamfile or project-root.jam in any parent directory." ;
-# EXIT ;
-# }
-#
-# return [ load $(found[1]:D) ] ;
-# }
-#
-# # Makes the specified 'module' act as if it were a regularly loaded Jamfile
-# # at 'location'. If Jamfile is already located for that location, it's an
-# # error.
-# rule act-as-jamfile ( module : location )
-# {
-# if [ module_name $(location) ] in $(.jamfile-modules)
-# {
-# errors.error "Jamfile was already loaded for '$(location)'" ;
-# }
-# # Set up non-default mapping from location to module.
-# .module.$(location) = $(module) ;
-#
-# # Add the location to the list of project locations
-# # so that we don't try to load Jamfile in future
-# .jamfile-modules += [ module_name $(location) ] ;
-#
-# initialize $(module) : $(location) ;
-# }
-#
-#
- # Given 'name' which can be project-id or plain directory name,
- # return project module corresponding to that id or directory.
- # Returns nothing of project is not found.
- def find (self, name, current_location):
- project_module = None
+ The standard attributes are 'id', "location", "project-root", "parent"
+ "requirements", "default-build", "source-location" and "projects-to-build".
+ """
- # Try interpreting name as project id.
- if path.is_rooted (name):
- project_module = self.id_to_module_.get (name, None)
-
- if not project_module:
- location = path.root (path.make (name), current_location)
+ def __init__(self, location, project_module):
+ self.location = location
+ self.project_module = project_module
+ self.attributes = {}
+ self.usage_requirements = None
+
+ def set(self, attribute, specification, exact):
+ """Set the named attribute from the specification given by the user.
+ The value actually set may be different."""
+
+ if exact:
+ self.__dict__[attribute] = specification
- raise Exception ('Cannot load Jamfiles yet')
- # If no project is registered for the given location, try to
- # load it. First see if we have Jamfile. If not we might have project
- # root, willing to act as Jamfile. In that case, project-root
- # must be placed in the directory referred by id.
+ elif attribute == "requirements":
+ try:
+ result = property_set.refine_from_user_input(
+ self.requirements, specification,
+ self.project_module, self.location)
+ except Exception, e:
+ print "Conflicting parent properties requirements", e.value
+ # FIXME:
+ #errors.error
+ # "Requirements for project at '$(self.location)'"
+ # "conflict with parent's." :
+ # "Explanation: " $(result[2-]) ;
+ self.requirements = result
-# project_module = [ module_name $(location) ] ;
-# if ! $(project_module) in $(.jamfile-modules)
-# {
-# if [ find-jamfile $(location) : no-error ]
-# {
-# project_module = [ load $(location) ] ;
-# }
-# else
-# {
-# project_module = ;
-# }
-# }
-# }
-#
-# return $(project_module) ;
-# }
-
-# #
-# # Returns the name of module corresponding to 'jamfile-location'.
-# # If no module corresponds to location yet, associates default
-# # module name with that location.
-# #
-# rule module_name ( jamfile-location )
-# {
-# if ! $(.module.$(jamfile-location))
-# {
-# # Root the path, so that locations are always umbiguious.
-# # Without this, we can't decide if '../../exe/program1' and '.'
-# # are the same paths, or not.
-# jamfile-location = [ path.root $(jamfile-location) [ path.pwd ] ] ;
-# .module.$(jamfile-location) = Jamfile<$(jamfile-location)> ;
-# }
-# return $(.module.$(jamfile-location)) ;
-# }
-#
-# # Default patterns to search for the Jamfiles to use for build
-# # declarations.
-# #
-# JAMFILE = [ modules.peek : JAMFILE ] ;
-
-#
-# # Find the Jamfile at the given location. This returns the exact names of
-# # all the Jamfiles in the given directory. The optional parent-root argument
-# # causes this to search not the given directory but the ones above it up
-# # to the directory given in it.
-# #
-
-
-
-# local rule find-jamfile (
-# dir # The directory(s) to look for a Jamfile.
-# parent-root ? # Optional flag indicating to search for the parent Jamfile.
-# : no-errors ?
-# )
-# {
-# # Glob for all the possible Jamfiles according to the match pattern.
-# #
-# local jamfile-glob = ;
-# if $(parent-root)
-# {
-# if ! $(.parent-jamfile.$(dir))
-# {
-# .parent-jamfile.$(dir) =
-# [ path.glob-in-parents $(dir) : $(JAMFILE) ] ;
-# }
-# jamfile-glob = $(.parent-jamfile.$(dir)) ;
-# }
-# else
-# {
-# if ! $(.jamfile.$(dir))
-# {
-# .jamfile.$(dir) = [ path.glob $(dir) : $(JAMFILE) ] ;
-# }
-# jamfile-glob = $(.jamfile.$(dir)) ;
-#
-# }
-#
-# local jamfile-to-load = $(jamfile-glob) ;
-# # Multiple Jamfiles found in the same place. Warn about this.
-# # And ensure we use only one of them.
-# # As a temporary convenience measure, if there's Jamfile.v2 amount
-# # found files, suppress the warning and use it.
-# #
-# if $(jamfile-to-load[2-])
-# {
-# local v2-jamfiles = [ MATCH (.*[Jj]amfile\\.v2) : $(jamfile-to-load) ] ;
-#
-# if $(v2-jamfiles) && ! $(v2-jamfiles[2])
-# {
-# jamfile-to-load = $(v2-jamfiles) ;
-# }
-# else
-# {
-# ECHO
-# "warning: Found multiple Jamfiles at '"$(dir)"'!"
-# "Loading the first one: '" [ path.basename $(jamfile-to-load[1]) ] "'." ;
-# }
-#
-# jamfile-to-load = $(jamfile-to-load[1]) ;
-# }
-#
-# # Could not find it, error.
-# #
-# if ! $(no-errors) && ! $(jamfile-to-load)
-# {
-# errors.error
-# "Unable to load Jamfile." :
-# "Could not find a Jamfile in directory '$(dir)'". :
-# "Attempted to find it with pattern '"$(JAMFILE:J=" ")"'." :
-# "Please consult the documentation at 'http://www.boost.org'." ;
-# }
-#
-# return $(jamfile-to-load) ;
-# }
-
-#
-# # Now load the Jamfile in it's own context.
-# # Initialization might have load parent Jamfiles, which might have
-# # loaded the current Jamfile with use-project. Do a final check to make
-# # sure it's not loaded already.
-# if ! $(jamfile-module) in $(.jamfile-modules)
-# {
-# .jamfile-modules += $(jamfile-module) ;
-# modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ] : . ;
-# if $(jamfile-to-load:BS) in $(JAMROOT)
-# {
-# jamfile = [ find-jamfile $(dir) : no-errors ] ;
-# # project-root.jam might have loaded some modules which used
-# # project.initialize, and clobbered .current-project
-# .current-project = [ target $(module_name) ] $(.current-project) ;
-# if $(jamfile)
-# {
-# load-aux $(jamfile-module) : [ path.native $(jamfile) ] ;
-# }
-# }
-# }
-# .current-project = $(.current-project[2-]) ;
-# }
-#
-#
-# rule load-aux ( module_name : file )
-# {
-# if USER_MODULE in [ RULENAMES ]
-# {
-# USER_MODULE $(module_name) ;
-# }
-#
-# module $(module_name)
-# {
-# include $(2) ;
-# local rules = [ RULENAMES $(1) ] ;
-# IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ;
-# }
-# }
-#
-#
-#
+ elif attribute == "usage-requirements":
+ unconditional = []
+ for p in specification:
+ split = property.split_conditional(p)
+ if split:
+ unconditional.append(split[1])
+ else:
+ unconditional.append(p)
-
+ non_free = property.remove("free", unconditional)
+ if non_free:
+ pass
+ # FIXME:
+ #errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ;
+
+ t = property.translate-paths(specification, self.location)
+
+ existing = self.__dict__.get("usage-requirements")
+ if existing:
+ new = property_set.create(existing.raw(), t)
+ else:
+ new = property_set.create(t)
+ self.__dict__["usage-requirements"] = new
+
+
+ elif attribute == "default-build":
+ self.__dict__["default-build"] = property.make(specification)
-# # Import rules common to all project modules from project-rules module,
-# # defined at the end of this file.
-# modules.clone-rules project-rules $(module_name) ;
-#
-# # We search for parent/project-root only if jamfile was specified --- i.e
-# # if the project is not standalone.
-# if $(location)
-# {
-
-# local parent-module ;
-# if ! $(basename) in $(JAMROOT)
-# {
-# parent-module = [ load-parent $(location) ] ;
-# inherit-attributes $(module_name) : $(parent-module) ;
-# }
-#
-# local parent ;
-# if $(parent-module)
-# {
-# parent = [ target $(parent-module) ] ;
-# }
-#
-# if ! $(.target.$(module_name))
-# {
-# .target.$(module_name) = [ new project-target $(module_name)
-# : $(module_name) $(parent)
-# : [ attribute $(module_name) requirements ] ] ;
-#
-# if --debug-loading in [ modules.peek : ARGV ]
-# {
-# ECHO "Assigned project target" $(.target.$(module_name))
-# "to '$(module_name)'" ;
-# }
-#
-#
-# }
-# }
-#
-# .current-project = [ target $(module_name) ] $(.current-project) ;
-# }
-#
-# # Make 'project_module' inherit attributes of project root and parent module.
-# rule inherit-attributes ( project_module : parent-module )
-# {
-# local attributes = $($(project_module).attributes) ;
-# local pattributes = [ attributes $(parent-module) ] ;
-# $(attributes).set parent : [ path.parent
-# [ path.make [ modules.binding $(parent-module) ] ] ] ;
-# $(attributes).set default-build
-# : [ $(pattributes).get default-build ] ;
-# $(attributes).set requirements
-# : [ $(pattributes).get requirements ] : exact ;
-# $(attributes).set usage-requirements
-# : [ $(pattributes).get usage-requirements ] : exact ;
-# local parent-build-dir = [ $(pattributes).get build-dir ] ;
-# if $(parent-build-dir)
-# {
-# # Have to compute relative path from parent dir to our dir
-# # Convert both paths to absolute, since we cannot
-# # find relative path from ".." to "."
-#
-# local location = [ attribute $(project_module) location ] ;
-# local parent-location = [ attribute $(parent-module) location ] ;
-#
-# local pwd = [ path.pwd ] ;
-# local parent-dir = [ path.root $(parent-location) $(pwd) ] ;
-# local our-dir = [ path.root $(location) $(pwd) ] ;
-# $(attributes).set build-dir : [ path.join $(parent-build-dir)
-# [ path.relative $(our-dir) $(parent-dir) ] ] : exact ;
-# }
-# }
-#
-#
- def register_id (self, id, project_module):
- """ Associate the given id with the given project module.
- """
- self.id_to_module_ [id] = project_module
+ elif attribute == "source-location":
+ source_location = []
+ for path in specification:
+ source_location += os.path.join(self.location, path)
+ self.__dict__["source-location"] = source_location
+
+ elif attribute == "build-dir":
+ self.__dict__["build-dir"] = os.path.join(self.location, specification)
+
+ elif not attribute in ["id", "default-build", "location",
+ "source-location", "parent",
+ "projects-to-build", "project-root"]:
+ pass
+ #errors.error "Invalid project attribute '$(attribute)' specified "
+ # "for project at '$(self.location)'" ;
+ else:
+ self.__dict__[attribute] = specification
-# # Returns the value of the given attribute.
-# rule get ( attribute )
-# {
-# return $(self.$(attribute)) ;
-# }
-#
-# # Prints the project attributes.
-# rule print ( )
-# {
-# local id = $(self.id) ; id ?= (none) ;
-# local parent = $(self.parent) ; parent ?= (none) ;
-# print.section "'"$(id)"'" ;
-# print.list-start ;
-# print.list-item "Parent project:" $(parent) ;
-# print.list-item "Requirements:" [ $(self.requirements).raw ] ;
-# print.list-item "Default build:" $(self.default-build) ;
-# print.list-item "Source location:" $(self.source-location) ;
-# print.list-item "Projects to build:"
-# [ sequence.insertion-sort $(self.projects-to-build) ] ;
-# print.list-end ;
-# }
-#
-# }
-#
-# # Returns the project which is currently being loaded
-# rule current ( )
-# {
-# return $(.current-project[1]) ;
-# }
-#
-# # Returns the project-attribute instance for the specified jamfile module.
-# rule attributes ( project )
-# {
-# return $($(project).attributes) ;
-# }
-#
-# # Returns the value of the specified attribute in the specified jamfile module.
-# rule attribute ( project attribute )
-# {
-# return [ $($(project).attributes).get $(attribute) ] ;
-# }
-#
-
-# def target (self, project_module):
-# """ Returns the project target corresponding to the 'project_module'.
-# """
-# id = str (project_module)
-# t = self.project_modules_to_targets_.get (id, None)
-#
-# if not t:
-# t = targets.ProjectTarget (id, project_module, None, project_module.attribute ('requirements'))
-# self.project_modules_to_targets_ [id] = t
-#
-# return t
+ def get(self, attribute):
+ return self.__dict__.get(attribute, [])
+
+ # FIXME
+ # Prints the project attributes.
+ #rule print ( )
+ #{
+ # local id = $(self.id) ; id ?= (none) ;
+ # local parent = $(self.parent) ; parent ?= (none) ;
+ # print.section "'"$(id)"'" ;
+ # print.list-start ;
+ # print.list-item "Parent project:" $(parent) ;
+ # print.list-item "Requirements:" [ $(self.requirements).raw ] ;
+ # print.list-item "Default build:" $(self.default-build) ;
+ # print.list-item "Source location:" $(self.source-location) ;
+ # print.list-item "Projects to build:"
+ # [ sequence.insertion-sort $(self.projects-to-build) ] ;
+ # print.list-end ;
+ #}
+
+class ProjectRules:
+ """Class keeping all rules that are made available to Jamfile."""
+
+ def __init__(self): pass
+
+ def add_rule(self, name, callable):
+ self.__class__.__dict__[name] = callable
+
+ def init_project(self, project_module):
+
+ names = [x for x in self.__class__.__dict__
+ if x not in ["__init__", "init_project", "add_rule"]]
+ for n in names:
+ # Using 'getattr' here gives us a bound method,
+ # while using self.__dict__[r] would give unbound one.
+ v = getattr(self, n)
+ if callable(v):
+ print "Importing '%s' to bjam" % n
+ bjam.import_rule(project_module, n, v)
-# # Use/load a project.
-# rule use ( id : location )
-# {
-# local saved-project = $(.current-project) ;
-# local project_module = [ project.load $(location) ] ;
-# local declared-id = [ project.attribute $(project_module) id ] ;
-#
-# if ! $(declared-id) || $(declared-id) != $(id)
-# {
-# # The project at 'location' either have no id or
-# # that id is not equal to the 'id' parameter.
-# if $($(id).jamfile-module)
-# && $($(id).jamfile-module) != $(project_module)
-# {
-# errors.user-error
-# "Attempt to redeclare already existing project id" ;
-# }
-# $(id).jamfile-module = $(project_module) ;
-# }
-# .current-project = $(saved-project) ;
-# }
-
-
-#
-# # This module defines rules common to all projects
-# module project-rules
-# {
-# # Make toolset.using accessible in project context
-# import toolset : using ;
-# EXPORT project-rules : using ;
-#
-# rule project ( id ? : options * : * )
-# {
-# import project ;
-# import path ;
-#
-# local attributes = [ project.attributes $(__name__) ] ;
-# if $(id)
-# {
-# id = [ path.root $(id) / ] ;
-# project.register-id $(id) : $(__name__) ;
-# $(attributes).set id : $(id) ;
-# }
-#
-# for n in 2 3 4 5 6 7 8 9
-# {
-# local option = $($(n)) ;
-# if $(option)
-# {
-# $(attributes).set $(option[1]) : $(option[2-]) ;
-# }
-# }
-# }
-#
-# # Declare and set a project global constant. Project global constants are
-# # normal variables but should not be changed. They are applied to every
-# # child Jamfile.
-# #
-# rule constant (
-# name # Variable name of the constant.
-# : value # Value of the constant.
-# )
-# {
-# import project ;
-# local p = [ project.target $(__name__) ] ;
-# $(p).add-constant $(name) : $(value) ;
-# }
-#
-# # Declare and set a project global constant, whose value is a path. The
-# # path is adjusted to be relative to the invocation directory. The given
-# # value path is taken to be either absolute, or relative to this project
-# # root.
-# rule path-constant (
-# name # Variable name of the constant.
-# : value # Value of the constant.
-# )
-# {
-# import project ;
-# local p = [ project.target $(__name__) ] ;
-# $(p).add-constant $(name) : $(value) : path ;
-# }
-#
-#
-# rule use-project ( id : where )
-# {
-# # See comment in 'load' for explanation.
-# .used-projects += $(id) $(where) ;
-# }
-#
-# rule build-project ( dir )
-# {
-# import project ;
-# local attributes = [ project.attributes $(__name__) ] ;
-#
-# local now = [ $(attributes).get projects-to-build ] ;
-# $(attributes).set projects-to-build : $(now) $(dir) ;
-# }
-#
-# rule explicit ( target-names * )
-# {
-# import project ;
-# local t = [ project.target $(__name__) ] ;
-# for local n in $(target-names)
-# {
-# $(t).mark-target-as-explicit $(n) ;
-# }
-# }
-#
-# rule glob ( wildcards + )
-# {
-# import path ;
-# import project ;
-#
-# local location = [ project.attribute $(__name__) source-location ] ;
-#
-# local result ;
-# local paths = [ path.glob $(location) : $(wildcards) ] ;
-# if $(wildcards:D)
-# {
-# # The paths we've found are relative to current directory,
-# # but the names specified in sources list are assumed to
-# # be relative to source directory of the corresponding
-# # prject. So, just make the name absolute.
-# for local p in $(paths)
-# {
-# result += [ path.root $(p) [ path.pwd ] ] ;
-# }
-# }
-# else
-# {
-# # There were not directory in wildcard, so the files are all
-# # in the source directory of the project. Just drop the
-# # directory, instead of making paths absolute.
-# result = $(paths:D="") ;
-# }
-#
-# return $(result) ;
-# }
-# }
-#
-#
-# local rule __test__ ( )
-# {
-# import assert ;
-# }
+ def foobar(self, param):
+ print "foobar called!"
+
+# FIXME
+## # This module defines rules common to all projects
+## module project-rules
+## {
+## rule using ( toolset-module : * )
+## {
+## import toolset ;
+## import modules ;
+## import project ;
+
+## # The module referred by 'using' can be placed in
+## # the same directory as Jamfile, and the user
+## # will expect the module to be found even though
+## # the directory is not in BOOST_BUILD_PATH.
+## # So temporary change the search path.
+## local x = [ modules.peek : BOOST_BUILD_PATH ] ;
+## local caller = [ modules.binding $(__name__) ] ;
+## modules.poke : BOOST_BUILD_PATH : $(caller:D) $(x) ;
+## toolset.using $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+## modules.poke : BOOST_BUILD_PATH : $(x) ;
+
+## # The above might have clobbered .current-project
+## # Restore the the right value.
+## modules.poke project : .current-project
+## : [ project.target $(__name__) ] ;
+## }
+
+## import modules ;
+
+## rule import ( * : * : * )
+## {
+## modules.import project ;
+
+## local caller = [ CALLER_MODULE ] ;
+## local saved = [ modules.peek project : .current-project ] ;
+## module $(caller)
+## {
+## modules.import $(1) : $(2) : $(3) ;
+## }
+## modules.poke project : .current-project : $(saved) ;
+## }
+
+
+## rule project ( id ? : options * : * )
+## {
+## import project ;
+## import path ;
+## import errors ;
+
+## local attributes = [ project.attributes $(__name__) ] ;
+## if $(id)
+## {
+## id = [ path.root $(id) / ] ;
+## project.register-id $(id) : $(__name__) ;
+## $(attributes).set id : $(id) ;
+## }
+
+## local explicit-build-dir ;
+
+## for n in 2 3 4 5 6 7 8 9
+## {
+## local option = $($(n)) ;
+## if $(option)
+## {
+## $(attributes).set $(option[1]) : $(option[2-]) ;
+## }
+## if $(option[1]) = "build-dir"
+## {
+## explicit-build-dir = [ path.make $(option[2-]) ] ;
+## }
+## }
+
+## # If '--build-dir' is specified, change the build dir for the project.
+## local global-build-dir =
+## [ modules.peek project : .global-build-dir ] ;
+
+## if $(global-build-dir)
+## {
+## local location = [ $(attributes).get location ] ;
+## # Project with empty location is 'standalone' project, like
+## # user-config, or qt. It has no build dir.
+## # If we try to set build dir for user-config, we'll then
+## # try to inherit it, with either weird, or wrong consequences.
+## if $(location) && $(location) = [ $(attributes).get project-root ]
+## {
+## # This is Jamroot.
+## if $(id)
+## {
+## if $(explicit-build-dir)
+## && [ path.is-rooted $(explicit-build-dir) ]
+## {
+## errors.user-error "Absolute directory specified via 'build-dir' project attribute"
+## : "Don't know how to combine that with the --build-dir option."
+## ;
+## }
+## # Strip the leading slash from id.
+## local rid = [ MATCH /(.*) : $(id) ] ;
+## local p = [ path.join
+## $(global-build-dir) $(rid) $(explicit-build-dir) ] ;
+
+## $(attributes).set build-dir : $(p) : exact ;
+## }
+## }
+## else
+## {
+## # Not Jamroot
+## if $(explicit-build-dir)
+## {
+## errors.user-error "When --build-dir is specified, the 'build-project'"
+## : "attribute is allowed only for top-level 'project' invocations" ;
+## }
+## }
+## }
+
+## }
+
+## # Declare and set a project global constant. Project global constants are
+## # normal variables but should not be changed. They are applied to every
+## # child Jamfile.
+## #
+## rule constant (
+## name # Variable name of the constant.
+## : value + # Value of the constant.
+## )
+## {
+## import project ;
+## local p = [ project.target $(__name__) ] ;
+## $(p).add-constant $(name) : $(value) ;
+## }
+
+## # Declare and set a project global constant, whose value is a path. The
+## # path is adjusted to be relative to the invocation directory. The given
+## # value path is taken to be either absolute, or relative to this project
+## # root.
+## rule path-constant (
+## name # Variable name of the constant.
+## : value + # Value of the constant.
+## )
+## {
+## import project ;
+## local p = [ project.target $(__name__) ] ;
+## $(p).add-constant $(name) : $(value) : path ;
+## }
+
+
+## rule use-project ( id : where )
+## {
+## # See comment in 'load' for explanation.
+## .used-projects += $(id) $(where) ;
+## }
+
+## rule build-project ( dir )
+## {
+## import project ;
+## local attributes = [ project.attributes $(__name__) ] ;
+
+## local now = [ $(attributes).get projects-to-build ] ;
+## $(attributes).set projects-to-build : $(now) $(dir) ;
+## }
+
+## rule explicit ( target-names * )
+## {
+## import project ;
+## # If 'explicit' is used in a helper rule defined in Jamroot,
+## # and inherited by children, then most of the time
+## # we want 'explicit' to operate on the Jamfile where
+## # the helper rule is invoked.
+## local t = [ project.current ] ;
+## for local n in $(target-names)
+## {
+## $(t).mark-target-as-explicit $(n) ;
+## }
+## }
+
+## rule glob ( wildcards + : excludes * )
+## {
+## import project ;
+## return [ project.glob-internal [ project.current ]
+## : $(wildcards) : $(excludes) : glob ] ;
+## }
+
+## rule glob-tree ( wildcards + : excludes * )
+## {
+## import project ;
+
+## if $(wildcards:D) || $(excludes:D)
+## {
+## errors.user-error "The patterns to 'glob-tree' may not include directory" ;
+## }
+## return [ project.glob-internal [ project.current ]
+## : $(wildcards) : $(excludes) : glob-tree ] ;
+## }
+
+## # Calculates conditional requirements for multiple requirements
+## # at once. This is a shorthand to be reduce duplication and to
+## # keep an inline declarative syntax. For example:
+## #
+## # lib x : x.cpp : [ conditional <toolset>gcc <variant>debug :
+## # <define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ;
+## #
+## rule conditional ( condition + : requirements * )
+## {
+## return $(condition:J=,):$(requirements) ;
+## }
+## }
+
+
+## local rule __test__ ( )
+## {
+## import assert ;
+## }
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/property.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/property.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/property.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -129,7 +129,7 @@
""" Interpret all path properties in 'properties' as relative to 'path'
The property values are assumed to be in system-specific form, and
will be translated into normalized form.
- """
+ """
result = []
for p in properties:
@@ -574,4 +574,4 @@
# feature.finish-test property-test-temp ;
# }
#
-
\ No newline at end of file
+
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/targets.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/targets.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/targets.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -123,7 +123,7 @@
project: Project where the main target is to be declared
"""
manager = project.manager ()
-
+
loc = project.get ('location')
requirements = property.translate_paths (specification, loc)
requirements = property.expand_subfeatures_in_conditions (requirements)
@@ -304,11 +304,11 @@
- The first time 'main-target' or 'has-main-target' rule is called,
all alternatives are enumerated an main targets are created.
"""
- def __init__ (self, name, project_module, parent_project, requirements, default_build):
- AbstractTarget.__init__ (self, name, self, project_module.manager ())
+ 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_ = project_module.attribute ('location')
+ self.location_ = manager.projects().attribute (project_module, 'location')
self.requirements_ = requirements
self.default_build_ = default_build
@@ -338,7 +338,8 @@
return self.project_module_
def get (self, attribute):
- return self.project_module_.attribute (attribute)
+ return self.manager().projects().attribute(
+ self.project_module_, attribute)
def build_dir (self):
if not self.build_dir_:
@@ -651,7 +652,7 @@
if not self.generated_.has_key (str (ps)):
rproperties = self.common_properties (ps, self.requirements_)
-
+
if self.manager ().logger ().on ():
self.manager ().logger ().log (__name__, "Common properties are '%s'" % str (rproperties.raw ()))
@@ -686,7 +687,7 @@
result = self.construct (self.name_, source_targets, rproperties)
gur = result [0]
- result = result [1]
+ result = result [1:]
s = self.create_subvariant (result, ps, source_targets, rproperties, usage_requirements)
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/type.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/type.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/build/type.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -279,8 +279,9 @@
import boost.build.build.project
def xpto (project, name, sources, requirements = [], default_build = None, usage_requirements = []):
return main_target_rule (type, project, name, sources, requirements, default_build, usage_requirements)
-
- boost.build.build.project.ProjectModule.__dict__ [main_rule_name] = xpto
+
+ # FIXME
+ #boost.build.build.project.ProjectModule.__dict__ [main_rule_name] = xpto
def type_to_rule_name (type):
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/main.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/main.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/main.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -19,14 +19,26 @@
sys.argv = argv
engine = Engine()
- manager = Manager(engine)
+
+
+ global_build_dir = None
+
+# FIXME:
+#.global-build-dir = [ MATCH --build-dir=(.*) : [ modules.peek : ARGV ] ] ;
+#if $(.global-build-dir)
+#{
+# # If the option is specified several times, take the last value.
+# .global-build-dir = [ path.make $(.global-build-dir[-1]) ] ;
+#}
+
+ manager = Manager(engine, global_build_dir)
boost.build.tools.common.init(manager)
- loader = ProjectLoader(manager.projects());
- project_here = loader.load('.')
+ project_here = manager.projects().load('.')
- result = project_here.target().generate(property_set.empty())
+ result = manager.projects().target(project_here).generate(
+ property_set.empty())
actual_targets = []
for t in result.targets():
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/manager.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/manager.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/manager.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -9,18 +9,24 @@
from boost.build.util.logger import NullLogger
from build import build_request, property_set, feature
+# To simplify implementation of tools level, we'll
+# have a global variable keeping the current manager.
+the_manager = None
+def get_manager():
+ return the_manager
+
class Manager:
""" This class is a facade to the Boost.Build system.
It serves as the root to access all data structures in use.
"""
- def __init__ (self, engine):
+ def __init__ (self, engine, global_build_dir):
""" Constructor.
engine: the build engine that will actually construct the targets.
"""
self.engine_ = engine
self.virtual_targets_ = VirtualTargetRegistry (self)
- self.projects_ = ProjectRegistry (self)
+ self.projects_ = ProjectRegistry (self, global_build_dir)
self.targets_ = TargetRegistry ()
self.logger_ = NullLogger ()
self.scanners_ = ScannerRegistry (self)
@@ -30,6 +36,9 @@
# Sometimes, objects are stored in properties, along with some grist.
# This map is used to store the value and return an id, which can be later on used to retriev it back.
self.object_map_ = {}
+
+ global the_manager
+ the_manager = self
def scanners (self):
return self.scanners_
@@ -95,4 +104,4 @@
actual_targets = []
for virtual_target in virtual_targets:
actual_targets.extend (virtual_target.actualize ())
-
\ No newline at end of file
+
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/builtin.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/builtin.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/builtin.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -114,6 +114,9 @@
feature.feature ('version', [], ['free'])
feature.feature ('location-prefix', [], ['free'])
+
+ feature.feature ('action', [], ['free'])
+
# The following features are incidental, since
# in themself they have no effect on build products.
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/common.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/common.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/common.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -572,3 +572,5 @@
engine.register_action ("common.MkDir1", 'mkdir "$(<)"')
engine.register_action ("common.Clean", 'rm -rf "$(>)"')
+
+ import boost.build.tools.make
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/make.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/make.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/tools/make.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -1,43 +1,52 @@
-# 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.
-
-""" This module defines the 'make' main target rule.
-"""
-import os
+# Copyright 2003 Dave Abrahams
+# Copyright 2003 Douglas Gregor
+# Copyright 2006 Rene Rivera
+# Copyright 2002, 2003, 2004, 2005, 2006 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 'make' main target rule.
+
from boost.build.build.targets import BasicTarget
-from boost.build.build.virtual_target import FileTarget, Action
-from boost.build.build import property_set, type
-from boost.build.util.utility import replace_suffix
-from boost.build.exceptions import *
-
-class MakeTarget (BasicTarget):
- def __init__ (self, project, name, sources, requirements, make_rule, default_build):
- BasicTarget.__init__ (self, name, project, sources, requirements, default_build)
-
- self.project_ = project
- self.make_rule_ = make_rule
-
- def construct (self, name, source_targets, ps):
- a = Action (self.manager_, source_targets, self.make_rule_, ps)
- t = FileTarget (self.name_, True, type.type (self.name_), self.project_, a)
-
- return (property_set.empty (), [ self.manager ().virtual_targets ().register (t) ])
-
-def make (project, target_name, sources, generating_rule, requirements):
- """ Declares the 'make' main target.
- """
- targets = project.manager ().targets ()
- engine = project.manager().engine()
+from boost.build.build.virtual_target import Action, FileTarget
+from boost.build.build import type
+from boost.build.manager import get_manager
+import boost.build.build.property_set
+
+class MakeTarget(BasicTarget):
+
+ def construct(self, name, source_targets, property_set):
+
+ action_name = property_set.get("<action>")[0]
+
+ action = Action(get_manager(), source_targets, action_name, property_set)
+ # FIXME: type.type uses global data.
+ target = FileTarget(self.name(), 1, type.type(self.name()),
+ self.project(), action)
+ return [ boost.build.build.property_set.empty(),
+ self.project().manager().virtual_targets().register(target)]
+
+# FIXME: should not have 'self' at all.
+def make (self, target_name, sources, generating_rule,
+ requirements=[], usage_requirements=[]):
+
+ target_name = target_name[0]
+ generating_rule = generating_rule[0]
+
+ requirements.append("<action>%s" % generating_rule)
+ m = get_manager()
+ targets = m.targets()
+ project = m.projects().current()
+ engine = m.engine()
engine.register_bjam_action(generating_rule)
-
- targets.main_target_alternative (MakeTarget (project.target (),
- target_name,
- targets.main_target_sources (sources, target_name),
- targets.main_target_requirements (requirements, project.target ()),
- generating_rule,
- targets.main_target_default_build ([], project.target ())))
-import boost.build.build.project
-boost.build.build.project.ProjectModule.__dict__ ['make'] = make
+ targets.main_target_alternative(MakeTarget(
+ target_name, project,
+ targets.main_target_sources(sources, target_name),
+ targets.main_target_requirements(requirements, project),
+ targets.main_target_default_build([], project),
+ targets.main_target_usage_requirements(usage_requirements, project)))
+
+get_manager().projects().add_rule("make", make)
+
+
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/util/path.py
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/util/path.py (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/boost/build/util/path.py 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -16,6 +16,7 @@
import os.path
from utility import to_seq
+from glob import glob as builtin_glob
def root (path, root):
""" If 'path' is relative, it is rooted at 'root'. Otherwise, it's unchanged.
@@ -805,3 +806,50 @@
# modules.poke path : os : $(save-os) ;
#
# }
+
+#
+
+
+def glob(dir, patterns):
+ result = []
+ for pattern in patterns:
+ result.extend(builtin_glob(os.path.join(dir, pattern)))
+ return result
+
+def glob_in_parents(dir, patterns, upper_limit=None):
+ result = []
+ absolute_dir = os.path.join(os.getcwd(), dir)
+
+ while absolute_dir:
+ result = glob(absolute_dir, patterns)
+ if result:
+ break
+ absolute_dir = os.path.dirname(absolute_dir)
+
+ return result
+
+
+# The relpath functionality is written by
+# Cimarron Taylor
+def pathsplit(p, rest=[]):
+ (h,t) = os.path.split(p)
+ if len(h) < 1: return [t]+rest
+ if len(t) < 1: return [h]+rest
+ return pathsplit(h,[t]+rest)
+
+def commonpath(l1, l2, common=[]):
+ if len(l1) < 1: return (common, l1, l2)
+ if len(l2) < 1: return (common, l1, l2)
+ if l1[0] != l2[0]: return (common, l1, l2)
+ return commonpath(l1[1:], l2[1:], common+[l1[0]])
+
+def relpath(p1, p2):
+ (common,l1,l2) = commonpath(pathsplit(p1), pathsplit(p2))
+ p = []
+ if len(l1) > 0:
+ p = [ '../' * len(l1) ]
+ p = p + l2
+ if p:
+ return os.path.join( *p )
+ else:
+ return "."
Modified: branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/tests/bjam/make/Jamroot
==============================================================================
--- branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/tests/bjam/make/Jamroot (original)
+++ branches/BOOST_BUILD_PYTHON/boost/tools/build/v2/python/tests/bjam/make/Jamroot 2007-10-08 14:48:17 EDT (Mon, 08 Oct 2007)
@@ -1,13 +1,14 @@
-make a : a.cpp : $(__name__).copy ;
+foobar param ;
-FOO = 1 ;
+make a : a.cpp : $(__name__).copy ;
rule copy ( targets : sources : properties * )
{
ECHO "XXXXXXXXXXX copy $(targets) : $(sources) : $(properties)" ;
}
+
actions copy
{
cp $(FOO) $(>) $(<)
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