Boost logo

Boost-Commit :

From: jurko.gospodnetic_at_[hidden]
Date: 2008-01-06 01:15:40


Author: jurko
Date: 2008-01-06 01:15:39 EST (Sun, 06 Jan 2008)
New Revision: 42508
URL: http://svn.boost.org/trac/boost/changeset/42508

Log:
Refactored build-system.jam. Added many detailed comments.

Test configuration module now gets loaded the same as all other configuration modules.

In addition to being able to specify which user configuration to load, user may now also prevent loading the user configuration by specifying an empty file name for it.
Text files modified:
   trunk/tools/build/v2/build-system.jam | 1151 ++++++++++++++++++++++-----------------
   trunk/tools/build/v2/build/project.jam | 6
   2 files changed, 642 insertions(+), 515 deletions(-)

Modified: trunk/tools/build/v2/build-system.jam
==============================================================================
--- trunk/tools/build/v2/build-system.jam (original)
+++ trunk/tools/build/v2/build-system.jam 2008-01-06 01:15:39 EST (Sun, 06 Jan 2008)
@@ -6,703 +6,826 @@
 
 # This file is part of Boost Build version 2. You can think of it as forming the
 # the main() routine. It is invoked by the bootstrapping code in bootstrap.jam.
-#
-# The version of bootstrap.jam invoking this lives in tools/build/kernel until
-# BBv1 is retired, so that BBv1 can have its bootstrap.jam in this directory.
 
-import project ;
-import targets ;
-import sequence ;
-import modules ;
-import feature ;
-import property-set ;
 import build-request ;
-import errors : error ;
-import virtual-target ;
-import "class" : new ;
-import toolset ;
-import regex ;
-
 import builtin ;
+import "class" : new ;
+import errors ;
+import feature ;
 import make ;
+import modules ;
 import os ;
-
+import project ;
+import property-set ;
+import regex ;
+import sequence ;
+import targets ;
+import toolset ;
 import version ;
+import virtual-target ;
 
-# Returns the location of the build system. The primary use case
-# is building Boost, where it's sometimes needed to get location
-# of other components (like BoostBook files), and it's convenient
-# to use location relatively to Boost.Build path.
-rule location ( )
-{
- local r = [ modules.binding build-system ] ;
- return $(r:P) ;
-}
 
-# Returns the property set with the
-# free features from the currently processed
+################################################################################
+#
+# Module global data.
+#
+################################################################################
+
+# Shortcut used in this module for accessing used command-line parameters.
+.argv = [ modules.peek : ARGV ] ;
+
+# Flag indicating we should display additional debugging information related to
+# locating and loading Boost Build configuration files.
+.debug-config = [ MATCH ^(--debug-configuration)$ : $(.argv) ] ;
+
+# Legacy option doing too many things, some of which are not even documented.
+# Should be phased out.
+# * Disables loading site and user configuration files.
+# * Disables auto-configuration for toolsets specified explicitly on the
+# command-line.
+# * Causes --toolset command-line options to be ignored.
+# * Prevents the default toolset from being used even if no toolset has been
+# configured at all.
+.legacy-ignore-config = [ MATCH ^(--ignore-config)$ : $(.argv) ] ;
+
+# The cleaning is tricky. Say, if user says 'bjam --clean foo' where 'foo' is a
+# directory, then we want to clean targets which are in 'foo' as well as those
+# in any children Jamfiles under foo but not in any unrelated Jamfiles. To
+# achieve this we collect a list of projects under which cleaning is allowed.
+.project-targets = ;
+
+# Virtual targets obtained when building main targets references on the command
+# line. When running 'bjam --clean main_target' we want to clean only files
+# belonging to that main target so we need to record which targets are produced
+# for it.
+.results-of-main-targets = ;
+
+# Was an XML dump requested?
+.out-xml = [ MATCH ^--out-xml=(.*)$ : $(.argv) ] ;
+
+
+################################################################################
+#
+# Public rules.
+#
+################################################################################
+
+# Returns the property set with the free features from the currently processed
 # build request.
+#
 rule command-line-free-features ( )
 {
     return $(.command-line-free-features) ;
 }
 
 
+# Returns the location of the build system. The primary use case is building
+# Boost where it's sometimes needed to get the location of other components
+# (e.g. BoostBook files) and it's convenient to use locations relative to the
+# Boost Build path.
+#
+rule location ( )
+{
+ local r = [ modules.binding build-system ] ;
+ return $(r:P) ;
+}
 
-# Check if we can load 'test-config.jam'. If we can, load it and
-# ignore user configs.
-
-local argv = [ modules.peek : ARGV ] ;
-
-local test-config = [ GLOB [ os.environ BOOST_BUILD_PATH ] : test-config.jam ] ;
 
-local debug-config = [ MATCH ^(--debug-configuration)$ : [ modules.peek : ARGV ] ] ;
+################################################################################
+#
+# Local rules.
+#
+################################################################################
 
