summaryrefslogtreecommitdiff
path: root/tools/build/src/build/project.jam
diff options
context:
space:
mode:
Diffstat (limited to 'tools/build/src/build/project.jam')
-rw-r--r--tools/build/src/build/project.jam1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/tools/build/src/build/project.jam b/tools/build/src/build/project.jam
new file mode 100644
index 0000000000..c9a090982c
--- /dev/null
+++ b/tools/build/src/build/project.jam
@@ -0,0 +1,1228 @@
+# 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 copy at
+# 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 lives.
+# - an instance of 'project-attributes' class.
+# (given a module name, can be obtained using the 'attributes' rule)
+# - an instance of 'project-target' class (from targets.jam)
+# (given a module name, can be obtained using the 'target' rule)
+#
+# Typically, projects are created as result of loading a Jamfile, which is done
+# by rules 'load' and 'initialize', below. First, a module is prepared and a new
+# project-attributes instance is created. Some rules necessary for all projects
+# are added to the module (see the 'project-rules' module). Default project
+# attributes are set (inheriting parent project attributes, if it exists). After
+# that the Jamfile is read. It can declare its own attributes using the
+# 'project' rule which will be combined with any already set.
+#
+# The 'project' rule can also declare a project id which will be associated with
+# the project module.
+#
+# Besides Jamfile projects, we also support 'standalone' projects created by
+# calling 'initialize' in an arbitrary module and not specifying the project's
+# location. After the call, the module can call the 'project' rule, declare main
+# targets and behave as a regular project except that, since it is not
+# associated with any location, it should only declare prebuilt targets.
+#
+# The list of all loaded Jamfiles is stored in the .project-locations variable.
+# It is possible to obtain a module name for a location using the 'module-name'
+# rule. Standalone projects are not recorded and can only be referenced using
+# their project id.
+
+import "class" : new ;
+import modules ;
+import path ;
+import print ;
+import property-set ;
+import sequence ;
+
+
+.debug-loading = [ MATCH ^(--debug-loading)$ : [ modules.peek : ARGV ] ] ;
+
+
+# Loads the Jamfile at the given location. After loading, project global file
+# and Jamfiles needed by the requested one will be loaded recursively. If the
+# Jamfile at that location is loaded already, does nothing. Returns the project
+# module for the Jamfile.
+#
+rule load ( jamfile-location )
+{
+ local module-name = [ module-name $(jamfile-location) ] ;
+ # If Jamfile is already loaded, do not try again.
+ if ! $(module-name) in $(.jamfile-modules)
+ {
+ if $(.debug-loading)
+ {
+ ECHO Loading Jamfile at '$(jamfile-location)' ;
+ }
+
+ load-jamfile $(jamfile-location) : $(module-name) ;
+
+ # We want to make sure that child project are loaded only after parent
+ # projects. In particular, because parent projects define attributes
+ # which are then inherited by children, and we do not want children to
+ # be loaded before parent has defined everything.
+ #
+ # While "build-project" and "use-project" can potentially refer to child
+ # projects from parent projects, we do not immediately load child
+ # projects when seeing those attributes. Instead, we record the minimal
+ # information to be used only later.
+ load-used-projects $(module-name) ;
+ }
+ return $(module-name) ;
+}
+
+
+rule load-used-projects ( module-name )
+{
+ local used = [ modules.peek $(module-name) : .used-projects ] ;
+ local location = [ attribute $(module-name) location ] ;
+ while $(used)
+ {
+ local id = $(used[1]) ;
+ local where = [ path.make $(used[2]) ] ;
+ register-id $(id) : [ load [ path.root $(where) $(location) ] ] ;
+ used = $(used[3-]) ;
+ }
+}
+
+
+# Note the use of character groups, as opposed to listing 'Jamroot' and
+# 'jamroot'. With the latter, we would get duplicate matches on Windows and
+# would have to eliminate duplicates.
+JAMROOT ?= [ modules.peek : JAMROOT ] ;
+JAMROOT ?= project-root.jam [Jj]amroot [Jj]amroot.jam ;
+
+
+# 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)
+ {
+ import errors ;
+ errors.error Could not find parent "for" project at '$(location)' :
+ Did not find Jamfile.jam or Jamroot.jam "in" any parent directory. ;
+ }
+ return [ load $(found[1]:D) ] ;
+}
+
+
+# Returns the project module corresponding to the given project-id or plain
+# directory name. Returns nothing if such a project can not be found.
+#
+rule find ( name : current-location )
+{
+ local project-module ;
+
+ # Try interpreting name as project id.
+ if [ path.is-rooted $(name) ]
+ {
+ project-module = $($(name).jamfile-module) ;
+ }
+
+ if ! $(project-module)
+ {
+ local location = [ path.root [ path.make $(name) ] $(current-location) ]
+ ;
+
+ # If no project is registered for the given location, try to load it.
+ # First see if we have a Jamfile. If not, then see if we might have a
+ # project root willing to act as a Jamfile. In that case, project root
+ # must be placed in the directory referred to by id.
+
+ project-module = [ module-name $(location) ] ;
+ if ! $(project-module) in $(.jamfile-modules)
+ {
+ if [ path.glob $(location) : $(JAMROOT) $(JAMFILE) ]
+ {
+ project-module = [ load $(location) ] ;
+ }
+ else
+ {
+ project-module = ;
+ }
+ }
+ }
+
+ return $(project-module) ;
+}
+
+
+# Returns the name of the module corresponding to 'jamfile-location'. If no
+# module corresponds to that location yet, associates the default module name
+# with that location.
+#
+rule module-name ( jamfile-location )
+{
+ if ! $(.module.$(jamfile-location))
+ {
+ # Root the path, so that locations are always unambiguous. Without this,
+ # we can not decide if '../../exe/program1' and '.' are the same paths.
+ local normalized = [ path.root $(jamfile-location) [ path.pwd ] ] ;
+
+ # Quick & dirty fix to get the same module name when we supply two
+ # equivalent location paths, e.g. 'd:\Foo' & 'D:\fOo\bar\..' on Windows.
+ # Note that our current implementation will not work correctly if the
+ # given location references an empty folder, but in that case any later
+ # attempt to load a Jamfile from this location will fail anyway.
+ # FIXME: Implement this cleanly. Support for this type of path
+ # normalization already exists internally in Boost Jam and the current
+ # fix relies on the GLOB builtin rule using that support. Most likely we
+ # just need to add a new builtin rule to do this explicitly.
+ normalized = [ NORMALIZE_PATH $(normalized) ] ;
+ local glob-result = [ GLOB [ path.native $(normalized) ] : * ] ;
+ if $(glob-result)
+ {
+ normalized = $(glob-result[1]:D) ;
+ }
+ .module.$(jamfile-location) = Jamfile<$(normalized)> ;
+ }
+ return $(.module.$(jamfile-location)) ;
+}
+
+
+# Default patterns to search for the Jamfiles to use for build declarations.
+#
+JAMFILE = [ modules.peek : JAMFILE ] ;
+JAMFILE ?= [Bb]uild.jam [Jj]amfile.v2 [Jj]amfile [Jj]amfile.jam ;
+
+
+# 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
+# parent-root directory.
+#
+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 is
+ # Jamfile.v2 among found files, suppress the warning and use it.
+ #
+ if $(jamfile-to-load[2-])
+ {
+ local v2-jamfiles = [ MATCH ^(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)$ :
+ $(jamfile-to-load) ] ;
+
+ if $(v2-jamfiles) && ! $(v2-jamfiles[2])
+ {
+ jamfile-to-load = $(v2-jamfiles) ;
+ }
+ else
+ {
+ local jamfile = [ path.basename $(jamfile-to-load[1]) ] ;
+ ECHO "warning: Found multiple Jamfiles at '"$(dir)"'!"
+ "Loading the first one: '$(jamfile)'." ;
+ }
+
+ jamfile-to-load = $(jamfile-to-load[1]) ;
+ }
+
+ # Could not find it, error.
+ #
+ if ! $(no-errors) && ! $(jamfile-to-load)
+ {
+ import errors ;
+ 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) ;
+}
+
+
+# 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 undefined.
+#
+local rule load-jamfile ( dir : jamfile-module )
+{
+ # See if the Jamfile is where it should be.
+ #
+ local jamfile-to-load = [ path.glob $(dir) : $(JAMROOT) ] ;
+ if ! $(jamfile-to-load)
+ {
+ jamfile-to-load = [ find-jamfile $(dir) ] ;
+ }
+
+ if $(jamfile-to-load[2])
+ {
+ import errors ;
+ errors.error "Multiple Jamfiles found at '$(dir)'" :
+ "Filenames are: " $(jamfile-to-load:D=) ;
+ }
+
+ # Now load the Jamfile in its own context.
+ # The call to 'initialize' may load the parent Jamfile, which might contain
+ # a 'use-project' or a 'project.load' call, causing a second attempt to load
+ # the same project we are loading now. Checking inside .jamfile-modules
+ # prevents that second attempt from messing things up.
+ if ! $(jamfile-module) in $(.jamfile-modules)
+ {
+ local previous-project = $(.current-project) ;
+
+ # Initialize the Jamfile module before loading.
+ initialize $(jamfile-module) : [ path.parent $(jamfile-to-load) ] :
+ $(jamfile-to-load:BS) ;
+
+ if ! $(jamfile-module) in $(.jamfile-modules)
+ {
+ .jamfile-modules += $(jamfile-module) ;
+
+ local saved-project = $(.current-project) ;
+
+ mark-as-user $(jamfile-module) ;
+ modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ]
+ : . ;
+ if [ MATCH ^($(JAMROOT))$ : $(jamfile-to-load:BS) ]
+ {
+ jamfile = [ find-jamfile $(dir) : no-errors ] ;
+ if $(jamfile)
+ {
+ load-aux $(jamfile-module) : [ path.native $(jamfile) ] ;
+ }
+ }
+
+ # Now do some checks.
+ if $(.current-project) != $(saved-project)
+ {
+ import errors ;
+ 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 in the wrong project.
+ : after loading $(jamfile-module)
+ : expected value $(saved-project)
+ : actual value $(.current-project) ;
+ }
+
+ end-load $(previous-project) ;
+
+ if $(.global-build-dir)
+ {
+ if [ attribute $(jamfile-module) location ] && ! [ attribute
+ $(jamfile-module) id ]
+ {
+ local project-root = [ attribute $(jamfile-module)
+ project-root ] ;
+ if $(project-root) = $(dir)
+ {
+ ECHO "warning: the --build-dir option was specified" ;
+ ECHO "warning: but Jamroot at '$(dir)'" ;
+ ECHO "warning: specified no project id" ;
+ ECHO "warning: the --build-dir option will be ignored" ;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+# Called when done loading a project module. Restores the current project to its
+# previous value and does some additional checking to make sure our 'currently
+# loaded project' identifier does not get left with an invalid value.
+#
+rule end-load ( previous-project ? )
+{
+ if ! $(.current-project)
+ {
+ import errors ;
+ errors.error Ending project loading requested when there was no project
+ currently being loaded. ;
+ }
+
+ if ! $(previous-project) && $(.saved-current-project)
+ {
+ import errors ;
+ errors.error Ending project loading requested with no 'previous project'
+ when there were other projects still marked as being loaded
+ recursively. ;
+ }
+
+ .current-project = $(previous-project) ;
+}
+
+
+rule mark-as-user ( module-name )
+{
+ if USER_MODULE in [ RULENAMES ]
+ {
+ USER_MODULE $(module-name) ;
+ }
+}
+
+
+rule load-aux ( module-name : file )
+{
+ mark-as-user $(module-name) ;
+
+ module $(module-name)
+ {
+ include $(2) ;
+ local rules = [ RULENAMES $(1) ] ;
+ IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ;
+ }
+}
+
+
+.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]) ] ;
+}
+
+
+# Initialize the module for a project.
+#
+rule initialize (
+ module-name # The name of the project module.
+ : location ? # The location (directory) of the project to initialize. If
+ # not specified, a standalone project will be initialized.
+ : basename ?
+ )
+{
+ if $(.debug-loading)
+ {
+ ECHO "Initializing project '$(module-name)'" ;
+ }
+
+ local jamroot ;
+
+ local parent-module ;
+ if $(module-name) = test-config
+ {
+ # No parent.
+ }
+ else if $(module-name) = site-config
+ {
+ parent-module = test-config ;
+ }
+ else if $(module-name) = user-config
+ {
+ parent-module = site-config ;
+ }
+ else if $(module-name) = project-config
+ {
+ parent-module = user-config ;
+ }
+ else if $(location) && ! [ MATCH ^($(JAMROOT))$ : $(basename) ]
+ {
+ # We search for parent/jamroot only if this is a jamfile project, i.e.
+ # if is not a standalone or a jamroot project.
+ parent-module = [ load-parent $(location) ] ;
+ }
+ else if $(location)
+ {
+ # We have a jamroot project. Inherit from user-config (or project-config
+ # if it exists).
+ if $(project-config.attributes)
+ {
+ parent-module = project-config ;
+ }
+ else
+ {
+ parent-module = user-config ;
+ }
+ jamroot = true ;
+ }
+
+ # TODO: need to consider if standalone projects can do anything but define
+ # prebuilt targets. If so, we need to give them a more sensible "location",
+ # so that source paths are correct.
+ location ?= "" ;
+ # Create the module for the Jamfile first.
+ module $(module-name)
+ {
+ }
+
+ # load-parent can end up loading this module again. Make sure this is not
+ # duplicated.
+ if ! $($(module-name).attributes)
+ {
+ $(module-name).attributes = [ new project-attributes $(location)
+ $(module-name) ] ;
+ local attributes = $($(module-name).attributes) ;
+
+ if $(location)
+ {
+ $(attributes).set source-location : [ path.make $(location) ] :
+ exact ;
+ }
+ else
+ {
+ local cfgs = project site test user ;
+ if ! $(module-name) in $(cfgs)-config
+ {
+ # This is a standalone project with known location. Set its
+ # source location so it can declare targets. This is needed so
+ # you can put a .jam file with your sources and use it via
+ # 'using'. Standard modules (in the 'tools' subdir) may not
+ # assume source dir is set.
+ local s = [ modules.binding $(module-name) ] ;
+ if ! $(s)
+ {
+ import errors ;
+ errors.error Could not determine project location
+ $(module-name) ;
+ }
+ $(attributes).set source-location : $(s:D) : exact ;
+ }
+ }
+
+ $(attributes).set requirements : [ property-set.empty ] : exact ;
+ $(attributes).set usage-requirements : [ property-set.empty ] : exact ;
+
+ # Import rules common to all project modules from project-rules module,
+ # defined at the end of this file.
+ local rules = [ RULENAMES project-rules ] ;
+ IMPORT project-rules : $(rules) : $(module-name) : $(rules) ;
+
+ if $(parent-module)
+ {
+ inherit-attributes $(module-name) : $(parent-module) ;
+ $(attributes).set parent-module : $(parent-module) : exact ;
+ }
+
+ if $(jamroot)
+ {
+ $(attributes).set project-root : $(location) : exact ;
+ if ! $(.first-project-root)
+ {
+ .first-project-root = $(module-name) ;
+ }
+ }
+
+ local parent ;
+ if $(parent-module)
+ {
+ parent = [ target $(parent-module) ] ;
+ }
+
+ if ! $(.target.$(module-name))
+ {
+ local requirements = [ attribute $(module-name) requirements ] ;
+ .target.$(module-name) = [ new project-target $(module-name) :
+ $(module-name) $(parent) : $(requirements) ] ;
+
+ if $(.debug-loading)
+ {
+ ECHO Assigned project target $(.target.$(module-name)) to
+ '$(module-name)' ;
+ }
+ }
+ }
+
+ .current-project = [ target $(module-name) ] ;
+}
+
+
+# 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) ] ;
+ # Parent module might be locationless configuration module.
+ 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 ;
+ $(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 ;
+ }
+}
+
+
+# Returns whether the given string is a valid registered project id.
+#
+rule is-registered-id ( id )
+{
+ return $($(id).jamfile-module) ;
+}
+
+
+# Associate the given id with the given project module. Returns the possibly
+# corrected project id.
+#
+rule register-id ( id : module )
+{
+ id = [ path.root $(id) / ] ;
+
+ if [ MATCH (//) : $(id) ]
+ {
+ import errors ;
+ errors.user-error Project id may not contain two consecutive slash
+ characters (project id: '$(id)'). ;
+ }
+
+ local orig-module = $($(id).jamfile-module) ;
+ if $(orig-module) && $(orig-module) != $(module)
+ {
+ local new-file = [ modules.peek $(module) : __file__ ] ;
+ local new-location = [ project.attribute $(module) location ] ;
+
+ local orig-file = [ modules.peek $(orig-module) : __file__ ] ;
+ local orig-main-id = [ project.attribute $(orig-module) id ] ;
+ local orig-location = [ project.attribute $(orig-module) location ] ;
+ local orig-project = [ target $(orig-module) ] ;
+ local orig-name = [ $(orig-project).name ] ;
+
+ import errors ;
+ errors.user-error Attempt to redeclare already registered project id
+ '$(id)'.
+ : Original project:
+ : " " Name: $(orig-name:E=---)
+ : " " Module: $(orig-module)
+ : " " Main id: $(orig-main-id:E=---)
+ : " " File: $(orig-file:E=---)
+ : " " Location: $(orig-location:E=---)
+ : New project:
+ : " " Module: $(module)
+ : " " File: $(new-file:E=---)
+ : " " Location: $(new-location:E=---) ;
+ }
+
+ $(id).jamfile-module = $(module) ;
+ return $(id) ;
+}
+
+
+# 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".
+#
+class project-attributes
+{
+ import path ;
+ import print ;
+ import project ;
+ import property ;
+ import property-set ;
+ import sequence ;
+
+ rule __init__ ( location project-module )
+ {
+ self.location = $(location) ;
+ self.project-module = $(project-module) ;
+ }
+
+ # Set the named attribute from the specification given by the user. The
+ # value actually set may be different.
+ #
+ rule set ( attribute : specification *
+ : exact ? # Sets value from 'specification' without any processing.
+ )
+ {
+ if $(exact)
+ {
+ self.$(attribute) = $(specification) ;
+ }
+ else if $(attribute) = "requirements"
+ {
+ local result = [ property-set.refine-from-user-input
+ $(self.requirements) : $(specification)
+ : $(self.project-module) : $(self.location) ] ;
+
+ if $(result[1]) = "@error"
+ {
+ import errors : error : errors.error ;
+ errors.error Requirements for project at '$(self.location)'
+ conflict with parent's. : Explanation: $(result[2-]) ;
+ }
+
+ self.requirements = $(result) ;
+ }
+ else if $(attribute) = "usage-requirements"
+ {
+ local unconditional ;
+ for local p in $(specification)
+ {
+ local split = [ property.split-conditional $(p) ] ;
+ split ?= nothing $(p) ;
+ unconditional += $(split[2]) ;
+ }
+
+ local non-free = [ property.remove free : $(unconditional) ] ;
+ if $(non-free)
+ {
+ import errors : error : errors.error ;
+ errors.error usage-requirements $(specification) have non-free
+ properties $(non-free) ;
+ }
+ local t = [ property.translate-paths $(specification) :
+ $(self.location) ] ;
+ if $(self.usage-requirements)
+ {
+ self.usage-requirements = [ property-set.create
+ [ $(self.usage-requirements).raw ] $(t) ] ;
+ }
+ else
+ {
+ self.usage-requirements = [ property-set.create $(t) ] ;
+ }
+ }
+ else if $(attribute) = "default-build"
+ {
+ self.default-build = [ property.make $(specification) ] ;
+ }
+ else if $(attribute) = "source-location"
+ {
+ self.source-location = ;
+ for local src-path in $(specification)
+ {
+ self.source-location += [ path.root [ path.make $(src-path) ]
+ $(self.location) ] ;
+ }
+ }
+ else if $(attribute) = "build-dir"
+ {
+ self.build-dir = [ path.root [ path.make $(specification) ]
+ $(self.location) ] ;
+ }
+ else if $(attribute) = "id"
+ {
+ self.id = [ project.register-id $(specification) :
+ $(self.project-module) ] ;
+ }
+ else if ! $(attribute) in "default-build" "location" "parent"
+ "projects-to-build" "project-root" "source-location"
+ {
+ import errors : error : errors.error ;
+ errors.error Invalid project attribute '$(attribute)' specified for
+ project at '$(self.location)' ;
+ }
+ else
+ {
+ self.$(attribute) = $(specification) ;
+ }
+ }
+
+ # Returns the value of the given attribute.
+ #
+ rule get ( attribute )
+ {
+ return $(self.$(attribute)) ;
+ }
+
+ # Returns whether these attributes belong to a Jamroot project module.
+ #
+ rule is-jamroot ( )
+ {
+ if $(self.location) && $(self.project-root) = $(self.location)
+ {
+ return true ;
+ }
+ }
+
+ # Prints the project attributes.
+ #
+ rule print ( )
+ {
+ local id = '$(self.id)' ;
+ print.section $(id:E=(none)) ;
+ print.list-start ;
+ print.list-item "Parent project:" $(self.parent:E=(none)) ;
+ 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 build directory for standalone projects
+#
+rule standalone-build-dir ( )
+{
+ project = [ target $(.first-project-root) ] ;
+ return [ path.join [ $(project).build-dir ] standalone ] ;
+}
+
+# Returns the project which is currently being loaded.
+#
+rule current ( )
+{
+ if ! $(.current-project)
+ {
+ import errors ;
+ errors.error Reference to the project currently being loaded requested
+ when there was no project module being loaded. ;
+ }
+ return $(.current-project) ;
+}
+
+
+# Temporarily changes the current project to 'project'. Should be followed by
+# 'pop-current'.
+#
+rule push-current ( project )
+{
+ .saved-current-project += $(.current-project) ;
+ .current-project = $(project) ;
+}
+
+
+rule pop-current ( )
+{
+ .current-project = $(.saved-current-project[-1]) ;
+ .saved-current-project = $(.saved-current-project[1--2]) ;
+}
+
+
+# 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) ] ;
+}
+
+
+# Returns whether a project module is one of Boost Build's configuration
+# modules.
+#
+rule is-config-module ( project )
+{
+ local cfgs = project site test user ;
+ if $(project) in $(cfgs)-config
+ {
+ return true ;
+ }
+}
+
+
+# Returns whether a project module is a Jamroot project module.
+#
+rule is-jamroot-module ( project )
+{
+ return [ $($(project).attributes).is-jamroot ] ;
+}
+
+
+# Returns a project's parent jamroot module. Returns nothing if there is no such
+# module, i.e. if this is a standalone project or one of the internal Boost
+# Build configuration projects.
+#
+rule get-jamroot-module ( project )
+{
+ local jamroot-location = [ attribute $(project) project-root ] ;
+ if $(jamroot-location)
+ {
+ return [ module-name $(jamroot-location) ] ;
+ }
+}
+
+
+# Returns the project target corresponding to the 'project-module'.
+#
+rule target ( project-module )
+{
+ if ! $(.target.$(project-module))
+ {
+ import errors ;
+ errors.user-error Project target requested but not yet assigned for
+ module '$(project-module)'. ;
+ }
+ return $(.target.$(project-module)) ;
+}
+
+
+# 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 initialized 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) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17)
+ : $(18) : $(19) ;
+ 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 have found are relative to the current directory, but the
+ # names specified in the sources list are assumed to be relative to the
+ # source directory of the corresponding project. So, just make the names
+ # absolute.
+ for local p in $(paths)
+ {
+ # If the path is below source location, use relative path.
+ # Otherwise, use full path just to avoid any ambiguities.
+ local rel = [ path.relative $(p) $(location) : no-error ] ;
+ if $(rel) = not-a-child
+ {
+ result += [ path.root $(p) [ path.pwd ] ] ;
+ }
+ else
+ {
+ result += $(rel) ;
+ }
+ }
+ }
+ else
+ {
+ # There were no wildcards in the directory path, 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) ;
+}
+
+
+# This module defines rules common to all projects.
+#
+module project-rules
+{
+ import modules ;
+
+ rule using ( toolset-module : * )
+ {
+ import toolset ;
+
+ local saved-project = [ modules.peek project : .current-project ] ;
+
+ # Temporarily change the search path so the module referred to by
+ # 'using' can be placed in the same directory as Jamfile. User will
+ # expect the module to be found even though the directory is not in
+ # BOOST_BUILD_PATH.
+ local x = [ modules.peek : BOOST_BUILD_PATH ] ;
+ local caller = [ CALLER_MODULE ] ;
+ local caller-location = [ modules.binding $(caller) ] ;
+ modules.poke : BOOST_BUILD_PATH : $(caller-location:D) $(x) ;
+ toolset.using $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
+ $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17)
+ : $(18) : $(19) ;
+ modules.poke : BOOST_BUILD_PATH : $(x) ;
+
+ # The above might have clobbered .current-project in case it caused a
+ # new project instance to be created (which would then automatically
+ # get set as the 'current' project). Restore the correct value so any
+ # main targets declared after this do not get mapped to the loaded
+ # module's project.
+ modules.poke project : .current-project : $(saved-project) ;
+ }
+
+ rule import ( * : * : * )
+ {
+ local caller = [ CALLER_MODULE ] ;
+ local saved-project = [ modules.peek project : .current-project ] ;
+ module $(caller)
+ {
+ modules.import $(1) : $(2) : $(3) ;
+ }
+
+ # The above might have clobbered .current-project in case it caused a
+ # new project instance to be created (which would then automatically
+ # get set as the 'current' project). Restore the correct value so any
+ # main targets declared after this do not get mapped to the loaded
+ # module's project.
+ modules.poke project : .current-project : $(saved-project) ;
+ }
+
+ rule project ( id ? : options * : * )
+ {
+ import path ;
+ import project ;
+
+ local caller = [ CALLER_MODULE ] ;
+ local attributes = [ project.attributes $(caller) ] ;
+ if $(id)
+ {
+ $(attributes).set id : $(id) ;
+ }
+
+ local explicit-build-dir ;
+
+ for n in 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+ {
+ 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 an empty location is a 'standalone' project such as
+ # user-config or qt. It has no build dir. If we try to set build dir
+ # for user-config, we shall then try to inherit it, with either
+ # weird or wrong consequences.
+ if $(location) && $(location) = [ $(attributes).get project-root ]
+ {
+ # Re-read the project id, since it might have been modified a
+ # bit when setting the project's id attribute, e.g. might have
+ # been prefixed by a slash if it was not already.
+ id = [ $(attributes).get id ] ;
+ # This is Jamroot.
+ if $(id)
+ {
+ if $(explicit-build-dir) &&
+ [ path.is-rooted $(explicit-build-dir) ]
+ {
+ import errors ;
+ errors.user-error Absolute directory specified via
+ 'build-dir' project attribute : Do not 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)
+ {
+ import errors ;
+ errors.user-error When --build-dir is specified, the
+ 'build-dir' 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 : value + )
+ {
+ import project ;
+ local caller = [ CALLER_MODULE ] ;
+ local p = [ project.target $(caller) ] ;
+ $(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 : value + )
+ {
+ import project ;
+ local caller = [ CALLER_MODULE ] ;
+ local p = [ project.target $(caller) ] ;
+ $(p).add-constant $(name) : $(value) : path ;
+ }
+
+ rule use-project ( id : where )
+ {
+ # See comment in 'load' for explanation.
+ local caller = [ CALLER_MODULE ] ;
+ modules.poke $(caller) : .used-projects : [ modules.peek $(caller) :
+ .used-projects ] $(id) $(where) ;
+ }
+
+ rule build-project ( dir )
+ {
+ import project ;
+ local caller = [ CALLER_MODULE ] ;
+ local attributes = [ project.attributes $(caller) ] ;
+ 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 always ( target-names * )
+ {
+ import project ;
+ local t = [ project.current ] ;
+ for local n in $(target-names)
+ {
+ $(t).mark-target-as-always $(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)
+ {
+ import errors ;
+ 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 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 * )
+ {
+ local condition = $(condition:J=,) ;
+ if [ MATCH (:) : $(condition) ]
+ {
+ return $(condition)$(requirements) ;
+ }
+ else
+ {
+ return $(condition):$(requirements) ;
+ }
+ }
+
+ rule option ( name : value )
+ {
+ local m = [ CALLER_MODULE ] ;
+ local cfgs = project site test user ;
+ if ! $(m) in $(cfgs)-config
+ {
+ import errors ;
+ errors.error The 'option' rule may only be used "in" Boost Build
+ configuration files. ;
+ }
+ import option ;
+ option.set $(name) : $(value) ;
+ }
+}