diff options
author | Chris Dyer <cdyer@cs.cmu.edu> | 2012-10-11 14:06:32 -0400 |
---|---|---|
committer | Chris Dyer <cdyer@cs.cmu.edu> | 2012-10-11 14:06:32 -0400 |
commit | 9339c80d465545aec5a6dccfef7c83ca715bf11f (patch) | |
tree | 64c56d558331edad1db3832018c80e799551c39a /jam-files/boost-build/build/project.py | |
parent | 438dac41810b7c69fa10203ac5130d20efa2da9f (diff) | |
parent | afd7da3b2338661657ad0c4e9eec681e014d37bf (diff) |
Merge branch 'master' of https://github.com/redpony/cdec
Diffstat (limited to 'jam-files/boost-build/build/project.py')
-rw-r--r-- | jam-files/boost-build/build/project.py | 1120 |
1 files changed, 0 insertions, 1120 deletions
diff --git a/jam-files/boost-build/build/project.py b/jam-files/boost-build/build/project.py deleted file mode 100644 index 1e1e16fa..00000000 --- a/jam-files/boost-build/build/project.py +++ /dev/null @@ -1,1120 +0,0 @@ -# Status: ported. -# Base revision: 64488 - -# 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 '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 -# 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 -# it exists). After that, Jamfile is read. It can declare its own attributes, -# via 'project' rule, which will be combined with already set attributes. -# -# -# The 'project' rule can also declare project id, which will be associated with -# the project module. -# -# There can also be 'standalone' projects. They are created by calling 'initialize' -# on arbitrary module, and not specifying location. After the call, the module can -# call 'project' rule, declare main target and behave as regular projects. However, -# since it's not associated with any location, it's better declare only prebuilt -# 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 -# are not recorded, the only way to use them is by project id. - -import b2.util.path -from b2.build import property_set, property -from b2.build.errors import ExceptionWithUserContext -import b2.build.targets - -import bjam - -import re -import sys -import os -import string -import imp -import traceback -import b2.util.option as option - -from b2.util import record_jam_to_value_mapping, qualify_jam_action - -class ProjectRegistry: - - def __init__(self, manager, global_build_dir): - self.manager = manager - self.global_build_dir = global_build_dir - self.project_rules_ = ProjectRules(self) - - # The target corresponding to the project 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 = {} - - # Map from names to Python modules, for modules loaded - # via 'using' and 'import' rules in Jamfiles. - self.loaded_tool_modules_ = {} - - self.loaded_tool_module_path_ = {} - - # Map from project target to the list of - # (id,location) pairs corresponding to all 'use-project' - # invocations. - # TODO: should not have a global map, keep this - # in ProjectTarget. - self.used_projects = {} - - self.saved_current_project = [] - - self.JAMROOT = self.manager.getenv("JAMROOT"); - - # 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. - self.JAMFILE = self.manager.getenv("JAMFILE") - - 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.""" - - absolute = os.path.join(os.getcwd(), jamfile_location) - absolute = os.path.normpath(absolute) - jamfile_location = b2.util.path.relpath(os.getcwd(), absolute) - - if "--debug-loading" in self.manager.argv(): - print "Loading Jamfile at '%s'" % jamfile_location - - - 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, mname) - - # 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 - # load child projects when seing those attributes. Instead, - # we record the minimal information that will be used only later. - - self.load_used_projects(mname) - - return mname - - def load_used_projects(self, module_name): - # local used = [ modules.peek $(module-name) : .used-projects ] ; - used = self.used_projects[module_name] - - location = self.attribute(module_name, "location") - for u in used: - id = u[0] - where = u[1] - - self.use(id, os.path.join(location, where)) - - def load_parent(self, location): - """Loads parent of Jamfile at 'location'. - Issues an error if nothing is found.""" - - found = b2.util.path.glob_in_parents( - location, self.JAMROOT + self.JAMFILE) - - if not found: - print "error: Could not find parent for project at '%s'" % location - print "error: Did not find Jamfile or project-root.jam in any parent directory." - sys.exit(1) - - return self.load(os.path.dirname(found[0])) - - 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: - self.manager.errors()( - "Jamfile was already loaded for '%s'" % 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) - - 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.""" - - project_module = None - - # 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: - if b2.util.path.glob([location], self.JAMROOT + self.JAMFILE): - project_module = self.load(location) - else: - project_module = None - - 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 parent_root: - parent = self.dir2parent_jamfile.get(dir) - if not parent: - parent = b2.util.path.glob_in_parents(dir, - self.JAMFILE) - self.dir2parent_jamfile[dir] = parent - jamfile_glob = parent - else: - jamfile = self.dir2jamfile.get(dir) - if not jamfile: - jamfile = b2.util.path.glob([dir], self.JAMFILE) - self.dir2jamfile[dir] = jamfile - jamfile_glob = jamfile - - if len(jamfile_glob) > 1: - # 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'!""" % (dir) - for j in jamfile_glob: - print " -", j - print "Loading the first one" - - # Could not find it, error. - if not no_errors and not jamfile_glob: - self.manager.errors()( - """Unable to load Jamfile. -Could not find a Jamfile in directory '%s' -Attempted to find it with pattern '%s'. -Please consult the documentation at 'http://boost.org/boost-build2'.""" - % (dir, string.join(self.JAMFILE))) - - if jamfile_glob: - return jamfile_glob[0] - - def load_jamfile(self, dir, jamfile_module): - """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.""" - - # See if the Jamfile is where it should be. - is_jamroot = False - jamfile_to_load = b2.util.path.glob([dir], self.JAMROOT) - if not jamfile_to_load: - jamfile_to_load = self.find_jamfile(dir) - else: - if len(jamfile_to_load) > 1: - get_manager().errors()("Multiple Jamfiles found at '%s'\n" +\ - "Filenames are: %s" - % (dir, [os.path.basename(j) for j in jamfile_to_load])) - - is_jamroot = True - jamfile_to_load = jamfile_to_load[0] - - dir = os.path.dirname(jamfile_to_load) - if not dir: - dir = "." - - self.used_projects[jamfile_module] = [] - - # Now load the Jamfile in it's own context. - # The call to 'initialize' may load parent Jamfile, which might have - # 'use-project' statement that causes a second attempt to load the - # same project we're loading now. Checking inside .jamfile-modules - # prevents that second attempt from messing up. - if not jamfile_module in self.jamfile_modules: - self.jamfile_modules[jamfile_module] = True - - # Initialize the jamfile module before loading. - # - self.initialize(jamfile_module, dir, os.path.basename(jamfile_to_load)) - - saved_project = self.current_project - - bjam.call("load", jamfile_module, jamfile_to_load) - basename = os.path.basename(jamfile_to_load) - - if is_jamroot: - jamfile = self.find_jamfile(dir, no_errors=True) - if jamfile: - bjam.call("load", jamfile_module, jamfile) - - # Now do some checks - if self.current_project != saved_project: - self.manager.errors()( -"""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 %s -expected value %s -actual value %s""" % (jamfile_module, saved_project, self.current_project)) - - if self.global_build_dir: - id = self.attributeDefault(jamfile_module, "id", None) - 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: - # FIXME: go via errors module, so that contexts are - # shown? - 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 load_standalone(self, jamfile_module, file): - """Loads 'file' as standalone project that has no location - associated with it. This is mostly useful for user-config.jam, - which should be able to define targets, but although it has - some location in filesystem, we don't want any build to - happen in user's HOME, for example. - - The caller is required to never call this method twice on - the same file. - """ - - self.used_projects[jamfile_module] = [] - bjam.call("load", jamfile_module, file) - self.load_used_projects(jamfile_module) - - def is_jamroot(self, basename): - match = [ pat for pat in self.JAMROOT if re.match(pat, basename)] - if match: - return 1 - else: - return 0 - - def initialize(self, module_name, location=None, basename=None): - """Initialize the module for a project. - - 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 - """ - - if "--debug-loading" in self.manager.argv(): - print "Initializing project '%s'" % module_name - - # 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 = "" - - attributes = ProjectAttributes(self.manager, location, module_name) - self.module2attributes[module_name] = attributes - - python_standalone = False - if location: - attributes.set("source-location", [location], exact=1) - elif not module_name in ["test-config", "site-config", "user-config", "project-config"]: - # This is a standalone project with known location. Set source location - # so that it can declare targets. This is intended so that you can put - # a .jam file in your sources and use it via 'using'. Standard modules - # (in 'tools' subdir) may not assume source dir is set. - module = sys.modules[module_name] - attributes.set("source-location", self.loaded_tool_module_path_[module_name], exact=1) - python_standalone = True - - attributes.set("requirements", property_set.empty(), exact=True) - attributes.set("usage-requirements", property_set.empty(), exact=True) - attributes.set("default-build", property_set.empty(), exact=True) - attributes.set("projects-to-build", [], exact=True) - attributes.set("project-root", None, exact=True) - attributes.set("build-dir", None, exact=True) - - self.project_rules_.init_project(module_name, python_standalone) - - jamroot = False - - parent_module = None; - if module_name == "test-config": - # No parent - pass - elif module_name == "site-config": - parent_module = "test-config" - elif module_name == "user-config": - parent_module = "site-config" - elif module_name == "project-config": - parent_module = "user-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 = self.load_parent(location) - else: - # It's either jamroot, or standalone project. - # If it's jamroot, inherit from user-config. - if location: - # If project-config module exist, inherit from it. - if self.module2attributes.has_key("project-config"): - parent_module = "project-config" - else: - parent_module = "user-config" ; - - jamroot = True ; - - 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 = b2.build.targets.ProjectTarget(self.manager, - module_name, module_name, parent, - self.attribute(module_name,"requirements"), - # FIXME: why we need to pass this? It's not - # passed in jam code. - self.attribute(module_name, "default-build")) - self.module2target[module_name] = target - - self.current_project = self.target(module_name) - - 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=True) - attributes.set("default-build", pattributes.get("default-build"), exact=True) - attributes.set("requirements", pattributes.get("requirements"), exact=True) - 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 "." - - location = attributes.get("location") - parent_location = pattributes.get("location") - - our_dir = os.path.join(os.getcwd(), location) - parent_dir = os.path.join(os.getcwd(), parent_location) - - build_dir = os.path.join(parent_build_dir, - os.path.relpath(our_dir, parent_dir)) - attributes.set("build-dir", build_dir, exact=True) - - 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 set_current(self, c): - self.current_project = c - - 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) - try: - return self.module2attributes[project].get(attribute) - except: - raise BaseException("No attribute '%s' for project" % (attribute, project)) - - def attributeDefault(self, project, attribute, default): - """Returns the value of the specified attribute in the - specified jamfile module.""" - return self.module2attributes[project].getDefault(attribute, default) - - def target(self, project_module): - """Returns the project target corresponding to the 'project-module'.""" - if not self.module2target.has_key(project_module): - self.module2target[project_module] = \ - b2.build.targets.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.attributeDefault(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.has_key(id) and self.id2module[id] != project_module: - self.manager.errors()( -"""Attempt to redeclare already existing project id '%s' at location '%s'""" % (id, location)) - 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) - - def project_rules(self): - return self.project_rules_ - - def glob_internal(self, project, wildcards, excludes, rule_name): - location = project.get("source-location")[0] - - result = [] - callable = b2.util.path.__dict__[rule_name] - - paths = callable([location], wildcards, excludes) - has_dir = 0 - for w in wildcards: - if os.path.dirname(w): - has_dir = 1 - break - - if has_dir or rule_name != "glob": - result = [] - # 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. Either translate them or make absolute. - - for p in paths: - rel = os.path.relpath(p, location) - # If the path is below source location, use relative path. - if not ".." in rel: - result.append(rel) - else: - # Otherwise, use full path just to avoid any ambiguities. - result.append(os.path.abspath(p)) - - 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 = [os.path.basename(p) for p in paths] - - return result - - def load_module(self, name, extra_path=None): - """Load a Python module that should be useable from Jamfiles. - - There are generally two types of modules Jamfiles might want to - use: - - Core Boost.Build. Those are imported using plain names, e.g. - 'toolset', so this function checks if we have module named - b2.package.module already. - - Python modules in the same directory as Jamfile. We don't - want to even temporary add Jamfile's directory to sys.path, - since then we might get naming conflicts between standard - Python modules and those. - """ - - # See if we loaded module of this name already - existing = self.loaded_tool_modules_.get(name) - if existing: - return existing - - # See if we have a module b2.whatever.<name>, where <name> - # is what is passed to this function - modules = sys.modules - for class_name in modules: - parts = class_name.split('.') - if name is class_name or parts[0] == "b2" \ - and parts[-1] == name.replace("-", "_"): - module = modules[class_name] - self.loaded_tool_modules_[name] = module - return module - - # Lookup a module in BOOST_BUILD_PATH - path = extra_path - if not path: - path = [] - path.extend(self.manager.boost_build_path()) - location = None - for p in path: - l = os.path.join(p, name + ".py") - if os.path.exists(l): - location = l - break - - if not location: - self.manager.errors()("Cannot find module '%s'" % name) - - mname = name + "__for_jamfile" - file = open(location) - try: - # TODO: this means we'll never make use of .pyc module, - # which might be a problem, or not. - self.loaded_tool_module_path_[mname] = location - module = imp.load_module(mname, file, os.path.basename(location), - (".py", "r", imp.PY_SOURCE)) - self.loaded_tool_modules_[name] = module - return module - finally: - file.close() - - - -# FIXME: -# 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 ; -# } -#} - - -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, project_module): - self.manager = manager - self.location = location - self.project_module = project_module - self.attributes = {} - self.usage_requirements = None - - 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.""" - - if exact: - self.__dict__[attribute] = specification - - elif attribute == "requirements": - self.requirements = property_set.refine_from_user_input( - self.requirements, specification, - self.project_module, self.location) - - 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: - get_manager().errors()("usage-requirements %s have non-free properties %s" \ - % (specification, non_free)) - - t = property.translate_paths( - property.create_from_strings(specification, allow_condition=True), - self.location) - - existing = self.__dict__.get("usage-requirements") - if existing: - new = property_set.create(existing.all() + t) - else: - new = property_set.create(t) - self.__dict__["usage-requirements"] = new - - - elif attribute == "default-build": - self.__dict__["default-build"] = property_set.create(specification) - - elif attribute == "source-location": - source_location = [] - for path in specification: - source_location.append(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[0]) - - elif attribute == "id": - id = specification[0] - if id[0] != '/': - id = "/" + id - self.manager.projects().register_id(id, self.project_module) - self.__dict__["id"] = id - - elif not attribute in ["default-build", "location", - "source-location", "parent", - "projects-to-build", "project-root"]: - self.manager.errors()( -"""Invalid project attribute '%s' specified -for project at '%s'""" % (attribute, self.location)) - else: - self.__dict__[attribute] = specification - - def get(self, attribute): - return self.__dict__[attribute] - - def getDefault(self, attribute, default): - return self.__dict__.get(attribute, default) - - def dump(self): - """Prints the project attributes.""" - id = self.get("id") - if not id: - id = "(none)" - else: - id = id[0] - - parent = self.get("parent") - if not parent: - parent = "(none)" - else: - parent = parent[0] - - print "'%s'" % id - print "Parent project:%s", parent - print "Requirements:%s", self.get("requirements") - print "Default build:%s", string.join(self.get("debuild-build")) - print "Source location:%s", string.join(self.get("source-location")) - print "Projects to build:%s", string.join(self.get("projects-to-build").sort()); - -class ProjectRules: - """Class keeping all rules that are made available to Jamfile.""" - - def __init__(self, registry): - self.registry = registry - self.manager_ = registry.manager - self.rules = {} - self.local_names = [x for x in self.__class__.__dict__ - if x not in ["__init__", "init_project", "add_rule", - "error_reporting_wrapper", "add_rule_for_type", "reverse"]] - self.all_names_ = [x for x in self.local_names] - - def _import_rule(self, bjam_module, name, callable): - if hasattr(callable, "bjam_signature"): - bjam.import_rule(bjam_module, name, self.make_wrapper(callable), callable.bjam_signature) - else: - bjam.import_rule(bjam_module, name, self.make_wrapper(callable)) - - - def add_rule_for_type(self, type): - rule_name = type.lower().replace("_", "-") - - def xpto (name, sources = [], requirements = [], default_build = [], usage_requirements = []): - return self.manager_.targets().create_typed_target( - type, self.registry.current(), name[0], sources, - requirements, default_build, usage_requirements) - - self.add_rule(rule_name, xpto) - - def add_rule(self, name, callable): - self.rules[name] = callable - self.all_names_.append(name) - - # Add new rule at global bjam scope. This might not be ideal, - # added because if a jamroot does 'import foo' where foo calls - # add_rule, we need to import new rule to jamroot scope, and - # I'm lazy to do this now. - self._import_rule("", name, callable) - - def all_names(self): - return self.all_names_ - - def call_and_report_errors(self, callable, *args, **kw): - result = None - try: - self.manager_.errors().push_jamfile_context() - result = callable(*args, **kw) - except ExceptionWithUserContext, e: - e.report() - except Exception, e: - try: - self.manager_.errors().handle_stray_exception (e) - except ExceptionWithUserContext, e: - e.report() - finally: - self.manager_.errors().pop_jamfile_context() - - return result - - def make_wrapper(self, callable): - """Given a free-standing function 'callable', return a new - callable that will call 'callable' and report all exceptins, - using 'call_and_report_errors'.""" - def wrapper(*args, **kw): - return self.call_and_report_errors(callable, *args, **kw) - return wrapper - - def init_project(self, project_module, python_standalone=False): - - if python_standalone: - m = sys.modules[project_module] - - for n in self.local_names: - if n != "import_": - setattr(m, n, getattr(self, n)) - - for n in self.rules: - setattr(m, n, self.rules[n]) - - return - - for n in self.local_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): - if n == "import_": - n = "import" - else: - n = string.replace(n, "_", "-") - - self._import_rule(project_module, n, v) - - for n in self.rules: - self._import_rule(project_module, n, self.rules[n]) - - def project(self, *args): - - jamfile_module = self.registry.current().project_module() - attributes = self.registry.attributes(jamfile_module) - - id = None - if args and args[0]: - id = args[0][0] - args = args[1:] - - if id: - attributes.set('id', [id]) - - explicit_build_dir = None - for a in args: - if a: - attributes.set(a[0], a[1:], exact=0) - if a[0] == "build-dir": - explicit_build_dir = a[1] - - # If '--build-dir' is specified, change the build dir for the project. - if self.registry.global_build_dir: - - 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 and location == attributes.get("project-root"): - # Re-read the project id, since it might have been changed in - # the project's attributes. - id = attributes.get('id') - - # This is Jamroot. - if id: - if explicit_build_dir and os.path.isabs(explicit_build_dir): - self.registry.manager.errors()( -"""Absolute directory specified via 'build-dir' project attribute -Don't know how to combine that with the --build-dir option.""") - - rid = id - if rid[0] == '/': - rid = rid[1:] - - p = os.path.join(self.registry.global_build_dir, rid) - if explicit_build_dir: - p = os.path.join(p, explicit_build_dir) - attributes.set("build-dir", p, exact=1) - elif explicit_build_dir: - self.registry.manager.errors()( -"""When --build-dir is specified, the 'build-dir' -attribute is allowed only for top-level 'project' invocations""") - - def constant(self, name, value): - """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.""" - m = "Jamfile</home/ghost/Work/Boost/boost-svn/tools/build/v2_python/python/tests/bjam/make>" - self.registry.current().add_constant(name[0], value) - - def path_constant(self, 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.""" - if len(value) > 1: - self.registry.manager.error()("path constant should have one element") - self.registry.current().add_constant(name[0], value[0], path=1) - - def use_project(self, id, where): - # See comment in 'load' for explanation why we record the - # parameters as opposed to loading the project now. - m = self.registry.current().project_module(); - self.registry.used_projects[m].append((id[0], where[0])) - - def build_project(self, dir): - assert(isinstance(dir, list)) - jamfile_module = self.registry.current().project_module() - attributes = self.registry.attributes(jamfile_module) - now = attributes.get("projects-to-build") - attributes.set("projects-to-build", now + dir, exact=True) - - def explicit(self, target_names): - self.registry.current().mark_targets_as_explicit(target_names) - - def always(self, target_names): - self.registry.current().mark_targets_as_alays(target_names) - - def glob(self, wildcards, excludes=None): - return self.registry.glob_internal(self.registry.current(), - wildcards, excludes, "glob") - - def glob_tree(self, wildcards, excludes=None): - bad = 0 - for p in wildcards: - if os.path.dirname(p): - bad = 1 - - if excludes: - for p in excludes: - if os.path.dirname(p): - bad = 1 - - if bad: - self.registry.manager.errors()( -"The patterns to 'glob-tree' may not include directory") - return self.registry.glob_internal(self.registry.current(), - wildcards, excludes, "glob_tree") - - - def using(self, toolset, *args): - # 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. - current = self.registry.current() - location = current.get('location') - - m = self.registry.load_module(toolset[0], [location]) - if not m.__dict__.has_key("init"): - self.registry.manager.errors()( - "Tool module '%s' does not define the 'init' method" % toolset[0]) - m.init(*args) - - # The above might have clobbered .current-project. Restore the correct - # value. - self.registry.set_current(current) - - def import_(self, name, names_to_import=None, local_names=None): - - name = name[0] - py_name = name - if py_name == "os": - py_name = "os_j" - jamfile_module = self.registry.current().project_module() - attributes = self.registry.attributes(jamfile_module) - location = attributes.get("location") - - saved = self.registry.current() - - m = self.registry.load_module(py_name, [location]) - - for f in m.__dict__: - v = m.__dict__[f] - f = f.replace("_", "-") - if callable(v): - qn = name + "." + f - self._import_rule(jamfile_module, qn, v) - record_jam_to_value_mapping(qualify_jam_action(qn, jamfile_module), v) - - - if names_to_import: - if not local_names: - local_names = names_to_import - - if len(names_to_import) != len(local_names): - self.registry.manager.errors()( -"""The number of names to import and local names do not match.""") - - for n, l in zip(names_to_import, local_names): - self._import_rule(jamfile_module, l, m.__dict__[n]) - - self.registry.set_current(saved) - - def conditional(self, condition, requirements): - """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 ] ; - """ - - c = string.join(condition, ",") - if c.find(":") != -1: - return [c + r for r in requirements] - else: - return [c + ":" + r for r in requirements] - - def option(self, name, value): - name = name[0] - if not name in ["site-config", "user-config", "project-config"]: - get_manager().errors()("The 'option' rule may be used only in site-config or user-config") - - option.set(name, value[0]) |