-if $(test-config)
+# Returns actual Jam targets to be used for executing a clean request.
+#
+local rule actual-clean-targets ( )
 {
- if $(debug-config)
+ # Construct a list of projects explicitly detected as targets on this build
+ # system run. These are the projects under which cleaning is allowed.
+ for local t in $(targets)
     {
- ECHO "notice: loading test-config.jam from"
- [ NORMALIZE_PATH $(test-config[1]) ] ;
- ECHO "notice: user-config.jam and site-config.jam will be ignored" ;
+ if [ class.is-a $(t) : project-target ]
+ {
+ .project-targets += [ $(t).project-module ] ;
+ }
     }
 
- module test-config
+ # Construct a list of targets explicitly detected on this build system run
+ # as a result of building main targets.
+ local targets-to-clean ;
+ for local t in $(.results-of-main-targets)
     {
- import toolset : using : using ;
+ # Don't include roots or sources.
+ targets-to-clean += [ virtual-target.traverse $(t) ] ;
     }
- import test-config ;
-}
-
-local ignore-config ;
-if $(test-config) || --ignore-config in [ modules.peek : ARGV ]
-{
- ignore-config = true ;
-}
-
-local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ;
+ targets-to-clean = [ sequence.unique $(targets-to-clean) ] ;
 
-# Unless ignore-config is set, load the configuration file in
-# $(path)/$(basename).jam
-local rule load-config ( basename : path + )
-{
- if ! $(ignore-config)
+ local to-clean ;
+ for local t in [ virtual-target.all-targets ]
     {
- if $(debug-config)
+ local p = [ $(t).project ] ;
+
+ # Remove only derived targets.
+ if [ $(t).action ]
         {
- ECHO notice: searching \"$(path)\" for \"$(basename).jam\" ;
- local where = [ GLOB $(path) : $(basename).jam ] ;
- if $(where)
+ if $(t) in $(targets-to-clean) ||
+ [ should-clean-project [ $(p).project-module ] ] = true
             {
- ECHO notice: loading $(basename).jam from
- [ NORMALIZE_PATH $(where[1]) ] ;
+ to-clean += $(t) ;
             }
         }
+ }
 
- modules.load $(basename) : : $(path) ;
- project.load-used-projects $(basename) ;
+ local to-clean-actual ;
+ for local t in $(to-clean)
+ {
+ to-clean-actual += [ $(t).actualize ] ;
     }
+ return $(to-clean-actual) ;
 }
 
+
+# Given a target id, try to find and return the corresponding target. This is
+# only invoked when there's no Jamfile in ".". This code somewhat duplicates
+# code in project-target.find but we can't reuse that code without a
+# project-targets instance.
 #
-# Load site-config.
-#
-module site-config
+local rule find-target ( target-id )
 {
- import project : initialize ;
- initialize site-config ;
-}
+ local split = [ MATCH (.*)//(.*) : $(target-id) ] ;
 
-local site-path = /etc $(user-path) ;
+ local pm ;
+ if $(split)
+ {
+ pm = [ project.find $(split[1]) : "." ] ;
+ }
+ else
+ {
+ pm = [ project.find $(target-id) : "." ] ;
+ }
 
-if [ os.name ] in NT CYGWIN
-{
- site-path = [ modules.peek : SystemRoot ] $(user-path) ;
+ local result ;
+ if $(pm)
+ {
+ result = [ project.target $(pm) ] ;
+ }
+
+ if $(split)
+ {
+ result = [ $(result).find $(split[2]) ] ;
+ }
+
+ return $(result) ;
 }
 
-load-config site-config : $(site-path) ;
 
+# Initializes a new configuration module.
 #
-# Load user-config.
-#
-module user-config
+local rule initialize-config-module ( module-name )
 {
- import project : initialize ;
- initialize user-config ;
+ project.initialize $(module-name) ;
+ if USER_MODULE in [ RULENAMES ]
+ {
+ USER_MODULE $(module-name) ;
+ }
 }
 
-local user-config-path = [ MATCH ^--user-config=(.*) : $(argv) ] ;
-user-config-path ?= [ os.environ BOOST_BUILD_USER_CONFIG ] ;
 
-if $(user-config-path)
+# Helper rule used to load configuration files. Loads the first configuration
+# file with the given 'filename' at 'path' into module with name 'module-name'.
+# Not finding the requested file may or may not be treated as an error depending
+# on the must-find parameter. Returns a normalized path to the loaded
+# configuration file or nothing if no file was loaded.
+#
+local rule load-config ( module-name : filename : path + : must-find ? )
 {
- if $(debug-config)
+ if $(.debug-config)
     {
- ECHO "Loading explicitly specified user configuration file:" ;
- ECHO " $(user-config-path)" ;
+ ECHO "notice: Searching" "$(path)" "for" "$(module-name)"
+ "configuration file" "$(filename)" "." ;
     }
+ local where = [ GLOB $(path) : $(filename) ] ;
+ if $(where)
+ {
+ where = [ NORMALIZE_PATH $(where[1]) ] ;
+ if $(.debug-config)
+ {
+ ECHO "notice: Loading" "$(module-name)" "configuration file"
+ "$(filename)" "from" $(where) "." ;
+ }
 
- modules.load user-config : $(user-config-path:BS) : $(user-config-path:D) ;
- project.load-used-projects user-config ;
+ modules.load $(module-name) : $(filename) : $(path) ;
+ project.load-used-projects $(module-name) ;
+ }
+ else
+ {
+ if ( must-find )
+ {
+ errors.user-error "Configuration file" "$(filename)" "not found in"
+ "$(path)" "." ;
+ }
+ if $(.debug-config)
+ {
+ ECHO "notice:" "Configuration file" "$(filename)" "not found in"
+ "$(path)" "." ;
+ }
+ }
+ return where ;
 }
-else
+
+
+# Loads all the configuration files used by Boost Build in the following order:
+#
+# -- test-config --
+# Loaded if the specified test-config.jam file is found under the path pointed
+# to by the BOOST_BUILD_PATH environment variable. It is ok for this file not to
+# exist even if specified. If this configuration file is loaded, regular site
+# and user configuration files will not be.
+#
+# -- site-config --
+# Always named site-config.jam. Will only be found if located on the system
+# root path (Windows), /etc (non-Windows), user's home folder or the Boost Build
+# path, in that order. Not loaded in case the test-config configuration file is
+# loaded or the --ignore-config command-line options is specified.
+#
+# -- user-config --
+# Named user-config.jam by default but may be explicitly renamed using the
+# --user-config command-line option or the BOOST_BUILD_USER_CONFIG environment
+# variable. File is searched for in the user's home folder and the Boost Build
+# path, in that order. Not loaded in case either the test-configuration file is
+# loaded, --ignore-config command-line option is specified or an empty file
+# name is explicitly specified. If its non-empty name has been explicitly
+# specified then the file must exist.
+#
+# Test configurations have been added primarily for use by Boost Build's
+# internal unit testing system but may be used freely in other places as well.
+#
+local rule load-configuration-files
 {
- load-config user-config : $(user-path) ;
+ if $(.legacy-ignore-config) && $(.debug-config)
+ {
+ ECHO "notice: Regular site and user configuration files will be ignored" ;
+ ECHO "notice: due to the --ignore-config command-line option." ;
+ }
+
+ initialize-config-module test-config ;
+ local test-config = [ GLOB [ os.environ BOOST_BUILD_PATH ] : test-config.jam ] ;
+ if $(test-config)
+ {
+ local where =
+ [ load-config test-config : $(test-config:BS) : $(test-config:D) ] ;
+ if $(where)
+ {
+ if $(.debug-config) && ! $(.legacy-ignore-config)
+ {
+ ECHO "notice: Regular site and user configuration files will" ;
+ ECHO "notice: be ignored due to the test configuration being"
+ "loaded." ;
+ }
+
+ # This legacy setting should be removed. Added here only to preserve
+ # behaviour with test configuration being loaded automatically
+ # disabling toolset auto-configuration and default toolset usage.
+ .legacy-ignore-config = true ;
+ }
+ else
+ {
+ test-config = ;
+ }
+ }
+
+ local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ;
+ local site-path = /etc $(user-path) ;
+ if [ os.name ] in NT CYGWIN
+ {
+ site-path = [ modules.peek : SystemRoot ] $(user-path) ;
+ }
+
+ initialize-config-module site-config ;
+ if ! $(test-config) && ! $(.legacy-ignore-config)
+ {
+ load-config site-config : site-config.jam : $(site-path) ;
+ }
+
+ initialize-config-module user-config ;
+ if ! $(test-config) && ! $(.legacy-ignore-config)
+ {
+ local user-config = [ MATCH ^--user-config=(.*)$ : $(.argv) ] ;
+ user-config = $(user-config[-1]) ;
+ user-config ?= [ os.environ BOOST_BUILD_USER_CONFIG ] ;
+ local explicitly-requested = $(user-config) ;
+ user-config ?= user-config.jam ;
+
+ if $(.debug-config) && $(explicitly-requested)
+ {
+ if $(user-config)
+ {
+ ECHO "Loading explicitly specified user configuration file:" ;
+ ECHO " $(user-config)" ;
+ }
+ else
+ {
+ ECHO "User configuration file loading explicitly disabled." ;
+ }
+ }
+
+ if $(user-config)
+ {
+ load-config user-config : $(user-config) : $(user-path)
+ : $(explicitly-requested) ;
+ }
+ }
 }
 
 
-#
 # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or
-# toolset=xx,yy,...zz in the command line
+# toolset=xx,yy,...zz in the command line. May return additional properties to
+# be processed as if they had been specified by the user.
 #
-local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*) : $(argv) ] : "," ] ;
-local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*) : $(argv) ] : "," ] ;
+local rule process-explicit-toolset-requests
+{
+ local extra-properties ;
 
-# if the user specified --toolset=..., we need to add toolset=... to
-# the build request
-local extra-build-request ;
+ local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*)$ : $(.argv) ] : "," ] ;
+ local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*)$ : $(.argv) ] : "," ] ;
 
-if ! $(ignore-config)
-{
     for local t in $(option-toolsets) $(feature-toolsets)
     {
- # Parse toolset-version/properties
+ # Parse toolset-version/properties.
         local (t-v,t,v) = [ MATCH (([^-/]+)-?([^/]+)?)/?.* : $(t) ] ;
         local toolset-version = $((t-v,t,v)[1]) ;
         local toolset = $((t-v,t,v)[2]) ;
         local version = $((t-v,t,v)[3]) ;
 
- if $(debug-config)
+ if $(.debug-config)
         {
- ECHO notice: [cmdline-cfg] Detected command-line request for
- $(toolset-version): toolset= \"$(toolset)\" "version= \""$(version)\" ;
+ ECHO "notice: [cmdline-cfg] Detected command-line request for"
+ $(toolset-version): "toolset=" $(toolset) "version="
+ $(version) ;
         }
 
+ # If the toolset isn't known, configure it now.
         local known ;
-
- # if the toolset isn't known, configure it now.
         if $(toolset) in [ feature.values <toolset> ]
         {
             known = true ;
         }
-
- if $(known) && $(version)
- && ! [ feature.is-subvalue toolset : $(toolset) : version : $(version) ]
+ if $(known) && $(version) && ! [ feature.is-subvalue toolset
+ : $(toolset) : version : $(version) ]
         {
             known = ;
         }
 
         if ! $(known)
         {
- if $(debug-config)
+ if $(.debug-config)
             {
- ECHO notice: [cmdline-cfg] toolset $(toolset-version)
- not previously configured; configuring now ;
+ ECHO notice: [cmdline-cfg] toolset $(toolset-version) not
+ previously configured; attempting to auto-configure now ;
             }
             toolset.using $(toolset) : $(version) ;
         }
         else
         {
- if $(debug-config)
+ if $(.debug-config)
             {
- ECHO notice: [cmdline-cfg] toolset $(toolset-version) already configured ;
+ ECHO notice: [cmdline-cfg] toolset $(toolset-version) already
+ configured ;
             }
         }
 
- # make sure we get an appropriate property into the build request in
- # case the user used the "--toolset=..." form
- if ! $(t) in $(argv)
- && ! $(t) in $(feature-toolsets)
+ # Make sure we get an appropriate property into the build request in
+ # case toolset was specified using the "--toolset=..." command-line
+ # option form.
+ if ! $(t) in $(.argv) && ! $(t) in $(feature-toolsets)
         {
- if $(debug-config)
+ if $(.debug-config)
             {
                 ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ;
             }
- extra-build-request += toolset=$(t) ;
+ extra-properties += toolset=$(t) ;
         }
     }
-}
 
-if USER_MODULE in [ RULENAMES ]
-{
- USER_MODULE site-config user-config ;
+ return $(extra-properties) ;
 }
 
 
-if --version in [ modules.peek : ARGV ]
+# Returns 'true' if the given 'project' is equal to or is a (possibly indirect)
+# child to any of the projects requested to be cleaned in this build system run.
+# Returns 'false' otherwise. Expects the .project-targets list to have already
+# been constructed.
+#
+local rule should-clean-project ( project )
 {
- version.print ;
- EXIT ;
+ if ! $(.should-clean-project.$(project))
+ {
+ local r = false ;
+ if $(project) in $(.project-targets)
+ {
+ r = true ;
+ }
+ else
+ {
+ local parent = [ project.attribute $(project) parent-module ] ;
+ if $(parent) && $(parent) != user-config
+ {
+ r = [ should-clean-project $(parent) ] ;
+ }
+ }
+ .should-clean-project.$(project) = $(r) ;
+ }
+
+ return $(.should-clean-project.$(project)) ;
 }
 
 
-# We always load project in "." so that 'use-project' directives has any chance
-# of been seen. Otherwise, we won't be able to refer to subprojects using target
-# ids.
-if [ project.find "." : "." ]
-{
- current-project = [ project.target [ project.load "." ] ] ;
-}
+################################################################################
+#
+# main()
+# ------
+#
+################################################################################
 
-if ! [ feature.values <toolset> ]
 {
- local default-toolset = gcc ;
- if [ os.name ] = NT
+ if --version in $(.argv)
     {
- default-toolset = msvc ;
+ version.print ;
+ EXIT ;
     }
 
- ECHO "warning: No toolsets are configured." ;
- ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ;
- ECHO "warning: If the default is wrong, you may not be able to build C++ programs." ;
- ECHO "warning: Use the \"--toolset=xxxxx\" option to override our guess." ;
- ECHO "warning: For more configuration options, please consult" ;
- ECHO "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ;
 
- if ! $(ignore-config)
+ load-configuration-files ;
+
+ local extra-properties ;
+ # Note that this causes --toolset options to be ignored if --ignore-config
+ # is specified.
+ if ! $(.legacy-ignore-config)
     {
- toolset.using $(default-toolset) ;
+ extra-properties = [ process-explicit-toolset-requests ] ;
     }
-}
-
-build-request = [
- build-request.from-command-line [ modules.peek : ARGV ]
- $(extra-build-request)
-] ;
 
-properties = [ $(build-request).get-at 2 ] ;
 
-if $(properties)
-{
- expanded = [ build-request.expand-no-defaults $(properties) ] ;
- local xexpanded ;
- for local e in $(expanded)
+ # We always load project in "." so that 'use-project' directives have any
+ # chance of being seen. Otherwise, we would not be able to refer to
+ # subprojects using target ids.
+ local current-project ;
+ if [ project.find "." : "." ]
     {
- xexpanded += [ property-set.create [ feature.split $(e) ] ] ;
+ current-project = [ project.target [ project.load "." ] ] ;
     }
- expanded = $(xexpanded) ;
-}
-else
-{
- expanded = [ property-set.empty ] ;
-}
-
-
 
-local target-ids = [ $(build-request).get-at 1 ] ;
-local targets
-local clean ;
 
+ # In case there are no toolsets currently defined makes the build run using
+ # the default toolset.
+ if ! $(.legacy-ignore-config) && ! [ feature.values <toolset> ]
+ {
+ local default-toolset = gcc ;
+ if [ os.name ] = NT
+ {
+ default-toolset = msvc ;
+ }
 
-if "--clean-all" in [ modules.peek : ARGV ]
-{
- cleanall = true ;
-}
-
-if "--clean" in [ modules.peek : ARGV ]
-{
- clean = true ;
-}
+ ECHO "warning: No toolsets are configured." ;
+ ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ;
+ ECHO "warning: If the default is wrong, your build may not work correctly." ;
+ ECHO "warning: Use the \"--toolset=xxxxx\" option to override our guess." ;
+ ECHO "warning: For more configuration options, please consult" ;
+ ECHO "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ;
 
+ toolset.using $(default-toolset) ;
+ }
 
-local bjam-targets ;
 
-# Given a target it, try to find and return corresponding target.
-# This is only invoked when there's no Jamfile in "."
-# This code somewhat duplicates code in project-target.find but we can't reuse
-# that code without project-targets instance.
-rule find-target ( target-id )
-{
- local split = [ MATCH (.*)//(.*) : $(target-id) ] ;
-
- local pm ;
- if $(split)
- {
- pm = [ project.find $(split[1]) : "." ] ;
+ # Parse command line for targets and properties. Note that this requires
+ # that all project files already be loaded.
+ local build-request = [ build-request.from-command-line $(.argv)
+ $(extra-properties) ] ;
+ local target-ids = [ $(build-request).get-at 1 ] ;
+ local properties = [ $(build-request).get-at 2 ] ;
+
+
+ # Expand properties specified on the command line into multiple property
+ # sets consisting of all legal property combinations. Each expanded property
+ # set will be used for a single build run. E.g. if multiple toolsets are
+ # specified then requested targets will be built with each of them.
+ if $(properties)
+ {
+ expanded = [ build-request.expand-no-defaults $(properties) ] ;
+ local xexpanded ;
+ for local e in $(expanded)
+ {
+ xexpanded += [ property-set.create [ feature.split $(e) ] ] ;
+ }
+ expanded = $(xexpanded) ;
     }
     else
     {
- pm = [ project.find $(target-id) : "." ] ;
- }
-
- local result ;
- if $(pm)
- {
- result = [ project.target $(pm) ] ;
+ expanded = [ property-set.empty ] ;
     }
 
- if $(split)
- {
- result = [ $(result).find $(split[2]) ] ;
- }
-
- return $(result) ;
-}
-
-
 
-if ! $(current-project)
-{
- if ! $(target-ids)
+ # Check that we actually found something to build.
+ if ! $(current-project) && ! $(target-ids)
     {
- ECHO "error: no Jamfile in current directory found, and no target references specified." ;
+ errors.user-error "error: no Jamfile in current directory found, and no"
+ "target references specified." ;
         EXIT ;
     }
-}
 
 
-for local id in $(target-ids)
-{
- if $(id) = clean
- {
- clean = true ;
- }
- else
+ # Flags indicating that this build system run has been started in order to
+ # clean existing instead of create new targets. Note that these are not the
+ # final flag values as they may get changed later on due to some special
+ # targets being specified on the command line.
+ local clean ; if "--clean" in $(.argv) { clean = true ; }
+ local cleanall ; if "--clean-all" in $(.argv) { cleanall = true ; }
+
+
+ # List of Boost Build meta-targets and actual raw Jam targets directly
+ # requested by the user. Raw Jam targets are used when user's request
+ # contains a reference to a specific file not modeled using a main Boost
+ # Build target.
+ local targets ;
+ local bjam-targets ;
+
+
+ # Process each target specified on the command-line and convert it into
+ # internal Boost Build target objects. Detect special clean target. If no
+ # main Boost Build targets were explictly requested use the current project
+ # as the target.
+ for local id in $(target-ids)
     {
- local t ;
- if $(current-project)
+ if $(id) = clean
         {
- t = [ $(current-project).find $(id) : no-error ] ;
+ clean = true ;
         }
         else
         {
- t = [ find-target $(id) ] ;
- }
+ local t ;
+ if $(current-project)
+ {
+ t = [ $(current-project).find $(id) : no-error ] ;
+ }
+ else
+ {
+ t = [ find-target $(id) ] ;
+ }
 
- if ! $(t)
- {
- ECHO "notice: could not find main target " $(id) ;
- ECHO "notice: assuming it's a name of file to create " ;
- bjam-targets += $(id) ;
- }
- else
- {
- targets += $(t) ;
+ if ! $(t)
+ {
+ ECHO "notice: could not find main target " $(id) ;
+ ECHO "notice: assuming it's a name of file to create " ;
+ bjam-targets += $(id) ;
+ }
+ else
+ {
+ targets += $(t) ;
+ }
         }
     }
-}
-
-if ! $(targets)
-{
- targets += [ project.target [ project.module-name "." ] ] ;
-}
-
-virtual-targets = ;
-
-# Virtual targets obtained when building main targets references on
-# the command line. When running
-#
-# bjam --clean main_target
-#
-# we want to clean the files that belong only to that main target,
-# so we need to record which targets are produced.
-local results-of-main-targets ;
-
-for local p in $(expanded)
-{
- .command-line-free-features = [ property-set.create [ $(p).free ] ] ;
- for local t in $(targets)
+ if ! $(targets)
     {
- local g = [ $(t).generate $(p) ] ;
- if ! [ class.is-a $(t) : project-target ]
- {
- results-of-main-targets += $(g[2-]) ;
- }
- virtual-targets += $(g[2-]) ;
+ targets += [ project.target [ project.module-name "." ] ] ;
     }
-}
 
-# The cleaning is tricky. Say, if user says:
-#
-# bjam --clean foo
-#
-# where 'foo' is a directory, then we want to clean targets
-# which are in 'foo' or in any children Jamfiles, but not in any
-# unrelated Jamfiles. So, we collect the list of project under which
-# cleaning is allowed.
-#
 
-local projects-to-clean ;
-local targets-to-clean ;
-if $(clean) || $(clean-all)
-{
- for local t in $(targets)
- {
- if [ class.is-a $(t) : project-target ]
- {
- projects-to-clean += [ $(t).project-module ] ;
- }
- }
+ # List of all virtual-targets created in this build system run.
+ local virtual-targets ;
 
- local subvariants ;
- for local t in $(results-of-main-targets)
- {
- # Don't include roots or sources.
- targets-to-clean += [ virtual-target.traverse $(t) ] ;
- }
- targets-to-clean = [ sequence.unique $(targets-to-clean) ] ;
-}
 
-# Returns 'true' if 'project' is a child of 'current-project',
-# possibly indirect, or is equal to 'project'.
-# Returns 'false' otherwise.
-rule is-child ( project )
-{
- if ! $(.is-child.$(project))
+ # Now that we have a set of targets to build and a set of property sets to
+ # build the targets with, we can start the main build process by using each
+ # property set to generate virtual targets from all of our listed targets
+ # and any of their dependants.
+ for local p in $(expanded)
     {
- local r = false ;
- if $(project) in $(projects-to-clean)
- {
- r = true ;
- }
- else
+ .command-line-free-features = [ property-set.create [ $(p).free ] ] ;
+ for local t in $(targets)
         {
- local parent = [ project.attribute $(project) parent-module ] ;
- if $(parent) && $(parent) != user-config
+ local g = [ $(t).generate $(p) ] ;
+ if ! [ class.is-a $(t) : project-target ]
             {
- r = [ is-child $(parent) ] ;
+ .results-of-main-targets += $(g[2-]) ;
             }
+ virtual-targets += $(g[2-]) ;
         }
-
- .is-child.$(project) = $(r) ;
     }
 
- return $(.is-child.$(project)) ;
-}
-
 
+ # List of all Jam targets constructed in this build system run.
+ local actual-targets ;
 
 
-actual-targets = ;
-for t in $(virtual-targets)
-{
- actual-targets += [ $(t).actualize ] ;
-}
-
-# Was an XML dump requested?
-.out-xml = [ MATCH --out-xml=(.*) : [ modules.peek : ARGV ] ] ;
-if $(.out-xml)
-{
- # Get the qualified name of a virtual target.
- rule full-target-name ( t )
+ # Convert all collected virtual targets into actual raw Jam targets.
+ for t in $(virtual-targets)
     {
- local name = [ $(t).name ] ;
- local project = [ $(t).project ] ;
- local project-path = [ $(project).get location ] ;
- return $(project-path)//$(name) ;
+ actual-targets += [ $(t).actualize ] ;
     }
 
- # Generate an XML file containing build statistics for each
- # constituent
- rule out-xml ( xml-file : constituents * )
- {
- # Prepare valid XML header and footer with some basic info
- local nl = "
-" ;
- local jam = [ version.jam ] ;
- local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ;
- local timestamp = [ modules.peek : JAMDATE ] ;
- local cwd = [ PWD ] ;
- local command = [ modules.peek : ARGV ] ;
- local bb-version = [ version.boost-build ] ;
- .header on $(xml-file) =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "$(nl)<build format=\"1.0\" version=\"$(bb-version)\">"
- "$(nl) <jam version=\"$(jam:J=.)\" />"
- "$(nl) <os name=\"$(os[1])\" platform=\"$(os[2])\"><![CDATA[$(os[3-]:J= )]]></os>"
- "$(nl) <timestamp><![CDATA[$(timestamp)]]></timestamp>"
- "$(nl) <directory><![CDATA[$(cwd)]]></directory>"
- "$(nl) <command><![CDATA[\"$(command:J=\" \")\"]]></command>"
- ;
- .footer on $(xml-file) =
- "$(nl)</build>" ;
- # Generate target dependency graph
- .contents on $(xml-file) +=
- "$(nl) <targets>"
- ;
 
- for local t in [ virtual-target.all-targets ]
+ # If XML data output has been requested prepare additional rules and targets
+ # so we can hook into Jam to collect build data while its building and have
+ # it trigger the final XML report generation after all the planned targets
+ # have been built.
+ if $(.out-xml)
+ {
+ # Get a qualified virtual target name.
+ rule full-target-name ( target )
         {
- local action = [ $(t).action ] ;
- if $(action)
- {
- # If a target has no action, it has
- # no dependencies.
-
- local name = [ full-target-name $(t) ] ;
- local sources = [ $(action).sources ] ;
- local dependencies ;
- for local s in $(sources)
- {
- dependencies += [ full-target-name $(s) ] ;
- }
-
- local path = [ $(t).path ] ;
- local jam-target = [ $(t).actual-name ] ;
+ local name = [ $(target).name ] ;
+ local project = [ $(target).project ] ;
+ local project-path = [ $(project).get location ] ;
+ return $(project-path)//$(name) ;
+ }
 
- .contents on $(xml-file) +=
- "$(nl) <target>"
- "$(nl) <name><![CDATA[$(name)]]></name>"
- "$(nl) <dependencies>"
- "$(nl) <dependency><![CDATA[$(dependencies)]]></dependency>"
- "$(nl) </dependencies>"
- "$(nl) <path><![CDATA[$(path)]]></path>"
- "$(nl) <jam-target><![CDATA[$(jam-target)]]></jam-target>"
- "$(nl) </target>"
- ;
- }
- }
- .contents on $(xml-file) +=
- "$(nl) </targets>"
- ;
-
- # Build $(xml-file) after $(constituents) and do so even if a
- # constituent action fails, and regenerate the xml on every bjam run.
- INCLUDES $(xml-file) : $(constituents) ;
- ALWAYS $(xml-file) ;
- __ACTION_RULE__ on $(xml-file) =
- build-system.out-xml.generate-action ;
- out-xml.generate $(xml-file) ;
- }
-
- # The actual build actions are here; if we did this work in the
- # actions clause we would have to form a valid command line
- # containing the result of @(...) below (the name of the XML file).
- rule out-xml.generate-action (
- args * : xml-file :
- command status start end user system : output ? )
- {
- local contents =
- [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ;
- local f = @($(xml-file):E=$(contents)) ;
- }
-
- # Nothing to do here; the *real* actions happen in
- # out-xml.generate-action
- actions quietly out-xml.generate { }
-
- # Define the out-xml file target, which depends on all the targets
- # so that it runs the collection after the targets have run.
- out-xml $(.out-xml) : $(actual-targets) ;
-
- # Set up a global __ACTION_RULE__ that records all the available
- # statistics about each actual target in a variable "on" the
- # --out-xml target.
- rule out-xml.collect (
- xml-file : target :
- command status start end user system : output ? )
- {
- local nl = "
+ # Generate an XML file containing build statistics for each constituent.
+ #
+ rule out-xml ( xml-file : constituents * )
+ {
+ # Prepare valid XML header and footer with some basic info.
+ local nl = "
 " ;
- # Open the action with some basic info.
- .contents on $(xml-file) +=
- "$(nl) <action status=\"$(status)\" start=\"$(start)\" end=\"$(end)\" user=\"$(user)\" system=\"$(system)\">"
- ;
-
- # If we have an action object we can print out more detailed info.
- local action = [ on $(target) return $(.action) ] ;
- if $(action)
- {
- local action-name = [ $(action).action-name ] ;
- local action-sources = [ $(action).sources ] ;
- local action-props = [ $(action).properties ] ;
-
- # The qualified name of the action which we created the target.
- .contents on $(xml-file) +=
- "$(nl) <name><![CDATA[$(action-name)]]></name>"
+ local jam = [ version.jam ] ;
+ local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ;
+ local timestamp = [ modules.peek : JAMDATE ] ;
+ local cwd = [ PWD ] ;
+ local command = $(.argv) ;
+ local bb-version = [ version.boost-build ] ;
+ .header on $(xml-file) =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "$(nl)<build format=\"1.0\" version=\"$(bb-version)\">"
+ "$(nl) <jam version=\"$(jam:J=.)\" />"
+ "$(nl) <os name=\"$(os[1])\" platform=\"$(os[2])\"><![CDATA[$(os[3-]:J= )]]></os>"
+ "$(nl) <timestamp><![CDATA[$(timestamp)]]></timestamp>"
+ "$(nl) <directory><![CDATA[$(cwd)]]></directory>"
+ "$(nl) <command><![CDATA[\"$(command:J=\" \")\"]]></command>"
                 ;
+ .footer on $(xml-file) =
+ "$(nl)</build>" ;
 
- # The sources that made up the target.
+ # Generate the target dependency graph.
             .contents on $(xml-file) +=
- "$(nl) <sources>"
- ;
- for local source in $(action-sources)
+ "$(nl) <targets>" ;
+ for local t in [ virtual-target.all-targets ]
             {
- local source-actual = [ $(source).actual-name ] ;
- .contents on $(xml-file) +=
- "$(nl) <source><![CDATA[$(source-actual)]]></source>"
- ;
+ local action = [ $(t).action ] ;
+ if $(action)
+ # If a target has no action, it has no dependencies.
+ {
+ local name = [ full-target-name $(t) ] ;
+ local sources = [ $(action).sources ] ;
+ local dependencies ;
+ for local s in $(sources)
+ {
+ dependencies += [ full-target-name $(s) ] ;
+ }
+
+ local path = [ $(t).path ] ;
+ local jam-target = [ $(t).actual-name ] ;
+
+ .contents on $(xml-file) +=
+ "$(nl) <target>"
+ "$(nl) <name><![CDATA[$(name)]]></name>"
+ "$(nl) <dependencies>"
+ "$(nl) <dependency><![CDATA[$(dependencies)]]></dependency>"
+ "$(nl) </dependencies>"
+ "$(nl) <path><![CDATA[$(path)]]></path>"
+ "$(nl) <jam-target><![CDATA[$(jam-target)]]></jam-target>"
+ "$(nl) </target>"
+ ;
+ }
             }
             .contents on $(xml-file) +=
- "$(nl) </sources>"
- ;
+ "$(nl) </targets>" ;
 
- # The properties that define the conditions under which the
- # target was built.
+ # Build $(xml-file) after $(constituents). Do so even if a
+ # constituent action fails and regenerate the xml on every bjam run.
+ INCLUDES $(xml-file) : $(constituents) ;
+ ALWAYS $(xml-file) ;
+ __ACTION_RULE__ on $(xml-file) = build-system.out-xml.generate-action ;
+ out-xml.generate $(xml-file) ;
+ }
+
+ # The actual build actions are here; if we did this work in the actions
+ # clause we would have to form a valid command line containing the
+ # result of @(...) below (the name of the XML file).
+ #
+ rule out-xml.generate-action ( args * : xml-file
+ : command status start end user system : output ? )
+ {
+ local contents =
+ [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ;
+ local f = @($(xml-file):E=$(contents)) ;
+ }
+
+ # Nothing to do here; the *real* actions happen in
+ # out-xml.generate-action.
+ actions quietly out-xml.generate { }
+
+ # Define the out-xml file target, which depends on all the targets so
+ # that it runs the collection after the targets have run.
+ out-xml $(.out-xml) : $(actual-targets) ;
+
+ # Set up a global __ACTION_RULE__ that records all the available
+ # statistics about each actual target in a variable "on" the --out-xml
+ # target.
+ #
+ rule out-xml.collect ( xml-file : target : command status start end user
+ system : output ? )
+ {
+ local nl = "
+" ;
+ # Open the action with some basic info.
             .contents on $(xml-file) +=
- "$(nl) <properties>"
- ;
- for local prop in [ $(action-props).raw ]
+ "$(nl) <action status=\"$(status)\" start=\"$(start)\" end=\"$(end)\" user=\"$(user)\" system=\"$(system)\">" ;
+
+ # If we have an action object we can print out more detailed info.
+ local action = [ on $(target) return $(.action) ] ;
+ if $(action)
             {
- local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ;
+ local action-name = [ $(action).action-name ] ;
+ local action-sources = [ $(action).sources ] ;
+ local action-props = [ $(action).properties ] ;
+
+ # The qualified name of the action which we created the target.
+ .contents on $(xml-file) +=
+ "$(nl) <name><![CDATA[$(action-name)]]></name>" ;
+
+ # The sources that made up the target.
+ .contents on $(xml-file) +=
+ "$(nl) <sources>" ;
+ for local source in $(action-sources)
+ {
+ local source-actual = [ $(source).actual-name ] ;
+ .contents on $(xml-file) +=
+ "$(nl) <source><![CDATA[$(source-actual)]]></source>" ;
+ }
+ .contents on $(xml-file) +=
+ "$(nl) </sources>" ;
+
+ # The properties that define the conditions under which the
+ # target was built.
                 .contents on $(xml-file) +=
- "$(nl) <property name=\"$(prop-name)\"><![CDATA[$(prop:G=)]]></property>"
- ;
+ "$(nl) <properties>" ;
+ for local prop in [ $(action-props).raw ]
+ {
+ local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ;
+ .contents on $(xml-file) +=
+ "$(nl) <property name=\"$(prop-name)\"><![CDATA[$(prop:G=)]]></property>" ;
+ }
+ .contents on $(xml-file) +=
+ "$(nl) </properties>" ;
             }
+
+ local locate = [ on $(target) return $(LOCATE) ] ;
+ locate ?= "" ;
             .contents on $(xml-file) +=
- "$(nl) </properties>"
- ;
+ "$(nl) <jam-target><![CDATA[$(target)]]></jam-target>"
+ "$(nl) <path><![CDATA[$(target:G=:R=$(locate))]]></path>"
+ "$(nl) <command><![CDATA[$(command)]]></command>"
+ "$(nl) <output><![CDATA[$(output)]]></output>" ;
+ .contents on $(xml-file) +=
+ "$(nl) </action>" ;
         }
 
- local locate = [ on $(target) return $(LOCATE) ] ;
- locate ?= "" ;
- .contents on $(xml-file) +=
- "$(nl) <jam-target><![CDATA[$(target)]]></jam-target>"
- "$(nl) <path><![CDATA[$(target:G=:R=$(locate))]]></path>"
- "$(nl) <command><![CDATA[$(command)]]></command>"
- "$(nl) <output><![CDATA[$(output)]]></output>"
- ;
- .contents on $(xml-file) +=
- "$(nl) </action>"
- ;
- }
-
- # When no __ACTION_RULE__ is set "on" a target, the search falls
- # back to the global module
- module
- {
- __ACTION_RULE__ = build-system.out-xml.collect
- [ modules.peek build-system : .out-xml ] ;
+ # When no __ACTION_RULE__ is set "on" a target, the search falls back to
+ # the global module.
+ module
+ {
+ __ACTION_RULE__ = build-system.out-xml.collect
+ [ modules.peek build-system : .out-xml ] ;
+ }
     }
-}
 
-NOTFILE all ;
-DEPENDS all : $(actual-targets) ;
 
-if $(bjam-targets)
-{
- UPDATE $(bjam-targets:G=e) $(.out-xml) ;
-}
-else if $(cleanall)
-{
- UPDATE clean-all ;
-}
-else if $(clean)
-{
- local to-clean ;
- for local t in [ virtual-target.all-targets ]
- {
- local p = [ $(t).project ] ;
+ # TODO: See if this 'NOTFILE all' statement can be moved below to the
+ # default case where the 'all' target is actually requested to be built.
+ # Check for other Jam scripts manually setting a dependency for this target.
+ NOTFILE all ;
 
- # Remove only derived targets.
- if [ $(t).action ]
- {
- if $(t) in $(targets-to-clean)
- || [ is-child [ $(p).project-module ] ] = true
- {
- to-clean += $(t) ;
- }
- }
+
+ # And now that all the actual raw Jam targets and all the dependencies
+ # between them have been prepared (or we have everything set so we can
+ # easily prepare them) all that is left is to tell Jam to update those
+ # targets.
+ if $(bjam-targets)
+ {
+ UPDATE $(bjam-targets:G=e) $(.out-xml) ;
     }
- local to-clean-actual ;
- for local t in $(to-clean)
+ else if $(cleanall)
     {
- to-clean-actual += [ $(t).actualize ] ;
+ UPDATE clean-all ;
+ }
+ else if $(clean)
+ {
+ common.Clean clean : [ actual-clean-targets ] ;
+ UPDATE clean ;
+ }
+ else
+ {
+ DEPENDS all : $(actual-targets) ;
+ UPDATE all $(.out-xml) ;
     }
- common.Clean clean : $(to-clean-actual) ;
- UPDATE clean ;
-}
-else
-{
- UPDATE all $(.out-xml) ;
 }

Modified: trunk/tools/build/v2/build/project.jam
==============================================================================
--- trunk/tools/build/v2/build/project.jam (original)
+++ trunk/tools/build/v2/build/project.jam 2008-01-06 01:15:39 EST (Sun, 06 Jan 2008)
@@ -422,10 +422,14 @@
     local jamroot ;
 
     local parent-module ;
- if $(module-name) = site-config
+ 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 ;


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