2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 13:02:11 +00:00

Wholesale Boost.Build merge from the trunk

[SVN r49353]
This commit is contained in:
Vladimir Prus
2008-10-16 08:42:03 +00:00
parent b3093bb1b7
commit ddc88906c1
262 changed files with 6651 additions and 6299 deletions

View File

@@ -5,7 +5,7 @@
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# 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.
# main() routine. It is invoked by the bootstrapping code in bootstrap.jam.
import build-request ;
import builtin ;
@@ -22,6 +22,7 @@ import regex ;
import sequence ;
import targets ;
import toolset ;
import utility ;
import version ;
import virtual-target ;
@@ -90,8 +91,8 @@ rule 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 where it is sometimes needed to get the location of other components
# (e.g. BoostBook files) and it is convenient to use locations relative to the
# Boost Build path.
#
rule location ( )
@@ -138,7 +139,7 @@ local rule actual-clean-targets ( )
local targets-to-clean ;
for local t in $(.results-of-main-targets)
{
# Don't include roots or sources.
# Do not include roots or sources.
targets-to-clean += [ virtual-target.traverse $(t) ] ;
}
targets-to-clean = [ sequence.unique $(targets-to-clean) ] ;
@@ -169,8 +170,8 @@ local rule actual-clean-targets ( )
# 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
# only invoked when there is no Jamfile in ".". This code somewhat duplicates
# code in project-target.find but we can not reuse that code without a
# project-targets instance.
#
local rule find-target ( target-id )
@@ -305,7 +306,7 @@ local rule load-configuration-files
if $(uq)
{
test-config = $(uq) ;
}
}
if $(test-config)
{
local where =
@@ -350,6 +351,9 @@ local rule load-configuration-files
local user-config = [ MATCH ^--user-config=(.*)$ : $(.argv) ] ;
user-config = $(user-config[-1]) ;
user-config ?= [ os.environ BOOST_BUILD_USER_CONFIG ] ;
# Special handling for the case when the OS does not strip the quotes
# around the file name, as is the case when using Cygwin bash.
user-config = [ utility.unquote $(user-config) ] ;
local explicitly-requested = $(user-config) ;
user-config ?= user-config.jam ;
@@ -366,7 +370,8 @@ local rule load-configuration-files
if $(.debug-config)
{
ECHO "notice: Loading explicitly specified user configuration file:" ;
ECHO "notice: Loading explicitly specified user"
"configuration file:" ;
ECHO " $(user-config)" ;
}
@@ -407,14 +412,14 @@ local rule process-explicit-toolset-requests
if $(.debug-config)
{
ECHO "notice: [cmdline-cfg] Detected command-line request for"
ECHO notice: [cmdline-cfg] Detected command-line request for
$(toolset-version): "toolset=" $(toolset) "version="
$(version) ;
}
# If the toolset isn't known, configure it now.
# If the toolset is not known, configure it now.
local known ;
if $(toolset) in [ feature.values <toolset> ]
if $(toolset) in [ feature.values <toolset> ]
{
known = true ;
}
@@ -423,6 +428,14 @@ local rule process-explicit-toolset-requests
{
known = ;
}
# TODO: we should do 'using $(toolset)' in case no version has been
# specified and there are no versions defined for the given toolset to
# allow the toolset to configure its default version. For this we need
# to know how to detect whether a given toolset has any versions
# defined. An alternative would be to do this whenever version is not
# specified but that would require that toolsets correctly handle the
# case when their default version is configured multiple times which
# should be checked for all existing toolsets first.
if ! $(known)
{
@@ -443,13 +456,14 @@ local rule process-explicit-toolset-requests
}
# Make sure we get an appropriate property into the build request in
# case toolset was specified using the "--toolset=..." command-line
# case toolset has been specified using the "--toolset=..." command-line
# option form.
if ! $(t) in $(.argv) && ! $(t) in $(feature-toolsets)
{
if $(.debug-config)
{
ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ;
ECHO notice: [cmdline-cfg] adding toolset=$(t) to the build
request. ;
}
extra-properties += toolset=$(t) ;
}
@@ -546,7 +560,7 @@ local rule should-clean-project ( project )
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: 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" ;
@@ -615,7 +629,7 @@ local rule should-clean-project ( project )
local virtual-targets ;
local actual-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
@@ -641,7 +655,7 @@ local rule should-clean-project ( project )
if ! $(t)
{
ECHO "notice: could not find main target" $(id) ;
ECHO "notice: assuming it's a name of file to create." ;
ECHO "notice: assuming it is a name of file to create." ;
explicitly-requested-files += $(id) ;
}
else

View File

@@ -33,8 +33,8 @@ class alias-target-class : basic-target
rule __init__ ( name : project : sources * : requirements *
: default-build * : usage-requirements * )
{
basic-target.__init__ $(name) : $(project) : $(sources) : $(requirements)
: $(default-build) : $(usage-requirements) ;
basic-target.__init__ $(name) : $(project) : $(sources) :
$(requirements) : $(default-build) : $(usage-requirements) ;
}
rule construct ( name : source-targets * : property-set )
@@ -45,16 +45,16 @@ class alias-target-class : basic-target
rule compute-usage-requirements ( subvariant )
{
local base = [ basic-target.compute-usage-requirements $(subvariant) ] ;
# Add source's usage requirement. If we don't do this, "alias" does not
# look like a 100% alias.
return [ $(base).add [ $(subvariant).sources-usage-requirements ] ] ;
}
}
# Declares the 'alias' target. It will build sources, and return them unaltered.
# Declares the 'alias' target. It will process its sources virtual-targets by
# returning them unaltered as its own constructed virtual-targets.
#
rule alias ( name : sources * : requirements * : default-build * : usage-requirements * )
rule alias ( name : sources * : requirements * : default-build * :
usage-requirements * )
{
local project = [ project.current ] ;
@@ -62,8 +62,10 @@ rule alias ( name : sources * : requirements * : default-build * : usage-require
[ new alias-target-class $(name) : $(project)
: [ targets.main-target-sources $(sources) : $(name) : no-renaming ]
: [ targets.main-target-requirements $(requirements) : $(project) ]
: [ targets.main-target-default-build $(default-build) : $(project) ]
: [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
: [ targets.main-target-default-build $(default-build) : $(project)
]
: [ targets.main-target-usage-requirements $(usage-requirements) :
$(project) ]
] ;
}

View File

@@ -13,6 +13,7 @@ import string ;
# Transform property-set by applying f to each component property.
#
local rule apply-to-property-set ( f property-set )
{
local properties = [ feature.split $(property-set) ] ;
@@ -20,9 +21,10 @@ local rule apply-to-property-set ( f property-set )
}
# Expand the given build request by combining all property-sets which don't
# Expand the given build request by combining all property-sets which do not
# specify conflicting non-free features. Expects all the project files to
# already be loaded.
#
rule expand-no-defaults ( property-sets * )
{
# First make all features and subfeatures explicit.
@@ -38,6 +40,7 @@ rule expand-no-defaults ( property-sets * )
# Implementation of x-product, below. Expects all the project files to already
# be loaded.
#
local rule x-product-aux ( property-sets + )
{
local result ;
@@ -49,7 +52,7 @@ local rule x-product-aux ( property-sets + )
{
local x-product-seen ;
{
# Don't mix in any conflicting features.
# Do not mix in any conflicting features.
local x-product-used = $(x-product-used) $(f) ;
if $(property-sets[2])
@@ -61,8 +64,8 @@ local rule x-product-aux ( property-sets + )
result ?= $(property-sets[1]) ;
}
# If we didn't encounter a conflicting feature lower down, don't recurse
# again.
# If we did not encounter a conflicting feature lower down, do not
# recurse again.
if ! [ set.intersection $(f) : $(x-product-seen) ]
{
property-sets = ;
@@ -76,7 +79,7 @@ local rule x-product-aux ( property-sets + )
result += [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ;
}
# Note that we've seen these features so that higher levels will recurse
# Note that we have seen these features so that higher levels will recurse
# again without them set.
x-product-seen += $(f) $(seen) ;
return $(result) ;
@@ -86,6 +89,7 @@ local rule x-product-aux ( property-sets + )
# Return the cross-product of all elements of property-sets, less any that would
# contain conflicting values for single-valued features. Expects all the project
# files to already be loaded.
#
local rule x-product ( property-sets * )
{
if $(property-sets).non-empty
@@ -101,6 +105,7 @@ local rule x-product ( property-sets * )
# Returns true if either 'v' or the part of 'v' before the first '-' symbol is
# an implicit value. Expects all the project files to already be loaded.
#
local rule looks-like-implicit-value ( v )
{
if [ feature.is-implicit-value $(v) ]
@@ -123,6 +128,7 @@ local rule looks-like-implicit-value ( v )
# "vector" means container.jam's "vector"). First is the set of targets
# specified in the command line, and second is the set of requested build
# properties. Expects all the project files to already be loaded.
#
rule from-command-line ( command-line * )
{
local targets ;
@@ -144,8 +150,8 @@ rule from-command-line ( command-line * )
if [ MATCH "(.*=.*)" : $(e) ]
|| [ looks-like-implicit-value $(e:D=) : $(feature-space) ]
{
properties += [ convert-command-line-element $(e)
: $(feature-space) ] ;
properties += [ convert-command-line-element $(e) :
$(feature-space) ] ;
}
else
{
@@ -165,25 +171,42 @@ rule from-command-line ( command-line * )
# Converts one element of command line build request specification into internal
# form. Expects all the project files to already be loaded.
#
local rule convert-command-line-element ( e )
{
local result ;
local parts = [ regex.split $(e) "/" ] ;
for local p in $(parts)
while $(parts)
{
local p = $(parts[1]) ;
local m = [ MATCH "([^=]*)=(.*)" : $(p) ] ;
local lresult ;
local feature ;
local values ;
if $(m)
{
local feature = $(m[1]) ;
local values = [ regex.split $(m[2]) "," ] ;
feature = $(m[1]) ;
values = [ regex.split $(m[2]) "," ] ;
lresult = <$(feature)>$(values) ;
}
else
{
lresult = [ regex.split $(p) "," ] ;
}
if $(feature) && free in [ feature.attributes $(feature) ]
{
# If we have free feature, then the value is everything
# until the end of the command line token. Slashes in
# the following string are not taked to mean separation
# of properties. Commas are also not interpreted specially.
values = $(values:J=,) ;
values = $(values) $(parts[2-]) ;
values = $(values:J=/) ;
lresult = <$(feature)>$(values) ;
parts = ;
}
if ! [ MATCH (.*-.*) : $(p) ]
{
# property.validate cannot handle subfeatures, so we avoid the check
@@ -202,6 +225,8 @@ local rule convert-command-line-element ( e )
{
result = $(result)/$(lresult) ;
}
parts = $(parts[2-]) ;
}
return $(result) ;

View File

@@ -357,7 +357,8 @@ local rule expand-subfeatures-aux (
local subvalue = $(subvalues[1]) ; # Pop the head off of subvalues.
subvalues = $(subvalues[2-]) ;
local subfeature = [ find-implied-subfeature $(feature) $(subvalue) : $(value) ] ;
local subfeature = [ find-implied-subfeature $(feature) $(subvalue) :
$(value) ] ;
# If no subfeature was found reconstitute the value string and use that.
if ! $(subfeature)
@@ -433,8 +434,8 @@ local rule extend-feature ( feature : values * )
}
if ! $($(feature).values)
{
# This is the first value specified for this feature,
# take it as default value
# This is the first value specified for this feature so make it be the
# default.
$(feature).default = $(values[1]) ;
}
$(feature).values += $(values) ;
@@ -584,7 +585,7 @@ rule extend ( feature-or-property subfeature ? : values * )
}
else
{
# If no subfeature was specified, we didn't expect to see a
# If no subfeature was specified, we do not expect to see a
# value-string.
if $(value-string)
{
@@ -771,7 +772,8 @@ local rule is-subfeature-of ( parent-property f )
{
# The feature has the form <topfeature-topvalue:subfeature>, e.g.
# <toolset-msvc:version>.
local feature-value = [ split-top-feature $(specific-subfeature[1]) ] ;
local feature-value = [ split-top-feature $(specific-subfeature[1])
] ;
if <$(feature-value[1])>$(feature-value[2]) = $(parent-property)
{
return true ;

View File

@@ -36,12 +36,19 @@
# that as early as possible. Specifically, this is done after invoking each
# generator. TODO: An example is needed to document the rationale for trying
# extra target conversion at that point.
#
# In order for the system to be able to use a specific generator instance 'when
# needed', the instance needs to be registered with the system using
# generators.register() or one of its related rules. Unregistered generators may
# only be run explicitly and will not be considered by Boost.Build when when
# converting between given target types.
import "class" : new ;
import errors ;
import property-set ;
import sequence ;
import set ;
import type ;
import utility ;
import virtual-target ;
@@ -52,8 +59,76 @@ if "--debug-generators" in [ modules.peek : ARGV ]
}
# Updated cached viable source target type information as needed after a new
# target type gets defined. This is needed because if a target type is a viable
# source target type for some generator then all of the target type's derived
# target types should automatically be considered as viable source target types
# for the same generator as well. Does nothing if a non-derived target type is
# passed to it.
#
rule update-cached-information-with-a-new-type ( type )
{
local base-type = [ type.base $(type) ] ;
if $(base-type)
{
for local g in $(.vstg-cached-generators)
{
if $(base-type) in $(.vstg.$(g))
{
.vstg.$(g) += $(type) ;
}
}
for local t in $(.vst-cached-types)
{
if $(base-type) in $(.vst.$(t))
{
.vst.$(t) += $(type) ;
}
}
}
}
# Clears cached viable source target type information except for target types
# and generators with all source types listed as viable. Should be called when
# something invalidates those cached values by possibly causing some new source
# types to become viable.
#
local rule invalidate-extendable-viable-source-target-type-cache ( )
{
local generators-with-cached-source-types = $(.vstg-cached-generators) ;
.vstg-cached-generators = ;
for local g in $(generators-with-cached-source-types)
{
if $(.vstg.$(g)) = *
{
.vstg-cached-generators += $(g) ;
}
else
{
.vstg.$(g) = ;
}
}
local types-with-cached-source-types = $(.vst-cached-types) ;
.vst-cached-types = ;
for local t in $(types-with-cached-source-types)
{
if $(.vst.$(t)) = *
{
.vst-cached-types += $(t) ;
}
else
{
.vst.$(t) = ;
}
}
}
# Outputs a debug message if generators debugging is on. Each element of
# 'message' is checked to see if it's class instance. If so, instead of the
# 'message' is checked to see if it is a class instance. If so, instead of the
# value, the result of 'str' call is output.
#
local rule generators.dout ( message * )
@@ -83,18 +158,8 @@ local rule decrease-indent ( )
}
# Takes a vector of 'virtual-target' instances and makes a normalized
# representation, which is the same for given set of targets, regardless of
# their order.
# Models a generator.
#
rule normalize-target-list ( targets )
{
local v = [ $(targets).get ] ;
$(targets).set $(v[1]) [ sequence.insertion-sort $(v[2-]) : utility.less ] ;
}
# Creates a generator
class generator
{
import generators : indent increase-indent decrease-indent generators.dout ;
@@ -108,7 +173,8 @@ class generator
import "class" : new ;
import property ;
EXPORT class@generator : indent increase-indent decrease-indent generators.dout ;
EXPORT class@generator : indent increase-indent decrease-indent
generators.dout ;
rule __init__ (
id # Identifies the generator - should be name
@@ -146,9 +212,9 @@ class generator
{
# Create three parallel lists: one with the list of target types,
# and two other with prefixes and postfixes to be added to target
# name. We use parallel lists for prefix and postfix (as opposed
# to mapping), because given target type might occur several times,
# for example "H H(%_symbols)".
# name. We use parallel lists for prefix and postfix (as opposed to
# mapping), because given target type might occur several times, for
# example "H H(%_symbols)".
local m = [ MATCH ([^\\(]*)(\\((.*)%(.*)\\))? : $(e) ] ;
self.target-types += $(m[1]) ;
self.name-prefix += $(m[3]:E="") ;
@@ -160,7 +226,7 @@ class generator
sequence.transform type.validate : $(self.target-types) ;
}
############## End of constructor #################
################# End of constructor #################
rule id ( )
{
@@ -175,8 +241,8 @@ class generator
}
# Returns the list of target types that this generator produces. It is
# assumed to be always the same -- i.e. it cannot change depending list of
# sources.
# assumed to be always the same -- i.e. it can not change depending on some
# provided list of sources.
#
rule target-types ( )
{
@@ -187,6 +253,7 @@ class generator
# set must be present in build properties if this generator is to be used.
# If result has grist-only element, that build properties must include some
# value of that feature.
#
# XXX: remove this method?
#
rule requirements ( )
@@ -199,7 +266,7 @@ class generator
#
rule match-rank ( property-set-to-match )
{
# See if generator's requirements are satisfied by 'properties'. Treat a
# See if generator requirements are satisfied by 'properties'. Treat a
# feature name in requirements (i.e. grist-only element), as matching
# any value of the feature.
local all-requirements = [ requirements ] ;
@@ -218,8 +285,8 @@ class generator
}
local properties-to-match = [ $(property-set-to-match).raw ] ;
if $(property-requirements) in $(properties-to-match)
&& $(feature-requirements) in $(properties-to-match:G)
if $(property-requirements) in $(properties-to-match) &&
$(feature-requirements) in $(properties-to-match:G)
{
return true ;
}
@@ -235,14 +302,12 @@ class generator
#
rule clone ( new-id : new-toolset-properties + )
{
return [ new $(__class__) $(new-id) $(self.composing)
: $(self.source-types)
: $(self.target-types-and-names)
# Note: this does not remove any subfeatures of <toolset>
# which might cause problems
: [ property.change $(self.requirements) : <toolset> ]
$(new-toolset-properties)
] ;
return [ new $(__class__) $(new-id) $(self.composing) :
$(self.source-types) : $(self.target-types-and-names) :
# Note: this does not remove any subfeatures of <toolset> which
# might cause problems.
[ property.change $(self.requirements) : <toolset> ]
$(new-toolset-properties) ] ;
}
# Creates another generator that is the same as $(self), except that if
@@ -265,23 +330,24 @@ class generator
}
}
return [ new $(__class__) $(self.id) $(self.composing)
: $(self.source-types)
: $(target-types)
: $(self.requirements)
] ;
return [ new $(__class__) $(self.id) $(self.composing) :
$(self.source-types) : $(target-types) : $(self.requirements) ] ;
}
# Tries to invoke this generator on the given sources. Returns a list of
# generated targets (instances of 'virtual-target'). Returning nothing from
# run indicates that the generator was unable to create the target.
# generated targets (instances of 'virtual-target') and optionally a set of
# properties to be added to the usage-requirements for all the generated
# targets. Returning nothing from run indicates that the generator was
# unable to create the target.
#
rule run ( project # Project for which the targets are generated
name ? # Determines the name of 'name' attribute for all
# generated targets. See 'generated-targets' method.
: property-set # Desired properties for generated targets.
: sources + # Source targets.
)
rule run
(
project # Project for which the targets are generated.
name ? # Used when determining the 'name' attribute for all
# generated targets. See the 'generated-targets' method.
: property-set # Desired properties for generated targets.
: sources + # Source targets.
)
{
generators.dout [ indent ] " ** generator" $(self.id) ;
generators.dout [ indent ] " composing:" $(self.composing) ;
@@ -291,7 +357,7 @@ class generator
errors.error "Unsupported source/source-type combination" ;
}
# We don't run composing generators if no name is specified. The reason
# We do not run composing generators if no name is specified. The reason
# is that composing generator combines several targets, which can have
# different names, and it cannot decide which name to give for produced
# target. Therefore, the name must be passed.
@@ -300,7 +366,7 @@ class generator
# the top-level of a transformation graph, or if their name is passed
# explicitly. Thus, we dissallow composing generators in the middle. For
# example, the transformation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE
# won't be allowed (the OBJ -> STATIC_LIB generator is composing)
# will not be allowed as the OBJ -> STATIC_LIB generator is composing.
if ! $(self.composing) || $(name)
{
run-really $(project) $(name) : $(property-set) : $(sources) ;
@@ -311,7 +377,7 @@ class generator
{
# Targets that this generator will consume directly.
local consumed = ;
# Targets that can't be consumed and will be returned as-is.
# Targets that can not be consumed and will be returned as-is.
local bypassed = ;
if $(self.composing)
@@ -328,8 +394,8 @@ class generator
local result ;
if $(consumed)
{
result = [ construct-result $(consumed) : $(project) $(name)
: $(property-set) ] ;
result = [ construct-result $(consumed) : $(project) $(name) :
$(property-set) ] ;
}
if $(result)
@@ -346,14 +412,20 @@ class generator
# Constructs the dependency graph to be returned by this generator.
#
rule construct-result (
consumed + # Already prepared list of consumable targets
# If generator requires several source files will contain
# exactly len $(self.source-types) targets with matching
# types. Otherwise, might contain several targets with the
# type of $(self.source-types[1]).
rule construct-result
(
consumed + # Already prepared list of consumable targets.
# Composing generators may receive multiple sources
# all of which will have types matching those in
# $(self.source-types). Non-composing generators with
# multiple $(self.source-types) will receive exactly
# len $(self.source-types) sources with types matching
# those in $(self.source-types). And non-composing
# generators with only a single source type may
# receive multiple sources with all of them of the
# type listed in $(self.source-types).
: project name ?
: property-set # Properties to be used for all actions create here.
: property-set # Properties to be used for all actions created here.
)
{
local result ;
@@ -363,16 +435,14 @@ class generator
{
for local r in $(consumed)
{
result += [ generated-targets $(r) : $(property-set) : $(project) $(name) ] ; #(targets) ;
result += [ generated-targets $(r) : $(property-set) :
$(project) $(name) ] ;
}
}
else
else if $(consumed)
{
if $(consumed)
{
result += [ generated-targets $(consumed) : $(property-set)
: $(project) $(name) ] ;
}
result += [ generated-targets $(consumed) : $(property-set) :
$(project) $(name) ] ;
}
return $(result) ;
}
@@ -383,11 +453,11 @@ class generator
{
# The simple case if when a name of source has single dot. Then, we take
# the part before dot. Several dots can be caused by:
# - Using source file like a.host.cpp
# - A type which suffix has a dot. Say, we can type 'host_cpp' with
# - using source file like a.host.cpp, or
# - a type whose suffix has a dot. Say, we can type 'host_cpp' with
# extension 'host.cpp'.
# In the first case, we want to take the part up to the last dot. In the
# second case -- no sure, but for now take the part up to the last dot
# second case -- not sure, but for now take the part up to the last dot
# too.
name = [ utility.basename [ $(sources[1]).name ] ] ;
@@ -410,11 +480,11 @@ class generator
# will be the list of virtual-target, which has the same length as the
# 'target-types' attribute and with corresponding types.
#
# When 'name' is empty, all source targets must have the same value of the
# 'name' attribute, which will be used instead of the 'name' argument.
# When 'name' is empty, all source targets must have the same 'name'
# attribute value, which will be used instead of the 'name' argument.
#
# The value of 'name' attribute for each generated target will be equal to
# the 'name' parameter if there's no name pattern for this type. Otherwise,
# The 'name' attribute value for each generated target will be equal to
# the 'name' parameter if there is no name pattern for this type. Otherwise,
# the '%' symbol in the name pattern will be replaced with the 'name'
# parameter to obtain the 'name' attribute.
#
@@ -424,8 +494,8 @@ class generator
# determines the basename of a file.
#
# Note that this pattern mechanism has nothing to do with implicit patterns
# in make. It is a way to produce target which name is different for name of
# source.
# in make. It is a way to produce a target whose name is different than the
# name of its source.
#
rule generated-targets ( sources + : property-set : project name ? )
{
@@ -434,7 +504,7 @@ class generator
name = [ determine-output-name $(sources) ] ;
}
# Assign an action for each target
# Assign an action for each target.
local action = [ action-class ] ;
local a = [ class.new $(action) $(sources) : $(self.id) :
$(property-set) ] ;
@@ -456,30 +526,33 @@ class generator
return [ sequence.transform virtual-target.register : $(targets) ] ;
}
# Attempts to convert 'source' to the types that this generator can handle.
# The intention is to produce the set of targets can should be used when
# generator is run.
# Attempts to convert 'sources' to targets of types that this generator can
# handle. The intention is to produce the set of targets that can be used
# when the generator is run.
#
rule convert-to-consumable-types ( project name ? :
property-set : sources +
: only-one ? # Convert 'source' to only one of the source types. If
# there's more that one possibility, report an error.
: consumed-var # Name of the variable which recieves all targets which
# can be consumed.
bypassed-var # Name of the variable which recieves all targets which
# cannot be consumed
rule convert-to-consumable-types
(
project name ?
: property-set
: sources +
: only-one ? # Convert 'source' to only one of the source types. If
# there is more that one possibility, report an error.
: consumed-var # Name of the variable which receives all targets which
# can be consumed.
bypassed-var # Name of the variable which receives all targets which
# can not be consumed.
)
{
# We're likely to be passed 'consumed' and 'bypassed' var names. Use "_"
# to avoid name conflicts.
# We are likely to be passed 'consumed' and 'bypassed' var names. Use
# '_' to avoid name conflicts.
local _consumed ;
local _bypassed ;
local missing-types ;
if $(sources[2])
{
# Don't know how to handle several sources yet. Just try to pass the
# request to other generator
# Do not know how to handle several sources yet. Just try to pass
# the request to other generator.
missing-types = $(self.source-types) ;
}
else
@@ -502,11 +575,11 @@ class generator
if $(missing-types)
{
local transformed = [ generators.construct-types $(project) $(name)
: $(missing-types) : $(property-set) : $(sources) ] ;
: $(missing-types) : $(property-set) : $(sources) ] ;
# Add targets of right type to 'consumed'. Add others to 'bypassed'.
# The 'generators.construct' rule has done its best to convert
# everything to the required type. There's no need to rerun it on
# everything to the required type. There is no need to rerun it on
# targets of different types.
# NOTE: ignoring usage requirements.
@@ -533,12 +606,12 @@ class generator
# from Y, X_2 will be added to 'bypassed'. Likewise, when creating X_2
# from Y, X_1 will be added to 'bypassed', but they are also in
# 'consumed'. We have to remove them from bypassed, so that generators
# up the call stack don't try to convert them.
# up the call stack do not try to convert them.
# In this particular case, X_1 instance in 'consumed' and X_1 instance
# in 'bypassed' will be the same: because they have the same source and
# action name, and 'virtual-target.register' won't allow two different
# instances. Therefore, it's OK to use 'set.difference'.
# action name, and 'virtual-target.register' will not allow two
# different instances. Therefore, it is OK to use 'set.difference'.
_bypassed = [ set.difference $(_bypassed) : $(_consumed) ] ;
@@ -546,10 +619,11 @@ class generator
$(bypassed-var) += $(_bypassed) ;
}
# Converts several files to consumable types.
# Converts several files to consumable types. Called for composing
# generators only.
#
rule convert-multiple-sources-to-consumable-types
( project : property-set : sources * : consumed-var bypassed-var )
rule convert-multiple-sources-to-consumable-types ( project : property-set :
sources * : consumed-var bypassed-var )
{
# We process each source one-by-one, trying to convert it to a usable
# type.
@@ -558,8 +632,8 @@ class generator
local _c ;
local _b ;
# TODO: need to check for failure on each source.
convert-to-consumable-types $(project) : $(property-set)
: $(source) : true : _c _b ;
convert-to-consumable-types $(project) : $(property-set) : $(source)
: true : _c _b ;
if ! $(_c)
{
generators.dout [ indent ] " failed to convert " $(source) ;
@@ -579,15 +653,15 @@ class generator
for local st in $(source-types)
{
# The 'source' if of right type already)
if $(real-source-type) = $(st) ||
[ type.is-derived $(real-source-type) $(st) ]
# The 'source' if of the right type already.
if $(real-source-type) = $(st) || [ type.is-derived
$(real-source-type) $(st) ]
{
$(consumed-var) += $(source) ;
}
else
{
$(missing-types-var) += $(st) ;
$(missing-types-var) += $(st) ;
}
}
}
@@ -602,33 +676,28 @@ class generator
}
.generators = ;
# Registers a new generator instance 'g'.
#
rule register ( g )
{
.generators += $(g) ;
# A generator can produce several targets of the same type. We want unique
# occurence of that generator in .generators.$(t) in that case, otherwise,
# it will be tried twice and we'll get false ambiguity.
# occurrence of that generator in .generators.$(t) in that case, otherwise,
# it will be tried twice and we will get a false ambiguity.
for local t in [ sequence.unique [ $(g).target-types ] ]
{
.generators.$(t) += $(g) ;
}
# Update the set of generators for toolset
# Update the set of generators for toolset.
# TODO: should we check that generator with this id is not already
# registered. For example, the fop.jam module intentionally declared two
# generators with the same id, so such check will break it.
local id = [ $(g).id ] ;
# Some generators have multiple periods in their name, so the normal
# $(id:S=) won't generate the right toolset name. E.g. if id =
# = gcc.compile.c++, then .generators-for-toolset.$(id:S=) will append to
# Some generators have multiple periods in their name, so a simple $(id:S=)
# will not generate the right toolset name. E.g. if id = gcc.compile.c++,
# then .generators-for-toolset.$(id:S=) will append to
# .generators-for-toolset.gcc.compile, which is a separate value from
# .generators-for-toolset.gcc. Correcting this makes generator inheritance
# work properly. See also inherit-generators in the toolset module.
@@ -638,35 +707,60 @@ rule register ( g )
base = $(base:B) ;
}
.generators-for-toolset.$(base) += $(g) ;
# After adding a new generator that can construct new target types, we need
# to clear the related cached viable source target type information for
# constructing a specific target type or using a specific generator. Cached
# viable source target type lists affected by this are those containing any
# of the target types constructed by the new generator or any of their base
# target types.
#
# A more advanced alternative to clearing that cached viable source target
# type information would be to expand it with additional source types or
# even better - mark it as needing to be expanded on next use.
#
# Also see the http://thread.gmane.org/gmane.comp.lib.boost.build/19077
# mailing list thread for an even more advanced idea of how we could convert
# Boost Build's Jamfile processing, target selection and generator selection
# into separate steps which would prevent these caches from ever being
# invalidated.
#
# For now we just clear all the cached viable source target type information
# that does not simply state 'all types' and may implement a more detailed
# algorithm later on if it becomes needed.
invalidate-extendable-viable-source-target-type-cache ;
}
# Creates new instance of the 'generator' class and registers it. Returns the
# created instance. Rationale: the instance is returned so that it's possible to
# first register a generator and then call the 'run' method on that generator,
# bypassing all generator selection.
# Creates a new non-composing 'generator' class instance and registers it.
# Returns the created instance. Rationale: the instance is returned so that it
# is possible to first register a generator and then call its 'run' method,
# bypassing the whole generator selection process.
#
rule register-standard ( id : source-types * : target-types + : requirements * )
{
local g = [ new generator $(id) : $(source-types) : $(target-types)
: $(requirements) ] ;
local g = [ new generator $(id) : $(source-types) : $(target-types) :
$(requirements) ] ;
register $(g) ;
return $(g) ;
}
# Creates new instance of the 'composing-generator' class and registers it.
# Creates a new composing 'generator' class instance and registers it.
#
rule register-composing ( id : source-types * : target-types + : requirements * )
rule register-composing ( id : source-types * : target-types + : requirements *
)
{
local g = [ new generator $(id) true : $(source-types) : $(target-types)
: $(requirements) ] ;
local g = [ new generator $(id) true : $(source-types) : $(target-types) :
$(requirements) ] ;
register $(g) ;
return $(g) ;
}
# Returns all generators which belong to 'toolset', i.e. whose ids are
# Returns all generators belonging to the given 'toolset', i.e. whose ids are
# '$(toolset).<something>'.
#
rule generators-for-toolset ( toolset )
@@ -677,11 +771,11 @@ rule generators-for-toolset ( toolset )
# Make generator 'overrider-id' be preferred to 'overridee-id'. If, when
# searching for generators that could produce a target of a certain type, both
# those generators are amoung viable generators, the overridden generator is
# those generators are among viable generators, the overridden generator is
# immediately discarded.
#
# The overridden generators are discarded immediately after computing the list
# of viable generators, before running any of them.
# of viable generators but before running any of them.
#
rule override ( overrider-id : overridee-id )
{
@@ -689,30 +783,34 @@ rule override ( overrider-id : overridee-id )
}
# Set if results of the current generators search are going to be cached. This
# means no futher attempts to cache generators search should be made.
.caching = ;
# Returns a list of source type which can possibly be converted to 'target-type'
# by some chain of generator invocation.
#
# More formally, takes all generators for 'target-type' and returns union of
# source types for those generators and result of calling itself recirsively on
# More formally, takes all generators for 'target-type' and returns a union of
# source types for those generators and result of calling itself recursively on
# source types.
#
# Returns '*' in case any type should be considered a viable source type for the
# given type.
#
local rule viable-source-types-real ( target-type )
{
local generators ;
local t = [ type.all-bases $(target-type) ] ;
local result ;
# 't' is the list of types which have not yet been processed.
# 't0' is the initial list of target types we need to process to get a list
# of their viable source target types. New target types will not be added to
# this list.
local t0 = [ type.all-bases $(target-type) ] ;
# 't' is the list of target types which have not yet been processed to get a
# list of their viable source target types. This list will get expanded as
# we locate more target types to process.
local t = $(t0) ;
while $(t)
{
# Find all generators for current type. Unlike 'find-viable-generators'
# we don't care about property-set.
# Find all generators for the current type. Unlike
# 'find-viable-generators' we do not care about the property-set.
local generators = $(.generators.$(t[1])) ;
t = $(t[2-]) ;
@@ -723,11 +821,11 @@ local rule viable-source-types-real ( target-type )
if ! [ $(g).source-types ]
{
# Empty source types -- everything can be accepted
# Empty source types -- everything can be accepted.
result = * ;
# This will terminate this loop.
generators = ;
# This will terminate outer loop.
# This will terminate the outer loop.
t = ;
}
@@ -735,14 +833,25 @@ local rule viable-source-types-real ( target-type )
{
if ! $(source-type) in $(result)
{
# If generator accepts 'source-type' it
# will happily accept any type derived from it
local all = [ type.all-derived $(source-type) ] ;
for local n in $(all)
# If a generator accepts a 'source-type' it will also
# happily accept any type derived from it.
for local n in [ type.all-derived $(source-type) ]
{
if ! $(n) in $(result)
{
t += $(n) ;
# Here there is no point in adding target types to
# the list of types to process in case they are or
# have already been on that list. We optimize this
# check by realizing that we only need to avoid the
# original target type's base types. Other target
# types that are or have been on the list of target
# types to process have been added to the 'result'
# list as well and have thus already been eliminated
# by the previous if.
if ! $(n) in $(t0)
{
t += $(n) ;
}
result += $(n) ;
}
}
@@ -751,7 +860,7 @@ local rule viable-source-types-real ( target-type )
}
}
return [ sequence.unique $(result) ] ;
return $(result) ;
}
@@ -762,6 +871,7 @@ rule viable-source-types ( target-type )
local key = .vst.$(target-type) ;
if ! $($(key))
{
.vst-cached-types += $(target-type) ;
local v = [ viable-source-types-real $(target-type) ] ;
if ! $(v)
{
@@ -781,15 +891,18 @@ rule viable-source-types ( target-type )
# 'generator', has some change of being eventually used (probably after
# conversion by other generators).
#
# Returns '*' in case any type should be considered a viable source type for the
# given generator.
#
rule viable-source-types-for-generator-real ( generator )
{
local source-types = [ $(generator).source-types ] ;
if ! $(source-types)
{
# If generator does not specify any source types, it might be special
# If generator does not specify any source types, it might be a special
# generator like builtin.lib-generator which just relays to other
# generators. Return '*' to indicate that any source type is possibly
# OK, since we don't know for sure.
# OK, since we do not know for sure.
return * ;
}
else
@@ -797,15 +910,18 @@ rule viable-source-types-for-generator-real ( generator )
local result ;
for local s in $(source-types)
{
result += [ type.all-derived $(s) ]
[ generators.viable-source-types $(s) ] ;
local viable-sources = [ generators.viable-source-types $(s) ] ;
if $(viable-sources) = *
{
result = * ;
source-types = ; # Terminate the loop.
}
else
{
result += [ type.all-derived $(s) ] $(viable-sources) ;
}
}
result = [ sequence.unique $(result) ] ;
if * in $(result)
{
result = * ;
}
return $(result) ;
return [ sequence.unique $(result) ] ;
}
}
@@ -817,6 +933,7 @@ local rule viable-source-types-for-generator ( generator )
local key = .vstg.$(generator) ;
if ! $($(key))
{
.vstg-cached-generators += $(generator) ;
local v = [ viable-source-types-for-generator-real $(generator) ] ;
if ! $(v)
{
@@ -874,8 +991,9 @@ local rule try-one-generator-really ( project name ? : generator : target-type
}
# Checks if generator invocation can be pruned, because it's guaranteed to fail.
# If so, quickly returns empty list. Otherwise, calls try-one-generator-really.
# Checks if generator invocation can be pruned, because it is guaranteed to
# fail. If so, quickly returns an empty list. Otherwise, calls
# try-one-generator-really.
#
local rule try-one-generator ( project name ? : generator : target-type
: property-set : sources * )
@@ -885,21 +1003,21 @@ local rule try-one-generator ( project name ? : generator : target-type
{
source-types += [ $(s).type ] ;
}
local viable-source-types =
[ viable-source-types-for-generator $(generator) ] ;
local viable-source-types = [ viable-source-types-for-generator $(generator)
] ;
if $(source-types) && $(viable-source-types) != * &&
! [ set.intersection $(source-types) : $(viable-source-types) ]
! [ set.intersection $(source-types) : $(viable-source-types) ]
{
local id = [ $(generator).id ] ;
generators.dout [ indent ] " ** generator '$(id)' pruned" ;
#generators.dout [ indent ] "source-types" '$(source-types)' ;
#generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ;
}
else {
return [ try-one-generator-really $(project) $(name)
: $(generator)
: $(target-type) : $(property-set) : $(sources) ] ;
else
{
return [ try-one-generator-really $(project) $(name) : $(generator) :
$(target-type) : $(property-set) : $(sources) ] ;
}
}
@@ -913,7 +1031,7 @@ rule construct-types ( project name ? : target-types + : property-set
for local t in $(target-types)
{
local r = [ construct $(project) $(name) : $(t) : $(property-set) :
$(sources) ] ;
$(sources) ] ;
if $(r)
{
usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
@@ -955,13 +1073,13 @@ local rule ensure-type ( targets * )
# Returns generators which can be used to construct target of specified type
# with specified properties. Uses the following algorithm:
# - iterates over requested target-type and all its bases (in the order returned
# by type.all-bases.
# by type.all-bases).
# - for each type find all generators that generate that type and whose
# requirements are satisfied by properties.
# - if the set of generators is not empty, returns that set.
#
# Note: this algorithm explicitly ignores generators for base classes if there's
# at least one generator for the requested target-type.
# Note: this algorithm explicitly ignores generators for base classes if there
# is at least one generator for the requested target-type.
#
local rule find-viable-generators-aux ( target-type : property-set )
{
@@ -988,11 +1106,11 @@ local rule find-viable-generators-aux ( target-type : property-set )
if $(t[1]) != $(target-type)
{
# We're here, when no generators for target-type are found, but
# there are some generators for a base type. We'll try to use
# them, but they will produce targets of base type, not of
# 'target-type'. So, we clone the generators and modify the list
# of target types.
# We are here because there were no generators found for
# target-type but there are some generators for its base type.
# We will try to use them, but they will produce targets of
# base type, not of 'target-type'. So, we clone the generators
# and modify the list of target types.
local generators2 ;
for local g in $(generators)
{
@@ -1001,8 +1119,8 @@ local rule find-viable-generators-aux ( target-type : property-set )
# should work. That list is only used when inheriting a
# toolset, which should have been done before running
# generators.
generators2 += [
$(g).clone-and-change-target-type $(t[1]) : $(target-type) ] ;
generators2 += [ $(g).clone-and-change-target-type $(t[1]) :
$(target-type) ] ;
generators.register $(generators2[-1]) ;
}
generators = $(generators2) ;
@@ -1059,7 +1177,7 @@ rule find-viable-generators ( target-type : property-set )
# Generators which override 'all'.
local all-overrides ;
# Generators which are overriden
# Generators which are overriden.
local overriden-ids ;
for local g in $(viable-generators)
{
@@ -1094,10 +1212,11 @@ rule find-viable-generators ( target-type : property-set )
# Attempts to construct a target by finding viable generators, running them and
# selecting the dependency graph.
#
local rule construct-really (
project name ? : target-type : property-set : sources * )
local rule construct-really ( project name ? : target-type : property-set :
sources * )
{
viable-generators = [ find-viable-generators $(target-type) : $(property-set) ] ;
viable-generators = [ find-viable-generators $(target-type) :
$(property-set) ] ;
generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ]
" viable generators" ;
@@ -1109,8 +1228,8 @@ local rule construct-really (
# This variable will be restored on exit from this scope.
local .active-generators = $(g) $(.active-generators) ;
local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type) :
$(property-set) : $(sources) ] ;
local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type)
: $(property-set) : $(sources) ] ;
if $(r)
{
@@ -1152,8 +1271,8 @@ local rule construct-really (
# Attempts to create a target of 'target-type' with 'properties' from 'sources'.
# The 'sources' are treated as a collection of *possible* ingridients -- i.e. it
# is not required to consume them all.
# The 'sources' are treated as a collection of *possible* ingridients, i.e.
# there is no obligation to consume them all.
#
# Returns a list of targets. When this invocation is first instance of
# 'construct' in stack, returns only targets of requested 'target-type',
@@ -1181,8 +1300,8 @@ rule construct ( project name ? : target-type : property-set * : sources * )
generators.dout [ indent ] " properties:" [ $(property-set).raw ] ;
}
local result = [ construct-really $(project) $(name)
: $(target-type) : $(property-set) : $(sources) ] ;
local result = [ construct-really $(project) $(name) : $(target-type) :
$(property-set) : $(sources) ] ;
decrease-indent ;

View File

@@ -103,14 +103,13 @@ JAMROOT ?= project-root.jam [Jj]amroot [Jj]amroot.jam ;
#
rule load-parent ( location )
{
local found = [ path.glob-in-parents $(location) :
$(JAMROOT) $(JAMFILE) ] ;
local found = [ path.glob-in-parents $(location) : $(JAMROOT) $(JAMFILE) ] ;
if ! $(found)
{
ECHO "error: Could not find parent for project at '$(location)'" ;
ECHO "error: Did not find Jamfile.jam or Jamroot.jam in any parent directory." ;
EXIT ;
ECHO error: Could not find parent for project at '$(location)' ;
EXIT error: Did not find Jamfile.jam or Jamroot.jam in any parent
directory. ;
}
return [ load $(found[1]:D) ] ;
@@ -153,8 +152,8 @@ rule find ( name : current-location )
if ! $(project-module)
{
local location = [ path.root
[ path.make $(name) ] $(current-location) ] ;
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
@@ -220,8 +219,8 @@ rule find-jamfile (
{
if ! $(.parent-jamfile.$(dir))
{
.parent-jamfile.$(dir) =
[ path.glob-in-parents $(dir) : $(JAMFILE) ] ;
.parent-jamfile.$(dir) = [ path.glob-in-parents $(dir) : $(JAMFILE)
] ;
}
jamfile-glob = $(.parent-jamfile.$(dir)) ;
}
@@ -237,7 +236,7 @@ rule find-jamfile (
local jamfile-to-load = $(jamfile-glob) ;
# Multiple Jamfiles found in the same place. Warn about this and ensure we
# use only one of them. As a temporary convenience measure, if there's
# 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-])
@@ -262,11 +261,10 @@ rule find-jamfile (
#
if ! $(no-errors) && ! $(jamfile-to-load)
{
errors.error
"Unable to load Jamfile." :
"Could not find a Jamfile in directory '$(dir)'". :
"Attempted to find it with pattern '"$(JAMFILE:J=" ")"'." :
"Please consult the documentation at 'http://www.boost.org'." ;
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) ;
@@ -319,13 +317,12 @@ local rule load-jamfile (
# Now do some checks.
if $(.current-project) != $(saved-project)
{
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)
;
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) ;
}
if $(.global-build-dir)
@@ -578,10 +575,8 @@ class project-attributes
if $(result[1]) = "@error"
{
errors.error
"Requirements for project at '$(self.location)'"
"conflict with parent's." :
"Explanation: " $(result[2-]) ;
errors.error Requirements for project at '$(self.location)'
conflict with parent's. : Explanation: $(result[2-]) ;
}
else
{
@@ -601,7 +596,8 @@ class project-attributes
local non-free = [ property.remove free : $(unconditional) ] ;
if $(non-free)
{
errors.error "usage-requirements" $(specification) "have non-free properties" $(non-free) ;
errors.error usage-requirements $(specification) have non-free
properties $(non-free) ;
}
local t = [ property.translate-paths $(specification)
: $(self.location) ] ;
@@ -624,8 +620,8 @@ class project-attributes
self.source-location = ;
for local src-path in $(specification)
{
self.source-location += [ path.root
[ path.make $(src-path) ] $(self.location) ] ;
self.source-location += [ path.root [ path.make $(src-path) ]
$(self.location) ] ;
}
}
else if $(attribute) = "build-dir"
@@ -633,11 +629,17 @@ class project-attributes
self.build-dir = [ path.root
[ path.make $(specification) ] $(self.location) ] ;
}
else if ! $(attribute) in "id" "default-build" "location"
"source-location" "parent" "projects-to-build" "project-root"
else if $(attribute) = "id"
{
errors.error "Invalid project attribute '$(attribute)' specified"
"for project at '$(self.location)'" ;
id = [ path.root $(specification) / ] ;
project.register-id $(id) : $(self.project-module) ;
self.id = $(id) ;
}
else if ! $(attribute) in "default-build" "location" "parent"
"projects-to-build" "project-root" "source-location"
{
errors.error Invalid project attribute '$(attribute)' specified for
project at '$(self.location)' ;
}
else
{
@@ -738,11 +740,11 @@ rule use ( id : location )
{
# The project at 'location' either has no id or that id is not equal to
# the 'id' parameter.
if $($(id).jamfile-module)
&& $($(id).jamfile-module) != $(project-module)
if $($(id).jamfile-module) && ( $($(id).jamfile-module) !=
$(project-module) )
{
errors.user-error
"Attempt to redeclare already existing project id '$(id)'" ;
errors.user-error Attempt to redeclare already existing project id
'$(id)' ;
}
$(id).jamfile-module = $(project-module) ;
}
@@ -777,12 +779,13 @@ rule extension ( id : options * : * )
# 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) ] ;
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) ;
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.
@@ -797,12 +800,12 @@ 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) ] ] ;
local paths = [ path.$(rule-name) $(location) :
[ sequence.transform path.make : $(wildcards) ] :
[ sequence.transform path.make : $(excludes) ] ] ;
if $(wildcards:D) || $(rule-name) != glob
{
# The paths we've found are relative to the current directory, but the
# 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.
@@ -873,8 +876,6 @@ module project-rules
local attributes = [ project.attributes $(__name__) ] ;
if $(id)
{
id = [ path.root $(id) / ] ;
project.register-id $(id) : $(__name__) ;
$(attributes).set id : $(id) ;
}
@@ -902,19 +903,22 @@ module project-rules
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'll then try to inherit it, with either weird
# or wrong consequences.
# 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 changed in
# the project's attributes.
id = [ $(attributes).get id ] ;
# This is Jamroot.
if $(id)
{
if $(explicit-build-dir)
&& [ path.is-rooted $(explicit-build-dir) ]
if $(explicit-build-dir) &&
[ path.is-rooted $(explicit-build-dir) ]
{
errors.user-error "Absolute directory specified via 'build-dir' project attribute"
: "Don't know how to combine that with the --build-dir option."
;
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) ] ;
@@ -926,11 +930,12 @@ module project-rules
}
else
{
# Not Jamroot
# Not Jamroot.
if $(explicit-build-dir)
{
errors.user-error "When --build-dir is specified, the 'build-dir' project"
: "attribute is allowed only for top-level 'project' invocations" ;
errors.user-error When --build-dir is specified, the
'build-dir' project : attribute is allowed only for
top-level 'project' invocations ;
}
}
}
@@ -943,7 +948,7 @@ module project-rules
rule constant (
name # Variable name of the constant.
: value + # Value of the constant.
)
)
{
import project ;
local p = [ project.target $(__name__) ] ;
@@ -1005,10 +1010,11 @@ module project-rules
if $(wildcards:D) || $(excludes:D)
{
errors.user-error "The patterns to 'glob-tree' may not include directory" ;
errors.user-error The patterns to 'glob-tree' may not include
directory ;
}
return [ project.glob-internal [ project.current ]
: $(wildcards) : $(excludes) : glob-tree ] ;
return [ project.glob-internal [ project.current ] : $(wildcards) :
$(excludes) : glob-tree ] ;
}
# Calculates conditional requirements for multiple requirements at once.

View File

@@ -5,6 +5,8 @@
import "class" : new ;
import feature ;
import path ;
import project ;
import property ;
import sequence ;
import set ;
@@ -250,8 +252,8 @@ class property-set
{
if ! $(self.target-path)
{
# The <location> feature can be used to explicitly
# change the location of generated targetsv
# The <location> feature can be used to explicitly change the
# location of generated targets.
local l = [ get <location> ] ;
if $(l)
{
@@ -260,7 +262,7 @@ class property-set
else
{
local p = [ as-path ] ;
# Really, an ugly hack. Boost regression test system requires
# A real ugly hack. Boost regression test system requires
# specific target paths, and it seems that changing it to handle
# other directory layout is really hard. For that reason, we
# teach V2 to do the things regression system requires. The
@@ -382,6 +384,10 @@ rule create-from-user-input ( raw-properties * : jamfile-module location )
: $(location) ] ;
specification = [ property.translate-indirect $(specification)
: $(jamfile-module) ] ;
local project-id = [ project.attribute $(jamfile-module) id ] ;
project-id ?= [ path.root $(location) [ path.pwd ] ] ;
specification = [ property.translate-dependencies
$(specification) : $(project-id) : $(location) ] ;
specification =
[ property.expand-subfeatures-in-conditions $(specification) ] ;
specification = [ property.make $(specification) ] ;

View File

@@ -469,7 +469,8 @@ rule translate-paths ( properties * : path )
# Assumes that all feature values that start with '@' are names of rules, used
# in 'context-module'. Such rules can be either local to the module or global.
# Converts such values into 'indirect-rule' format (see indirect.jam), so they
# can be called from other modules.
# can be called from other modules. Does nothing for such values that are
# already in the 'indirect-rule' format.
#
rule translate-indirect ( specification * : context-module )
{
@@ -482,7 +483,7 @@ rule translate-indirect ( specification * : context-module )
local v ;
if [ MATCH "^([^%]*)%([^%]+)$" : $(m) ]
{
# Rule is already in indirect format.
# Rule is already in the 'indirect-rule' format.
v = $(m) ;
}
else
@@ -512,6 +513,50 @@ rule translate-indirect ( specification * : context-module )
}
# Binds all dependency properties in a list relative to the given project.
# Targets with absolute paths will be left unchanged and targets which have a
# project specified will have the path to the project interpreted relative to
# the specified location.
#
rule translate-dependencies ( specification * : project-id : location )
{
local result ;
for local p in $(specification)
{
local split = [ split-conditional $(p) ] ;
local condition = "" ;
if $(split)
{
condition = $(split[1]): ;
p = $(split[2]) ;
}
if dependency in [ feature.attributes $(p:G) ]
{
local split-target = [ regex.match (.*)//(.*) : $(p:G=) ] ;
if $(split-target)
{
local rooted = [ path.root [ path.make $(split-target[1]) ]
[ path.root $(location) [ path.pwd ] ] ] ;
result += $(condition)$(p:G)$(rooted)//$(split-target[2]) ;
}
else if [ path.is-rooted $(p:G=) ]
{
result += $(condition)$(p) ;
}
else
{
result += $(condition)$(p:G)$(project-id)//$(p:G=) ;
}
}
else
{
result += $(condition)$(p) ;
}
}
return $(result) ;
}
# Class maintaining a property set -> string mapping.
#
class property-map

View File

@@ -13,8 +13,8 @@
# each Jamfile, and can be obtained by the 'target' rule in the Jamfile's module
# (see project.jam).
#
# Project targets keep a list of 'main-target' instances. A main target is
# what the user explicitly defines in a Jamfile. It is possible to have several
# Project targets keep a list of 'main-target' instances. A main target is what
# the user explicitly defines in a Jamfile. It is possible to have several
# definitions for a main target, for example to have different lists of sources
# for different platforms. So, main targets keep a list of alternatives.
#
@@ -89,8 +89,8 @@ class abstract-target
import "class" ;
import errors ;
rule __init__ ( name # name of the target in Jamfile
: project-target # the project target to which this one belongs
rule __init__ ( name # Name of the target in Jamfile.
: project-target # The project target to which this one belongs.
)
{
# Note: it might seem that we don't need either name or project at all.
@@ -115,7 +115,7 @@ class abstract-target
return $(self.project) ;
}
# Return the location where the target was declared
# Return the location where the target was declared.
rule location ( )
{
return $(self.location) ;
@@ -232,10 +232,8 @@ class project-target : abstract-target
self.build-dir = [ get build-dir ] ;
if ! $(self.build-dir)
{
self.build-dir = [ path.join
[ $(self.project).get location ]
bin
] ;
self.build-dir = [ path.join [ $(self.project).get location ]
bin ] ;
}
}
return $(self.build-dir) ;
@@ -300,8 +298,8 @@ class project-target : abstract-target
#
rule mark-target-as-explicit ( target-name )
{
# Record the name of the target, not instance, since this
# rule is called before main target instaces are created.
# Record the name of the target, not instance, since this rule is called
# before main target instances are created.
self.explicit-targets += $(target-name) ;
}
@@ -311,13 +309,13 @@ class project-target : abstract-target
{
if $(self.built-main-targets)
{
errors.error "add-alternative called when main targets are already created."
: "in project" [ full-name ] ;
errors.error add-alternative called when main targets are already
created. : in project [ full-name ] ;
}
self.alternatives += $(target-instance) ;
}
# Returns a 'main-target' class instance corresponding to the 'name'.
# Returns a 'main-target' class instance corresponding to 'name'.
#
rule main-target ( name )
{
@@ -325,11 +323,10 @@ class project-target : abstract-target
{
build-main-targets ;
}
return $(self.main-target.$(name)) ;
}
# Tells if a main target with the specified name exists.
# Returns whether a main target with the specified name exists.
#
rule has-main-target ( name )
{
@@ -344,13 +341,12 @@ class project-target : abstract-target
}
}
# Find and return the target with the specified id, treated relative to
# self.
# Worker function for the find rule not implementing any caching and simply
# returning nothing in case the target can not be found.
#
rule find-really ( id )
{
local result ;
local project = $(self.project) ;
local current-location = [ get location ] ;
local split = [ MATCH (.*)//(.*) : $(id) ] ;
@@ -360,8 +356,8 @@ class project-target : abstract-target
local extra-error-message ;
if $(project-part)
{
# There's explicit project part in id. Looks up the project and
# passes the request to it.
# There is an explicitly specified project part in id. Looks up the
# project and passes the request to it.
local pm = [ project.find $(project-part) : $(current-location) ] ;
if $(pm)
{
@@ -370,37 +366,44 @@ class project-target : abstract-target
}
else
{
extra-error-message = "error: could not find project '$(project-part)'" ;
# TODO: This extra error message will not get displayed most
# likely due to some buggy refactoring. Refactor the code so the
# message gets diplayed again.
extra-error-message = error: could not find project
'$(project-part)' ;
}
}
else
{
# Interpret target-name as name of main target. Need to do this
# before checking for file. Consider this:
# before checking for file. Consider the following scenario with a
# toolset not modifying its executable's names, e.g. gcc on
# Unix-like platforms:
#
# exe test : test.cpp ;
# install s : test : <location>. ;
#
# After first build we'll have target 'test' in Jamfile and file
# 'test' on the disk. We need target to override the file.
# After the first build we would have a target named 'test' in the
# Jamfile and a file named 'test' on the disk. We need the target to
# override the file.
result = [ main-target $(id) ] ;
# Interpret id as an existing file reference.
if ! $(result)
{
result = [ new file-reference [ path.make $(id) ] : $(project) ] ;
result = [ new file-reference [ path.make $(id) ] :
$(self.project) ] ;
if ! [ $(result).exists ]
{
# File actually does not exist. Reset 'target' so that an
# error is issued.
result = ;
}
}
# Interpret id as project-id
# Interpret id as project-id.
if ! $(result)
{
local project-module = [ project.find $(id) : $(current-location) ] ;
local project-module = [ project.find $(id) :
$(current-location) ] ;
if $(project-module)
{
result = [ project.target $(project-module) ] ;
@@ -411,6 +414,11 @@ class project-target : abstract-target
return $(result) ;
}
# Find and return the target with the specified id, treated relative to
# self. Id may specify either a target or a file name with the target taking
# priority. May report an error or return nothing if the target is not found
# depending on the 'no-error' parameter.
#
rule find ( id : no-error ? )
{
local v = $(.id.$(id)) ;
@@ -490,7 +498,7 @@ class project-target : abstract-target
self.constants += $(name) ;
}
self.constant.$(name) = $(value) ;
# Inject the constant in the scope of project-root module.
# Inject the constant in the scope of the Jamroot module.
modules.poke $(self.project-module) : $(name) : $(value) ;
}
@@ -771,7 +779,7 @@ class main-target : abstract-target
}
# Abstract target which refers to a source file. This is an artificial entity
# Abstract target refering to a source file. This is an artificial entity
# allowing sources to a target to be represented using a list of abstract target
# instances.
#
@@ -788,11 +796,8 @@ class file-reference : abstract-target
rule generate ( properties )
{
location ;
return [ property-set.empty ]
[ virtual-target.from-file $(self.name)
: $(self.file-location)
: $(self.project) ] ;
return [ property-set.empty ] [ virtual-target.from-file $(self.name) :
[ location ] : $(self.project) ] ;
}
# Returns true if the referred file really exists.
@@ -909,7 +914,7 @@ rule common-properties ( build-request requirements )
# Given a 'context' -- a set of already present properties, and 'requirements',
# decide which extra properties should be applied to 'context'. For conditional
# requirements, this means evaluating condition. For indirect conditional
# requirements, this means evaluating the condition. For indirect conditional
# requirements, this means calling a rule. Ordinary requirements are always
# applied.
#
@@ -945,7 +950,7 @@ rule evaluate-requirements ( requirements : context : what )
local conditionals = [ $(requirements).conditional ] ;
# The 'count' variable has one element for each conditional feature and for
# each occurence of '<indirect-conditional>' feature. It is used as a loop
# each occurrence of '<indirect-conditional>' feature. It is used as a loop
# counter: for each iteration of the loop before we remove one element and
# the property set should stabilize before we are done. It is assumed that
# #conditionals iterations should be enough for properties to propagate
@@ -959,9 +964,10 @@ rule evaluate-requirements ( requirements : context : what )
local current = $(raw) ;
# It is assumed that ordinary conditional requirements can not add
# <indirect-conditional> properties, and that rules referred to by
# <indirect-conditional> properties can not add new <indirect-conditional>
# properties. So the list of indirect conditionals does not change.
# <conditional> properties (a.k.a. indirect conditional properties), and
# that rules referred to by <conditional> properties can not add new
# <conditional> properties. So the list of indirect conditionals does not
# change.
local indirect = [ $(requirements).get <conditional> ] ;
indirect = [ MATCH @(.*) : $(indirect) ] ;
@@ -980,7 +986,7 @@ rule evaluate-requirements ( requirements : context : what )
if $(e) = $(added-requirements)
{
# If we got the same result, we've found final properties.
# If we got the same result, we have found the final properties.
count = ;
ok = true ;
}
@@ -1009,7 +1015,7 @@ rule evaluate-requirements ( requirements : context : what )
}
else
{
errors.error "Invalid value of the 'what' parameter" ;
errors.error "Invalid value of the 'what' parameter." ;
}
}
@@ -1074,7 +1080,8 @@ class basic-target : abstract-target
if $(sources:G)
{
errors.user-error "properties found in the 'sources' parameter for" [ full-name ] ;
errors.user-error properties found in the 'sources' parameter for
[ full-name ] ;
}
}
@@ -1198,7 +1205,8 @@ class basic-target : abstract-target
ECHO [ targets.indent ] "Common properties: " [ $(rproperties).raw ] ;
}
if $(rproperties[1]) != "@error" && [ $(rproperties).get <build> ] != no
if ( $(rproperties[1]) != "@error" ) && ( [ $(rproperties).get
<build> ] != no )
{
local source-targets ;
local properties = [ $(rproperties).non-dependency ] ;
@@ -1401,11 +1409,11 @@ class typed-target : basic-target
{
import generators ;
rule __init__ ( name : project : type : sources * : requirements *
: default-build * : usage-requirements * )
rule __init__ ( name : project : type : sources * : requirements * :
default-build * : usage-requirements * )
{
basic-target.__init__ $(name) : $(project) : $(sources)
: $(requirements) : $(default-build) : $(usage-requirements) ;
basic-target.__init__ $(name) : $(project) : $(sources) :
$(requirements) : $(default-build) : $(usage-requirements) ;
self.type = $(type) ;
}
@@ -1508,10 +1516,10 @@ rule main-target-usage-requirements (
{
local project-usage-requirements = [ $(project).get usage-requirements ] ;
# We don't use 'refine-from-user-input' because:
# - I'm not sure if removing of parent's usage requirements makes sense
# - refining of usage requirements is not needed, since usage requirements
# are always free.
# We do not use 'refine-from-user-input' because:
# - I am not sure if removing parent's usage requirements makes sense
# - refining usage requirements is not needed, since usage requirements are
# always free.
local usage-requirements = [ property-set.create-from-user-input
$(specification)
: [ $(project).project-module ] [ $(project).get location ] ] ;

View File

@@ -21,8 +21,8 @@ import set ;
.ignore-requirements = ;
# This is used only for testing, to make sure we don't get random extra elements
# in paths.
# This is used only for testing, to make sure we do not get random extra
# elements in paths.
if --ignore-toolset-requirements in [ modules.peek : ARGV ]
{
.ignore-requirements = 1 ;
@@ -56,7 +56,7 @@ local rule normalize-condition ( property-sets * )
# Specifies if the 'flags' rule should check that the invoking module is the
# same as the module we're setting the flag for. 'v' can be either 'checked' or
# same as the module we are setting the flag for. 'v' can be either 'checked' or
# 'unchecked'. Subsequent call to 'pop-checking-for-flags-module' will restore
# the setting that was in effect before calling this rule.
#
@@ -101,7 +101,7 @@ rule flags (
# <architecture>ia64/<address-model>
#
# Where both features are optional. Without this syntax
# we'd be forced to define "default" values.
# we would be forced to define "default" values.
values * : # The value to add to variable. If <feature> is specified,
# then the value of 'feature' will be added.
@@ -138,7 +138,7 @@ rule flags (
if $(condition) && ! $(condition:G=) && ! $(hack-hack)
{
# We have condition in the form '<feature>', that is, without value.
# That's a previous syntax:
# That is an older syntax:
# flags gcc.link RPATH <dll-path> ;
# for compatibility, convert it to
# flags gcc.link RPATH : <dll-path> ;
@@ -162,10 +162,10 @@ local rule add-flag ( rule-or-module : variable-name : condition * : values * )
{
.$(rule-or-module).flags += $(.flag-no) ;
# Store all flags for a module
# Store all flags for a module.
local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ;
.module-flags.$(module_) += $(.flag-no) ;
# Store flag-no -> rule-or-module mapping
# Store flag-no -> rule-or-module mapping.
.rule-or-module.$(.flag-no) = $(rule-or-module) ;
.$(rule-or-module).variable.$(.flag-no) += $(variable-name) ;
@@ -177,7 +177,7 @@ local rule add-flag ( rule-or-module : variable-name : condition * : values * )
# Returns the first element of 'property-sets' which is a subset of
# 'properties', or an empty list if no such element exists.
# 'properties' or an empty list if no such element exists.
#
rule find-property-subset ( property-sets * : properties * )
{
@@ -225,6 +225,9 @@ rule find-property-subset ( property-sets * : properties * )
}
# Returns a value to be added to some flag for some target based on the flag's
# value definition and the given target's property set.
#
rule handle-flag-value ( value * : properties * )
{
local result ;
@@ -245,8 +248,8 @@ rule handle-flag-value ( value * : properties * )
local values ;
# Treat features with && in the value specially -- each
# &&-separated element is considered a separate value. This is
# needed to handle searched libraries, which must be in a
# specific order.
# needed to handle searched libraries or include paths, which
# may need to be in a specific order.
if ! [ MATCH (&&) : $(p:G=) ]
{
values = $(p:G=) ;
@@ -383,11 +386,11 @@ rule inherit-generators ( toolset properties * : base : generators-to-ignore * )
if ! $(id) in $(generators-to-ignore)
{
# Some generator names have multiple periods in their name, so
# $(id:B=$(toolset)) doesn't generate the right new-id name. E.g. if
# id = gcc.compile.c++, $(id:B=darwin) = darwin.c++, which is not
# what we want. Manually parse the base and suffix (if there's a
# better way to do this, I'd love to see it). See also register in
# module generators.
# $(id:B=$(toolset)) does not generate the right new-id name. E.g.
# if id = gcc.compile.c++ then $(id:B=darwin) = darwin.c++, which is
# not what we want. Manually parse the base and suffix. If there is
# a better way to do this, I would love to see it. See also the
# register() rule in the generators module.
local base = $(id) ;
local suffix = "" ;
while $(base:S)
@@ -405,14 +408,14 @@ rule inherit-generators ( toolset properties * : base : generators-to-ignore * )
# Brings all flag definitions from the 'base' toolset into the 'toolset'
# toolset. Flag definitions whose conditions make use of properties in
# 'prohibited-properties' are ignored. Don't confuse property and feature, for
# 'prohibited-properties' are ignored. Do not confuse property and feature, for
# example <debug-symbols>on and <debug-symbols>off, so blocking one of them does
# not block the other one.
#
# The flag conditions are not altered at all, so if a condition includes a name,
# or version of a base toolset, it won't ever match the inheriting toolset. When
# such flag settings must be inherited, define a rule in base toolset module and
# call it as needed.
# or version of a base toolset, it will not ever match the inheriting toolset.
# When such flag settings must be inherited, define a rule in base toolset
# module and call it as needed.
#
rule inherit-flags ( toolset : base : prohibited-properties * )
{
@@ -447,7 +450,7 @@ rule inherit-flags ( toolset : base : prohibited-properties * )
rule inherit-rules ( toolset : base )
{
# It appears that "action" creates a local rule...
# It appears that "action" creates a local rule.
local base-generators = [ generators.generators-for-toolset $(base) ] ;
local rules ;
for local g in $(base-generators)
@@ -456,8 +459,7 @@ rule inherit-rules ( toolset : base )
rules += $(id) ;
}
IMPORT $(base) : $(rules) : $(toolset) : $(rules) ;
# Import the rules to the global scope
IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ;
IMPORT $(base) : $(rules) : : $(toolset).$(rules) ;
}

View File

@@ -14,7 +14,8 @@ import project ;
import property ;
import scanner ;
# The follwing import would create a circular dependency:
# The following import would create a circular dependency:
# project -> project-root -> builtin -> type -> targets -> project
# import targets ;
@@ -48,15 +49,15 @@ rule register ( type : suffixes * : base-type ? )
else
{
.types += $(type) ;
.bases.$(type) = $(base-type) ;
.base.$(type) = $(base-type) ;
.derived.$(base-type) += $(type) ;
if $(suffixes)-is-not-empty
{
# Specify mapping from suffixes to type.
register-suffixes $(suffixes) : $(type) ;
# Generated targets of 'type' will use the first of 'suffixes'. This
# may be overriden.
# By default generated targets of 'type' will use the first of
#'suffixes'. This may be overriden.
set-generated-target-suffix $(type) : : $(suffixes[1]) ;
}
@@ -68,14 +69,18 @@ rule register ( type : suffixes * : base-type ? )
feature.compose <base-target-type>$(type) : <base-target-type>$(base-type) ;
# We used to declare the main target rule only when a 'main' parameter
# was specified. However, it's hard to decide that a type will *never*
# need a main target rule and so from time to time we needed to make yet
# another type 'main'. So now a main target rule is defined for each
# type.
# has been specified. However, it is hard to decide that a type will
# *never* need a main target rule and so from time to time we needed to
# make yet another type 'main'. So now a main target rule is defined for
# each type.
main-rule-name = [ type-to-rule-name $(type) ] ;
.main-target-type.$(main-rule-name) = $(type) ;
IMPORT $(__name__) : main-target-rule : : $(main-rule-name) ;
# Adding a new derived type affects generator selection so we need to
# make the generator selection module update any of its cached
# information related to a new derived type being defined.
generators.update-cached-information-with-a-new-type $(type) ;
}
}
@@ -114,8 +119,8 @@ rule register-suffixes ( suffixes + : type )
}
else if $(.type.$(s)) != type
{
errors.error Attempting to specify multiple types for suffix \"$(s)\"
: "Old type $(.type.$(s)), New type $(type)" ;
errors.error Attempting to specify multiple types for suffix
\"$(s)\" : "Old type $(.type.$(s)), New type $(type)" ;
}
}
}
@@ -136,7 +141,7 @@ rule registered ( type )
#
rule validate ( type )
{
if ! $(type) in $(.types)
if ! [ registered $(type) ]
{
errors.error "Unknown target type $(type)" ;
}
@@ -147,10 +152,7 @@ rule validate ( type )
#
rule set-scanner ( type : scanner )
{
if ! $(type) in $(.types)
{
error "Type" $(type) "is not declared" ;
}
validate $(type) ;
.scanner.$(type) = $(scanner) ;
}
@@ -166,6 +168,15 @@ rule get-scanner ( type : property-set )
}
# Returns a base type for the given type or nothing in case the given type is
# not derived.
#
rule base ( type )
{
return $(.base.$(type)) ;
}
# Returns the given type and all of its base types in order of their distance
# from type.
#
@@ -174,13 +185,16 @@ rule all-bases ( type )
local result = $(type) ;
while $(type)
{
type = $(.bases.$(type)) ;
type = [ base $(type) ] ;
result += $(type) ;
}
return $(result) ;
}
# Returns the given type and all of its derived types in order of their distance
# from type.
#
rule all-derived ( type )
{
local result = $(type) ;
@@ -192,7 +206,8 @@ rule all-derived ( type )
}
# Returns true if 'type' has 'base' as its direct or indirect base.
# Returns true if 'type' is equal to 'base' or has 'base' as its direct or
# indirect base.
#
rule is-derived ( type base )
{
@@ -202,19 +217,15 @@ rule is-derived ( type base )
}
}
# Returns true if 'type' is either derived from or is equal to 'base'.
#
# TODO: It might be that is-derived and is-subtype were meant to be different
# rules - one returning true for type = base and one not, but as currently
# implemented they are actually the same. Clean this up.
#
rule is-subtype ( type base )
{
if $(type) = $(base)
{
return true ;
}
else
{
return [ is-derived $(type) $(base) ] ;
}
return [ is-derived $(type) $(base) ] ;
}
@@ -329,7 +340,7 @@ local rule generated-target-ps-real ( ps : type : properties * )
{
found = true ;
}
type = $(.bases.$(type)) ;
type = $(.base.$(type)) ;
}
if $(result) = ""
{
@@ -345,8 +356,8 @@ local rule generated-target-ps ( ps : type : property-set )
local v = $($(key)) ;
if ! $(v)
{
v = [ generated-target-ps-real $(ps) : $(type)
: [ $(property-set).raw ] ] ;
v = [ generated-target-ps-real $(ps) : $(type) : [ $(property-set).raw ]
] ;
if ! $(v)
{
v = none ;
@@ -361,9 +372,9 @@ local rule generated-target-ps ( ps : type : property-set )
}
# Returns file type given it's name. If there are several dots in filename,
# tries each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and "so"
# will be tried.
# Returns file type given its name. If there are several dots in filename, tries
# each suffix. E.g. for name of "file.so.1.2" suffixes "2", "1", and "so" will
# be tried.
#
rule type ( filename )
{
@@ -379,23 +390,32 @@ rule type ( filename )
# Rule used to construct all main targets. Note that this rule gets imported
# into the global namespace under different alias names and exactly what type of
# target it is supposed to construct is read from the name of the alias rule
# actually used to invoke it.
# into the global namespace under different alias names and the exact target
# type to construct is selected based on the alias used to actually invoke this
# rule.
#
rule main-target-rule ( name : sources * : requirements * : default-build *
: usage-requirements * )
rule main-target-rule ( name : sources * : requirements * : default-build * :
usage-requirements * )
{
# First discover the required target type, which is equal to the rule name
# used to invoke us.
# First discover the required target type based on the exact alias used to
# invoke this rule.
local bt = [ BACKTRACE 1 ] ;
local rulename = $(bt[4]) ;
local target-type = [ type-from-rule-name $(rulename) ] ;
local project = [ project.current ] ;
# This is a circular module dependency so it must be imported here.
# This is a circular module dependency and so must be imported here.
import targets ;
return [ targets.create-typed-target $(.main-target-type.$(rulename))
: $(project) : $(name) : $(sources) : $(requirements)
: $(default-build) : $(usage-requirements) ] ;
return [ targets.create-typed-target $(target-type) : [ project.current ] :
$(name) : $(sources) : $(requirements) : $(default-build) :
$(usage-requirements) ] ;
}
rule __test__ ( )
{
import assert ;
# TODO: Add tests for all the is-derived, is-base & related type relation
# checking rules.
}

View File

@@ -6,7 +6,7 @@
# Implements virtual targets, which correspond to actual files created during a
# build, but are not yet targets in Jam sense. They are needed, for example,
# when searching for possible transformation sequences, when it's not yet known
# when searching for possible transformation sequences, when it is not yet known
# whether a particular target should be created at all.
import "class" : new ;
@@ -17,6 +17,7 @@ import set ;
import type ;
import utility ;
# +--------------------------+
# | virtual-target |
# +==========================+
@@ -88,18 +89,24 @@ class virtual-target
# Name of this target.
#
rule name ( ) { return $(self.name) ; }
rule name ( )
{
return $(self.name) ;
}
# Project of this target.
#
rule project ( ) { return $(self.project) ; }
rule project ( )
{
return $(self.project) ;
}
# Adds additional 'virtual-target' instances this one depends on.
#
rule depends ( d + )
{
self.dependencies = [ sequence.merge $(self.dependencies)
: [ sequence.insertion-sort $(d) ] ] ;
self.dependencies = [ sequence.merge $(self.dependencies) :
[ sequence.insertion-sort $(d) ] ] ;
}
rule dependencies ( )
@@ -185,7 +192,8 @@ class virtual-target
{
# In fact, we just need to merge virtual-target with
# abstract-file-target as the latter is the only class derived from the
# former. But that's for later.
# former. But that has been left for later.
errors.error "method should be defined in derived classes" ;
}
}
@@ -197,8 +205,9 @@ class virtual-target
# May be a source file (when no action is specified) or a derived file
# (otherwise).
#
# The target's grist is concatenation of its project's location, properties of
# action (for derived files) and, optionally, value identifying the main target.
# The target's grist is a concatenation of its project's location, action
# properties (for derived targets) and, optionally, value identifying the main
# target.
#
class abstract-file-target : virtual-target
{
@@ -236,7 +245,10 @@ class abstract-file-target : virtual-target
}
}
rule type ( ) { return $(self.type) ; }
rule type ( )
{
return $(self.type) ;
}
# Sets the path. When generating target name, it will override any path
# computation from properties.
@@ -291,11 +303,11 @@ class abstract-file-target : virtual-target
}
# Return a human-readable representation of this target. If this target has
# an action, that's:
# an action, that is:
#
# { <action-name>-<self.name>.<self.type> <action-sources>... }
#
# otherwise, it's:
# otherwise, it is:
#
# { <self.name>.<self.type> }
#
@@ -358,7 +370,7 @@ class abstract-file-target : virtual-target
rule grist ( )
{
# Depending on target, there may be different approaches to generating
# unique prefixes. We'll generate prefixes in the form
# unique prefixes. We generate prefixes in the form:
# <one letter approach code> <the actual prefix>
local path = [ path ] ;
if $(path)
@@ -441,7 +453,7 @@ class abstract-file-target : virtual-target
}
}
# If there's no tag or the tag rule returned nothing.
# If there is no tag or the tag rule returned nothing.
if ! $(tag) || ! $(self.name)
{
self.name = [ virtual-target.add-prefix-and-suffix $(specified-name)
@@ -460,7 +472,7 @@ class abstract-file-target : virtual-target
if $(self.action)
{
# For non-derived target, we don't care if there are several
# For non-derived target, we do not care if there are several
# virtual targets that refer to the same name. One case when
# this is unavoidable is when the file name is main.cpp and two
# targets have types CPP (for compiling) and MOCCABLE_CPP (for
@@ -531,23 +543,23 @@ class file-target : abstract-file-target
import errors ;
rule __init__ (
name exact ?
: type ? # Optional type for this target
name exact ?
: type ? # Optional type for this target.
: project
: action ?
: path ?
)
{
abstract-file-target.__init__ $(name) $(exact) : $(type) : $(project)
: $(action) ;
abstract-file-target.__init__ $(name) $(exact) : $(type) : $(project) :
$(action) ;
self.path = $(path) ;
}
rule clone-with-different-type ( new-type )
{
return [ new file-target $(self.name) exact : $(new-type)
: $(self.project) : $(self.action) : $(self.path) ] ;
return [ new file-target $(self.name) exact : $(new-type) :
$(self.project) : $(self.action) : $(self.path) ] ;
}
rule actualize-location ( target )
@@ -581,14 +593,14 @@ class file-target : abstract-file-target
# target has no directory name and uses a special <e> grist.
#
# First, that means that "bjam hello.o" will build all known hello.o
# targets. Second, the <e> grist makes sure this target won't be
# targets. Second, the <e> grist makes sure this target will not be
# confused with other targets, for example, if we have subdir 'test'
# with target 'test' in it that includes a 'test.o' file, then the
# target for directory will be just 'test' the target for test.o
# will be <ptest/bin/gcc/debug>test.o and the target we create below
# will be <e>test.o
DEPENDS $(target:G=e) : $(target) ;
# Allow bjam <path-to-file>/<file> to work. This won't catch all
# Allow bjam <path-to-file>/<file> to work. This will not catch all
# possible ways to refer to the path (relative/absolute, extra ".",
# various "..", but should help in obvious cases.
DEPENDS $(target:G=e:R=$(path)) : $(target) ;
@@ -608,13 +620,13 @@ class file-target : abstract-file-target
if $(self.action)
{
local p = [ $(self.action).properties ] ;
local path = [ $(p).target-path ] ;
local path,relative-to-build-dir = [ $(p).target-path ] ;
local path = $(path,relative-to-build-dir[1]) ;
local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
if $(path[2]) = true
if $(relative-to-build-dir)
{
# Indicates that the path is relative to the build dir.
path = [ path.join [ $(self.project).build-dir ]
$(path[1]) ] ;
path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
}
self.path = [ path.native $(path) ] ;
@@ -727,7 +739,8 @@ class action
actualize-sources [ sources ] : $(properties) ;
DEPENDS $(actual-targets) : $(self.actual-sources) $(self.dependency-only-sources) ;
DEPENDS $(actual-targets) : $(self.actual-sources)
$(self.dependency-only-sources) ;
# Action name can include additional argument to rule, which should
# not be passed to 'set-target-variables'
@@ -794,8 +807,8 @@ class action
#
# For bjam to find the dependency the generated target must be
# actualized (i.e. have its Jam target constructed). In the above case,
# if we're building just hello ("bjam hello"), 'a.h' won't be actualized
# unless we do it here.
# if we are building just hello ("bjam hello"), 'a.h' will not be
# actualized unless we do it here.
local implicit = [ $(self.properties).get <implicit-dependency> ] ;
for local i in $(implicit)
{
@@ -816,7 +829,7 @@ class action
# Action class which does nothing --- it produces the targets with specific
# properties out of nowhere. It's needed to distinguish virtual targets with
# properties out of nowhere. It is needed to distinguish virtual targets with
# different properties that are known to exist and have no actions which create
# them.
#
@@ -866,16 +879,21 @@ class non-scanning-action : action
# Creates a virtual target with an appropriate name and type from 'file'. If a
# target with that name in that project already exists, returns that already
# created target.
#
# FIXME: a more correct way would be to compute the path to the file, based on
# name and source location for the project, and use that path to determine if
# the target was already created.
# the target has already been created. This logic should be shared with how we
# usually find targets identified by a specific target id. It should also be
# updated to work correctly when the file is specified using both relative and
# absolute paths.
#
# TODO: passing a project with all virtual targets is starting to be annoying.
#
rule from-file ( file : file-loc : project )
{
import type ; # Had to do this here to break a circular dependency.
# Check if we've created a target corresponding to this file.
# Check whether we already created a target corresponding to this file.
local path = [ path.root [ path.root $(file) $(file-loc) ] [ path.pwd ] ] ;
if $(.files.$(path))
@@ -888,11 +906,8 @@ rule from-file ( file : file-loc : project )
local type = [ type.type $(file) ] ;
local result ;
result = [ new file-target $(file)
: $(type)
: $(project)
: #action
: $(file-loc) ] ;
result = [ new file-target $(file) : $(type) : $(project) : :
$(file-loc) ] ;
.files.$(path) = $(result) ;
return $(result) ;
@@ -900,7 +915,7 @@ rule from-file ( file : file-loc : project )
}
# Registers a new virtual target. Checks if there's already a registered target
# Registers a new virtual target. Checks if there is already a registered target
# with the same name, type, project and subvariant properties as well as the
# same sources and equal action. If such target is found it is returned and a
# new 'target' is not registered. Otherwise, 'target' is registered and
@@ -926,15 +941,15 @@ rule register ( target )
else
{
if $(a1) && $(a2) &&
[ $(a1).action-name ] = [ $(a2).action-name ] &&
[ $(a1).sources ] = [ $(a2).sources ]
( [ $(a1).action-name ] = [ $(a2).action-name ] ) &&
( [ $(a1).sources ] = [ $(a2).sources ] )
{
local ps1 = [ $(a1).properties ] ;
local ps2 = [ $(a2).properties ] ;
local p1 = [ $(ps1).base ] [ $(ps1).free ]
[ set.difference [ $(ps1).dependency ] : [ $(ps1).incidental ] ] ;
local p2 = [ $(ps2).base ] [ $(ps2).free ]
[ set.difference [ $(ps2).dependency ] : [ $(ps2).incidental ] ] ;
local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference
[ $(ps1).dependency ] : [ $(ps1).incidental ] ] ;
local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference
[ $(ps2).dependency ] : [ $(ps2).incidental ] ] ;
if $(p1) = $(p2)
{
result = $(t) ;
@@ -957,10 +972,10 @@ rule register ( target )
}
# Each target returned by 'register' is added to a .recent-targets list,
# Each target returned by 'register' is added to the .recent-targets list,
# returned by this function. This allows us to find all virtual targets created
# when building a given main target, even those constructed only as intermediate
# targets.
# when building a specific main target, even those constructed only as
# intermediate targets.
#
rule recent-targets ( )
{
@@ -1040,9 +1055,9 @@ rule register-actual-name ( actual-name : virtual-target )
# Traverses the dependency graph of 'target' and return all targets that will be
# created before this one is created. If the root of some dependency graph is
# found during traversal, it's either included or not, depending on the value of
# 'include-roots'. In either case traversal stops at root targets, i.e. sources
# of root targets are not traversed.
# found during traversal, it is either included or not, depending on the
# 'include-roots' value. In either case traversal stops at root targets, i.e.
# root target sources are not traversed.
#
rule traverse ( target : include-roots ? : include-sources ? )
{
@@ -1097,9 +1112,9 @@ rule clone-action ( action : new-project : new-action-name ? : new-properties ?
for local target in [ $(action).targets ]
{
local n = [ $(target).name ] ;
# Don't modify produced targets names.
local cloned-target = [ class.new file-target $(n) exact
: [ $(target).type ] : $(new-project) : $(cloned-action) ] ;
# Do not modify produced target names.
local cloned-target = [ class.new file-target $(n) exact :
[ $(target).type ] : $(new-project) : $(cloned-action) ] ;
local d = [ $(target).dependencies ] ;
if $(d)
{

View File

@@ -0,0 +1,195 @@
################################################################################
#
# Copyright (c) 2007-2008 Dario Senic, Jurko Gospodnetic.
#
# Use, modification and distribution is subject to the Boost Software
# License Version 1.0. (See accompanying file LICENSE_1_0.txt or
# http://www.boost.org/LICENSE_1_0.txt)
#
################################################################################
################################################################################
#
# Boost Build wxFormBuilder generator tool module.
#
# wxFormBuilder is a GUI designer tool for the wxWidgets library. It can then
# generate C++ sources modeling the designed GUI using the wxWidgets library
# APIs.
#
# This module defines a wxFormBuilder project file type and rules needed to
# generate C++ source files from those projects. With it you can simply list
# wxFormBuilder projects as sources for some target and Boost Build will
# automatically convert them to C++ sources and process from there.
#
# The wxFormBuilder executable location may be provided as a parameter when
# configuring this toolset. Otherwise the default wxFormBuilder.exe executable
# name is used located in the folder pointed to by the WXFORMBUILDER environment
# variable.
#
# Current limitations:
#
# * Works only on Windows.
# * Works only when run via Boost Jam using the native Windows cmd.exe command
# interpreter, i.e. the default native Windows Boost Jam build.
# * Used wxFormBuilder projects need to have their output file names defined
# consistently with target names assumed by this build script. This means
# that their target names must use the prefix 'wxFormBuilderGenerated_' and
# have no output folder defined where the base name is equal to the .fpb
# project file's name.
#
################################################################################
################################################################################
#
# Implementation note:
#
# Avoiding the limitation on the generated target file names can be done but
# would require depending on external tools to copy the wxFormBuilder project to
# a temp location and then modify it in-place to set its target file names. On
# the other hand wxFormBuilder is expected to add command-line options for
# choosing the target file names from the command line which will allow us to
# remove this limitation in a much cleaner way.
# (23.08.2008.) (Jurko)
#
################################################################################
import generators ;
import os ;
import path ;
import toolset ;
import type ;
################################################################################
#
# wxFormBuilder.generate()
# ------------------------
#
# Action for processing WX_FORM_BUILDER_PROJECT types.
#
################################################################################
#
# Implementation notes:
#
# wxFormBuilder generated CPP and H files need to be moved to the location
# where the Boost Build target system expects them so that the generated CPP
# file can be included into the compile process and that the clean rule
# succesfully deletes both CPP and H files. We expect wxFormBuilder to generate
# files in the same location where the provided WX_FORM_BUILDER_PROJECT file is
# located.
# (15.05.2007.) (Dario)
#
################################################################################
actions generate
{
start "" /wait "$(EXECUTABLE)" /g "$(2)"
move "$(1[1]:BSR=$(2:P))" "$(1[1]:P)"
move "$(1[2]:BSR=$(2:P))" "$(1[2]:P)"
}
################################################################################
#
# wxFormBuilder.init()
# --------------------
#
# Main toolset initialization rule called via the toolset.using rule.
#
################################################################################
rule init ( executable ? )
{
if $(.initialized)
{
if $(.debug-configuration)
{
ECHO notice: [wxFormBuilder-cfg] Repeated initialization request
(executable \"$(executable:E="")\") detected and ignored. ;
}
}
else
{
local environmentVariable = WXFORMBUILDER ;
if $(.debug-configuration)
{
ECHO notice: [wxFormBuilder-cfg] Configuring wxFormBuilder... ;
}
# Deduce the path to the used wxFormBuilder executable.
if ! $(executable)
{
executable = "wxFormBuilder.exe" ;
local executable-path = [ os.environ $(environmentVariable) ] ;
if $(executable-path)-is-not-empty
{
executable = [ path.root $(executable) $(executable-path) ] ;
}
else if $(.debug-configuration)
{
ECHO notice: [wxFormBuilder-cfg] No wxFormBuilder path
configured either explicitly or using the
$(environmentVariable) environment variable. ;
ECHO notice: [wxFormBuilder-cfg] To avoid complications please
update your configuration to includes a correct path to the
wxFormBuilder executable. ;
ECHO notice: [wxFormBuilder-cfg] wxFormBuilder executable will
be searched for on the system path. ;
}
}
if $(.debug-configuration)
{
ECHO notice: [wxFormBuilder-cfg] Will use wxFormBuilder executable
\"$(executable)\". ;
}
# Now we are sure we have everything we need to initialize this toolset.
.initialized = true ;
# Store the path to the used wxFormBuilder executable.
.executable = $(executable) ;
# Type registration.
type.register WX_FORM_BUILDER_PROJECT : fbp ;
# Parameters to be forwarded to the action rule.
toolset.flags wxFormBuilder.generate EXECUTABLE : $(.executable) ;
# Generator definition and registration.
generators.register-standard wxFormBuilder.generate :
WX_FORM_BUILDER_PROJECT : CPP(wxFormBuilderGenerated_%)
H(wxFormBuilderGenerated_%) ;
}
}
################################################################################
#
# wxFormBuilder.is-initialized()
# ------------------------------
#
# Returns whether this toolset has been initialized.
#
################################################################################
rule is-initialized ( )
{
return $(.initialized) ;
}
################################################################################
#
# Startup code executed when loading this module.
#
################################################################################
# Global variables for this module.
.executable = ;
.initialized = ;
if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
{
.debug-configuration = true ;
}

View File

@@ -5,60 +5,78 @@
<chapter id="bbv2.advanced">
<title>Overview</title>
<para>This section will provide the information necessary to create your own
projects using Boost.Build. The information provided here is relatively
high-level, and <xref linkend="bbv2.reference"/> as well as the on-line help
system must be used to obtain low-level documentation (see <xref linkend=
"bbv2.reference.init.options.help"/>).</para>
<para>
This section will provide the information necessary to create your own
projects using Boost.Build. The information provided here is relatively
high-level, and <xref linkend="bbv2.reference"/> as well as the on-line
help system must be used to obtain low-level documentation (see <xref
linkend="bbv2.reference.init.options.help"/>).
</para>
<para>Boost.Build actually consists of two parts - Boost.Jam, a build engine
with its own interpreted language, and Boost.Build itself, implemented in
Boost.Jam's language. The chain of events when you type
<command>bjam</command> on the command line is:
<para>
Boost.Build actually consists of two parts - Boost.Jam, a build engine
with its own interpreted language, and Boost.Build itself, implemented in
Boost.Jam's language. The chain of events when you type
<command>bjam</command> on the command line is as follows:
<orderedlist>
<listitem>
<para>Boost.Jam tries to find Boost.Build and loads the top-level
module. The exact process is described in <xref
linkend="bbv2.reference.init"/></para>
</listitem>
<listitem>
<para>The top-level module loads user-defined configuration files,
<filename>user-config.jam</filename> and
<filename>site-config.jam</filename>, which define available toolsets.
<para>
Boost.Jam tries to find Boost.Build and loads the top-level module.
The exact process is described in <xref linkend=
"bbv2.reference.init"/>
</para>
</listitem>
<listitem>
<para>The Jamfile in the current directory is read. That in turn
might cause reading of further Jamfiles. As a result, a tree of
projects is created, with targets inside projects.</para>
<para>
The top-level module loads user-defined configuration files,
<filename>user-config.jam</filename> and <filename>site-config.jam
</filename>, which define available toolsets.
</para>
</listitem>
<listitem>
<para>Finally, using the build request specified on the command line,
Boost.Build decides which targets should be built, and how. That
information is passed back to Boost.Jam, which takes care of
actually running commands.</para>
<para>
The Jamfile in the current directory is read. That in turn might
cause reading of further Jamfiles. As a result, a tree of projects
is created, with targets inside projects.
</para>
</listitem>
<listitem>
<para>
Finally, using the build request specified on the command line,
Boost.Build decides which targets should be built and how. That
information is passed back to Boost.Jam, which takes care of
actually running the scheduled build action commands.
</para>
</listitem>
</orderedlist>
</para>
<para>So, to be able to successfully use Boost.Build, you need to know only
four things:
<para>
So, to be able to successfully use Boost.Build, you need to know only four
things:
<itemizedlist>
<listitem>
<para><link linkend="bbv2.advanced.configuration">
How to configure Boost.Build</link></para>
<para>
<link linkend="bbv2.advanced.configuration">How to configure
Boost.Build</link>
</para>
</listitem>
<listitem>
<para><link linkend="bbv2.advanced.targets">
How to write declares targets in Jamfiles</link></para>
<para>
<link linkend="bbv2.advanced.targets">How to declare targets in
Jamfiles</link>
</para>
</listitem>
<listitem>
<para><link linkend="bbv2.advanced.build_process">
How the build process works</link></para>
<para>
<link linkend="bbv2.advanced.build_process">How the build process
works</link>
</para>
</listitem>
<listitem>
<para>Some Basics about the Boost.Jam language. See
<xref linkend="bbv2.advanced.jam_language"/>.
<para>
Some Basics about the Boost.Jam language. See <xref linkend=
"bbv2.advanced.jam_language"/>.
</para>
</listitem>
</itemizedlist>
@@ -67,153 +85,187 @@
<section id="bbv2.advanced.jam_language">
<title>Boost.Jam Language</title>
<para>This section will describe the basics of the Boost.Jam
language&#x2014;just enough for writing Jamfiles. For more information,
please see the <link linkend="bbv2.jam">Boost.Jam</link> documentation.
</para>
<para><link linkend="bbv2.jam">Boost.Jam</link> has an interpreted,
procedural language. On the lowest level, a <link linkend="bbv2.jam">
Boost.Jam</link> program consists of variables and
<indexterm><primary>rule</primary></indexterm>
<firstterm>rules</firstterm> (the Jam term for function). They are grouped
in modules&#x2014;there's one global module and a number of named modules.
Besides that, a <link linkend="bbv2.jam">Boost.Jam</link> program contains
classes and class instances.</para>
<para>Syntantically, a <link linkend="bbv2.jam">Boost.Jam</link> program
consists of two kind of elements&#x2014;keywords (which have a special
meaning to <link linkend="bbv2.jam">Boost.Jam</link>) and literals.
Consider this code:
<programlisting>
a = b ;</programlisting>
which assigns the value <literal>b</literal> to the variable
<literal>a</literal>. Here, <literal>=</literal> and <literal>;</literal>
are keywords, while <literal>a</literal> and <literal>b</literal> are
literals.
<warning>
<para>All syntax elements, even keywords, must be separated by spaces.
For example, omitting the space character before <literal>;</literal>
will lead to a syntax error.
</para>
</warning>
If you want to use a literal value that is the same as some keyword, the
value can be quoted:
<programlisting>
a = "=" ;</programlisting>
</para>
<para>All variables in <link linkend="bbv2.jam">Boost.Jam</link> have the
same type&#x2014;list of strings. To define a variable one assigns a value
to it, like in the previous example. An undefined variable is the same as
a variable with an empty value. Variables can be accessed using the
<code>$(<replaceable>variable</replaceable>)</code> syntax. For example:
<programlisting>
a = $(b) $(c) ;</programlisting>
<para>
This section will describe the basics of the Boost.Jam language&#x2014;
just enough for writing Jamfiles. For more information, please see the
<link linkend="bbv2.jam">Boost.Jam</link> documentation.
</para>
<para>
Rules are defined by specifying the rule name, the parameter names,
and the allowed size of the list value for each parameter.
<programlisting>
<link linkend="bbv2.jam">Boost.Jam</link> has an interpreted, procedural
language. On the lowest level, a <link linkend="bbv2.jam">Boost.Jam
</link> program consists of variables and <indexterm><primary>rule
</primary></indexterm> <firstterm>rules</firstterm> (Jam term for
function). They are grouped into modules&#x2014;there is one global
module and a number of named modules. Besides that, a <link linkend=
"bbv2.jam">Boost.Jam</link> program contains classes and class
instances.
</para>
<para>
Syntantically, a <link linkend="bbv2.jam">Boost.Jam</link> program
consists of two kind of elements&#x2014;keywords (which have a special
meaning to <link linkend="bbv2.jam">Boost.Jam</link>) and literals.
Consider this code:
<programlisting>
a = b ;
</programlisting>
which assigns the value <literal>b</literal> to the variable <literal>a
</literal>. Here, <literal>=</literal> and <literal>;</literal> are
keywords, while <literal>a</literal> and <literal>b</literal> are
literals.
<warning>
<para>
All syntax elements, even keywords, must be separated by spaces. For
example, omitting the space character before <literal>;</literal>
will lead to a syntax error.
</para>
</warning>
If you want to use a literal value that is the same as some keyword, the
value can be quoted:
<programlisting>
a = "=" ;
</programlisting>
</para>
<para>
All variables in <link linkend="bbv2.jam">Boost.Jam</link> have the same
type&#x2014;list of strings. To define a variable one assigns a value to
it, like in the previous example. An undefined variable is the same as a
variable with an empty value. Variables can be accessed using the
<code>$(<replaceable>variable</replaceable>)</code> syntax. For example:
<programlisting>
a = $(b) $(c) ;
</programlisting>
</para>
<para>
Rules are defined by specifying the rule name, the parameter names, and
the allowed value list size for each parameter.
<programlisting>
rule <replaceable>example</replaceable>
(
<replaceable>parameter1</replaceable> :
<replaceable>parameter2 ?</replaceable> :
<replaceable>parameter3 +</replaceable> :
<replaceable>parameter4 *</replaceable>
)
{
// body
}</programlisting>
(
<replaceable>parameter1</replaceable> :
<replaceable>parameter2 ?</replaceable> :
<replaceable>parameter3 +</replaceable> :
<replaceable>parameter4 *</replaceable>
)
{
# rule body
}
</programlisting>
When this rule is called, the list passed as the first argument must
have exactly one value. The list passed as the second argument can
either have one value of be empty. The two remaining arguments can be
arbitrarily long, but the third argument may not be empty.
</para>
<para>The overview of <link linkend="bbv2.jam">Boost.Jam</link> language
statements is given below:
<programlisting>
<para>
The overview of <link linkend="bbv2.jam">Boost.Jam</link> language
statements is given below:
<programlisting>
helper 1 : 2 : 3 ;
x = [ helper 1 : 2 : 3 ] ;</programlisting>
This code calls the named rule with the specified arguments. When the
result of the call must be used inside some expression, you need to add
brackets around the call, like shown on the second line.
<programlisting>
if cond { statements } [ else { statements } ]</programlisting>
This is a regular if-statement. The condition is composed of:
<itemizedlist>
<listitem><para>Literals (true if at least one string is not empty)</para></listitem>
<listitem><para>Comparisons: <code>a
<replaceable>operator</replaceable> b</code> where
<replaceable>operator</replaceable> is one of <code>=</code>,
<code>!=</code>, <code>&lt;</code>, <code>&gt;</code>,
<code>&lt;=</code>, <code>&gt;=</code>. The comparison is done
pairwise between each string in the left and the right arguments.
</para>
</listitem>
<listitem><para>Logical operations: <code>! a</code>, <code>a &amp;&amp;
b</code>, <code>a || b</code></para></listitem>
<listitem><para>Grouping: <code>( cond )</code></para></listitem>
</itemizedlist>
<programlisting>
for var in list { statements }</programlisting>
Executes statements for each element in list, setting the variable
<varname>var</varname> to the element value.
<programlisting>
while cond { statements }</programlisting>
Repeatedly execute statements while cond remains true upon entry.
<programlisting>
x = [ helper 1 : 2 : 3 ] ;
</programlisting>
This code calls the named rule with the specified arguments. When the
result of the call must be used inside some expression, you need to add
brackets around the call, like shown on the second line.
<programlisting>
if cond { statements } [ else { statements } ]
</programlisting>
This is a regular if-statement. The condition is composed of:
<itemizedlist>
<listitem>
<para>
Literals (true if at least one string is not empty)
</para>
</listitem>
<listitem>
<para>
Comparisons: <code>a <replaceable>operator</replaceable> b</code>
where <replaceable>operator</replaceable> is one of
<code>=</code>, <code>!=</code>, <code>&lt;</code>,
<code>&gt;</code>, <code>&lt;=</code> or <code>&gt;=</code>. The
comparison is done pairwise between each string in the left and
the right arguments.
</para>
</listitem>
<listitem>
<para>
Logical operations: <code>! a</code>, <code>a &amp;&amp; b</code>,
<code>a || b</code>
</para>
</listitem>
<listitem>
<para>
Grouping: <code>( cond )</code>
</para>
</listitem>
</itemizedlist>
<programlisting>
for var in list { statements }
</programlisting>
Executes statements for each element in list, setting the variable
<varname>var</varname> to the element value.
<programlisting>
while cond { statements }
</programlisting>
Repeatedly execute statements while cond remains true upon entry.
<programlisting>
return values ;
</programlisting>This statement should be used only inside a
rule and assigns <code>values</code> to the return value of the
rule.
<warning><para>
The <code>return</code> statement does not exit the rule. For example:
<programlisting>
</programlisting>
This statement should be used only inside a rule and assigns
<code>values</code> to the return value of the rule.
<warning>
<para>
The <code>return</code> statement does not exit the rule. For
example:
<programlisting>
rule test ( )
{
if 1 = 1 {
if 1 = 1
{
return "reasonable" ;
}
return "strange" ;
}</programlisting> will return <literal>strange</literal>, not
<literal>reasonable</literal>.
</para></warning>
<programlisting>
}
</programlisting>
will return <literal>strange</literal>, not
<literal>reasonable</literal>.
</para>
</warning>
<programlisting>
import <replaceable>module</replaceable> ;
import <replaceable>module</replaceable> : <replaceable>rule</replaceable> ;</programlisting>
The first form imports the specified bjam module. All rules from
that module are made available using the qualified name:
<code><replaceable>module</replaceable>.<replaceable>rule</replaceable></code>.
The second form imports the specified rules only, and they can be called
using unqualified names.
import <replaceable>module</replaceable> : <replaceable>rule</replaceable> ;
</programlisting>
The first form imports the specified bjam module. All rules from that
module are made available using the qualified name: <code><replaceable>
module</replaceable>.<replaceable>rule</replaceable></code>. The second
form imports the specified rules only, and they can be called using
unqualified names.
</para>
<para id="bbv2.advanced.jam_language.actions">
Sometimes, you'd need to specify the actual command lines to be used
when creating targets. In jam language, you use named actions to do this.
For example:
when creating targets. In jam language, you use named actions to do
this. For example:
<programlisting>
actions create-file-from-another
{
create-file-from-another $(&lt;) $(&gt;)
}
</programlisting>
This specifies a named action called
<literal>create-file-from-another</literal>. The text inside braces is
the command to invoke. The <literal>$(&lt;)</literal> variable will be
expanded to a list of generated files, and the
<literal>$(&gt;)</literal> variable will be expanded to a list of
source files.
This specifies a named action called <literal>
create-file-from-another</literal>. The text inside braces is the
command to invoke. The <literal>$(&lt;)</literal> variable will be
expanded to a list of generated files, and the <literal>$(&gt;)
</literal> variable will be expanded to a list of source files.
</para>
<para>To flexibly adjust command line, you can define a rule with the same
name as the action, and taking three parameters -- targets, sources and
properties. For example:
<para>
To flexibly adjust the command line, you can define a rule with the same
name as the action and taking three parameters -- targets, sources and
properties. For example:
<programlisting>
rule create-file-from-another ( targets * : sources * : properties * )
{
@@ -227,18 +279,18 @@ actions create-file-from-another
create-file-from-another $(OPTIONS) $(&lt;) $(&gt;)
}
</programlisting>
In this example, the rule checks if certain build property is specified.
If so, it sets variable <varname>OPIONS</varname> that is then used inside
the action. Note that the variables set "on a target" will be visible only
inside actions building that target, not globally. Were they set globally,
using variable named <varname>OPTIONS</varname> in two unrelated actions
would be impossible.
In this example, the rule checks if certain build property is specified.
If so, it sets variable <varname>OPIONS</varname> that is then used
inside the action. Note that the variables set "on a target" will be
visible only inside actions building that target, not globally. Were
they set globally, using variable named <varname>OPTIONS</varname> in
two unrelated actions would be impossible.
</para>
<para>More details can be found in Jam reference,
<xref linkend="jam.language.rules"/>
<para>
More details can be found in Jam reference, <xref
linkend="jam.language.rules"/>.
</para>
</section>
<section id="bbv2.advanced.configuration">
@@ -265,7 +317,7 @@ using <replaceable>tool-name</replaceable> ;
default settings. For example, it will use the <command>gcc</command>
executable found in the <envar>PATH</envar>, or look in some known
installation locations. In most cases, this strategy works automatically.
In case you have several versions of a compiler, it's installed in some
In case you have several versions of a compiler, it is installed in some
unusual location, or you need to tweak its configuration, you'll need to
pass additional parameters to the <functionname>using</functionname> rule.
The parameters to <functionname>using</functionname> can be different for
@@ -310,39 +362,39 @@ bjam --help <replaceable>tool-name</replaceable>.init
using msvc : 7.1 ;
using gcc ;
</programlisting>
If the compiler can be found in the <envar>PATH</envar> but only by a
nonstandard name, you can just supply that name:
If the compiler can be found in the <envar>PATH</envar> but only by a
nonstandard name, you can just supply that name:
<programlisting>
using gcc : : g++-3.2 ;
</programlisting>
Otherwise, it might be necessary to supply the complete path to the
compiler executable:
Otherwise, it might be necessary to supply the complete path to the
compiler executable:
<programlisting>
using msvc : : "Z:/Programs/Microsoft Visual Studio/vc98/bin/cl" ;
</programlisting>
Some Boost.Build toolsets will use that path to take additional
actions required before invoking the compiler, such as calling
vendor-supplied scripts to set up its required environment variables.
When compiler executables for C and C++ are different, path to the C++
compiler executable must be specified. The &#x201C;invocation command&#x201D;
can be any command allowed by the operating system. For example:
Some Boost.Build toolsets will use that path to take additional actions
required before invoking the compiler, such as calling vendor-supplied
scripts to set up its required environment variables. When compiler
executables for C and C++ are different, path to the C++ compiler
executable must be specified. The &#x201C;invocation command&#x201D; can
be any command allowed by the operating system. For example:
<programlisting>
using msvc : : echo Compiling &#x26;&#x26; foo/bar/baz/cl ;
</programlisting>
will work.
will work.
</para>
<para>To configure several versions of a toolset, simply invoke
the <functionname>using</functionname> rule multiple times:
<para>
To configure several versions of a toolset, simply invoke the
<functionname>using</functionname> rule multiple times:
<programlisting>
using gcc : 3.3 ;
using gcc : 3.4 : g++-3.4 ;
using gcc : 3.2 : g++-3.2 ;
</programlisting>
Note that in the first call to
<functionname>using</functionname>, the compiler found in the
<envar>PATH</envar> will be used, and there's no need to
explicitly specify the command.
Note that in the first call to <functionname>using</functionname>, the
compiler found in the <envar>PATH</envar> will be used, and there is no
need to explicitly specify the command.
</para>
<para>As shown above, both the <parameter
@@ -664,34 +716,31 @@ exe b : [ glob *.cpp ] ; # all .cpp files in this directory are sources
<para>
<!-- use "project-id" here? -->
The list of sources can also refer to other main targets.
Targets in the same project can be referred to by name, while
targets in other projects must be qualified with a directory or a
symbolic project name. The directory/project name is separated from
the target name by a double forward slash. There's no special syntax to
distinguish the directory name from the project name&#x2014;the part before
the double slash is first looked up as project name, and then as directory
name. For example:
The list of sources can also refer to other main targets. Targets in
the same project can be referred to by name, while targets in other
projects must be qualified with a directory or a symbolic project
name. The directory/project name is separated from the target name by
a double forward slash. There is no special syntax to distinguish the
directory name from the project name&#x2014;the part before the double
slash is first looked up as project name, and then as directory name.
For example:
</para>
<programlisting>
lib helper : helper.cpp ;
exe a : a.cpp helper ;
# Since all project ids start with slash, ".." is directory name.
# Since all project ids start with slash, ".." is a directory name.
exe b : b.cpp ..//utils ;
exe c : c.cpp /boost/program_options//program_options ;
</programlisting>
<para>
The first exe uses the library defined in the same
project. The second one uses some target (most likely library)
defined by Jamfile one level higher. Finally, the third target
uses some <ulink url="http://boost.org">C++ Boost</ulink>
library, referring to it by absolute symbolic name. More
information about target references can be found in <xref
linkend="bbv2.tutorial.libs"/> and <xref
The first exe uses the library defined in the same project. The second
one uses some target (most likely a library) defined by a Jamfile one
level higher. Finally, the third target uses a <ulink url=
"http://boost.org">C++ Boost</ulink> library, referring to it using
its absolute symbolic name. More information about target references
can be found in <xref linkend="bbv2.tutorial.libs"/> and <xref
linkend="bbv2.reference.ids"/>.
</para>
</section>
<section id="bbv2.advanced.targets.requirements">

View File

@@ -2,191 +2,232 @@
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
<chapter id="bbv2.faq">
<title>Frequently Asked Questions</title>
<chapter id="bbv2.faq">
<title>Frequently Asked Questions</title>
<section>
<title>
How do I get the current value of feature in Jamfile?
</title>
<section>
<title>
How do I get the current value of feature in Jamfile?
</title>
<para>
This is not possible, since Jamfile does not have "current" value of any
feature, be it toolset, build variant or anything else. For a single invocation of
<filename>bjam</filename>, any given main target can be built with several property sets.
For example, user can request two build variants on the command line. Or one library
is built as shared when used from one application, and as static when used from another.
Obviously, Jamfile is read only once, so generally, there's no single value of a feature
you can access in Jamfile.
</para>
<para>
This is not possible, since Jamfile does not have "current" value of any
feature, be it toolset, build variant or anything else. For a single
invocation of <filename>bjam</filename>, any given main target can be
built with several property sets. For example, user can request two build
variants on the command line. Or one library is built as shared when used
from one application, and as static when used from another. Each Jamfile
is read only once so generally there is no single value of a feature you
can access in Jamfile.
</para>
<para>A feature has a specific value only when building a target, and there are two ways how you
can use that value:</para>
<itemizedlist>
<listitem><simpara>Use conditional requirements or indirect conditional requirements. See
<xref linkend="bbv2.advanced.targets.requirements.conditional"/>.</simpara>
</listitem>
<listitem>Define a custom generator and a custom main target type. The custom generator can do arbitrary processing
or properties. See the <xref linkend="bbv2.extender">extender manual</xref>.
</listitem>
</itemizedlist>
<para>
A feature has a specific value only when building a target, and there are
two ways you can use that value:
</para>
</section>
<itemizedlist>
<listitem>
<simpara>
Use conditional requirements or indirect conditional requirements. See
<xref linkend="bbv2.advanced.targets.requirements.conditional"/>.
</simpara>
</listitem>
<listitem>
Define a custom generator and a custom main target type. The custom
generator can do arbitrary processing or properties. See the <xref
linkend="bbv2.extender">extender manual</xref>.
</listitem>
</itemizedlist>
</section>
<section>
<title>
I'm getting "Duplicate name of actual target" error. What
does it mean?
</title>
<para>
The most likely case is that you're trying to
compile the same file twice, with almost the same,
but differing properties. For example:
<section>
<title>
I am getting a "Duplicate name of actual target" error. What does that
mean?
</title>
<para>
The most likely case is that you are trying to compile the same file
twice, with almost the same, but differing properties. For example:
<programlisting>
exe a : a.cpp : &lt;include&gt;/usr/local/include ;
exe b : a.cpp ;
</programlisting>
</programlisting>
</para>
<para>
The above snippet requires two different compilations
of 'a.cpp', which differ only in 'include' property.
Since the 'include' property is free, Boost.Build
can't generate two objects files into different directories.
On the other hand, it's dangerous to compile the file only
once -- maybe you really want to compile with different
includes.
The above snippet requires two different compilations of
<code>a.cpp</code>, which differ only in their <literal>include</literal>
property. Since the <literal>include</literal> feature is declared as
<literal>free</literal> Boost.Build does not create a separate build
directory for each of its values and those two builds would both produce
object files generated in the same build directory. Ignoring this and
compiling the file only once would be dangerous as different includes
could potentially cause completely different code to be compiled.
</para>
<para>
To solve this issue, you need to decide if file should
be compiled once or twice.</para>
To solve this issue, you need to decide if the file should be compiled
once or twice.
</para>
<orderedlist>
<listitem>
<para>Two compile file only once, make sure that properties
are the same:
<listitem>
<para>
To compile the file only once, make sure that properties are the same
for both target requests:
<programlisting>
exe a : a.cpp : &lt;include&gt;/usr/local/include ;
exe b : a.cpp : &lt;include&gt;/usr/local/include ;
</programlisting></para></listitem>
<listitem><para>
If changing the properties is not desirable, for example
if 'a' and 'b' target have other sources which need
specific properties, separate 'a.cpp' into it's own target:
</programlisting>
or:
<programlisting>
obj a_obj : a.cpp : &lt;include&gt;/usr/local/include ;
exe a : a_obj ;
</programlisting></para></listitem>
<listitem><para>
To compile file twice, you can make the object file local
to the main target:
alias a-with-include : a.cpp : &lt;include&gt;/usr/local/include ;
exe a : a-with-include ;
exe b : a-with-include ;
</programlisting>
or if you want the <literal>includes</literal> property not to affect
how any other sources added for the built <code>a</code> and
<code>b</code> executables would be compiled:
<programlisting>
exe a : [ obj a_obj : a.cpp ] : &lt;include&gt;/usr/local/include ;
obj a-obj : a.cpp : &lt;include&gt;/usr/local/include ;
exe a : a-obj ;
exe b : a-obj ;
</programlisting>
</para>
<para>
Note that in both of these cases the <literal>include</literal>
property will be applied only for building these object files and not
any other sources that might be added for targets <code>a</code> and
<code>b</code>.
</para>
</listitem>
<listitem>
<para>
To compile the file twice, you can tell Boost.Build to compile it to
two separate object files like so:
<programlisting>
obj a_obj : a.cpp : &lt;include&gt;/usr/local/include ;
obj b_obj : a.cpp ;
exe a : a_obj ;
exe b : b_obj ;
</programlisting>
or you can make the object file targets local to the main target:
<programlisting>
exe a : [ obj a_obj : a.cpp : &lt;include&gt;/usr/local/include ] ;
exe b : [ obj a_obj : a.cpp ] ;
</programlisting></para></listitem>
</programlisting>
which will cause Boost.Build to actually change the generated object
file names a bit for you and thus avoid any conflicts.
</para>
<para>
Note that in both of these cases the <literal>include</literal>
property will be applied only for building these object files and not
any other sources that might be added for targets <code>a</code> and
<code>b</code>.
</para>
</listitem>
</orderedlist>
</orderedlist>
<para>
A good question is why Boost.Build can not use some of the above
approaches automatically. The problem is that such magic would only help
in half of the cases, while in the other half it would be silently doing
the wrong thing. It is simpler and safer to ask the user to clarify his
intention in such cases.
</para>
</section>
<para>
A good question is why Boost.Build can't use some of the above
approaches automatically. The problem is that such magic would
require additional implementation complexities and would only
help in half of the cases, while in other half we'd be silently
doing the wrong thing. It's simpler and safe to ask user to
clarify his intention in such cases.
</para>
</section>
<section id="bbv2.faq.envar">
<title>
<section id="bbv2.faq.envar">
<title>
Accessing environment variables
</title>
</title>
<para>
<para>
Many users would like to use environment variables in Jamfiles, for
example, to control location of external libraries. In many cases you
better declare those external libraries in the site-config.jam file, as
documented in the <link linkend="bbv2.recipies.site-config">recipes
section</link>. However, if the users already have the environment variables set
up, it's not convenient to ask them to set up site-config.jam files as
well, and using environment variables might be reasonable.
example, to control the location of external libraries. In many cases it
is better to declare those external libraries in the site-config.jam file,
as documented in the <link linkend="bbv2.recipies.site-config">recipes
section</link>. However, if the users already have the environment
variables set up, it may not be convenient for them to set up their
site-config.jam files as well and using the environment variables might be
reasonable.
</para>
<para>In Boost.Build V2, each Jamfile is a separate namespace, and the
variables defined in environment is imported into the global
namespace. Therefore, to access environment variable from Jamfile, you'd
need the following code:
<para>
Boost.Jam automatically imports all environment variables into its
built-in .ENVIRON module so user can read them from there directly or by
using the helper os.environ rule. For example:
<programlisting>
import os ;
local unga-unga = [ os.environ UNGA_UNGA ] ;
ECHO $(unga-unga) ;
</programlisting>
or a bit more realistic:
<programlisting>
import os ;
local SOME_LIBRARY_PATH = [ os.environ SOME_LIBRARY_PATH ] ;
exe a : a.cpp : &lt;include&gt;$(SOME_LIBRARY_PATH) ;
</programlisting>
</para>
</section>
<section>
<title>
How to control properties order?
</title>
<para>For internal reasons, Boost.Build sorts all the properties
alphabetically. This means that if you write:
<programlisting>
exe a : a.cpp : &lt;include&gt;b &lt;include&gt;a ;
</programlisting>
then the command line with first mention the "a" include directory, and
then "b", even though they are specified in the opposite order. In most
cases, the user doesn't care. But sometimes the order of includes, or
other properties, is important. For example, if one uses both the C++
Boost library and the "boost-sandbox" (libraries in development), then
include path for boost-sandbox must come first, because some headers may
override ones in C++ Boost. For such cases, a special syntax is
provided:
<programlisting>
exe a : a.cpp : &lt;include&gt;a&amp;&amp;b ;
</programlisting>
</para>
<para>The <code>&amp;&amp;</code> symbols separate values of an
property, and specify that the order of the values should be preserved. You
are advised to use this feature only when the order of properties really
matters, and not as a convenient shortcut. Using it everywhere might
negatively affect performance.
</para>
</section>
<section>
<title>
How to control the library order on Unix?
How to control properties order?
</title>
<para>On the Unix-like operating systems, the order in which static
libraries are specified when invoking the linker is important, because by
default, the linker uses one pass though the libraries list. Passing the
libraries in the incorrect order will lead to a link error. Further, this
behaviour is often used to make one library override symbols from
another. So, sometimes it's necessary to force specific order of
libraries.
<para>
For internal reasons, Boost.Build sorts all the properties alphabetically.
This means that if you write:
<programlisting>
exe a : a.cpp : &lt;include&gt;b &lt;include&gt;a ;
</programlisting>
then the command line with first mention the <code>a</code> include
directory, and then <code>b</code>, even though they are specified in the
opposite order. In most cases, the user does not care. But sometimes the
order of includes, or other properties, is important. For such cases, a
special syntax is provided:
<programlisting>
exe a : a.cpp : &lt;include&gt;a&amp;&amp;b ;
</programlisting>
</para>
<para>Boost.Build tries to automatically compute the right order. The
primary rule is that if library a "uses" library b, then library a will
appear on the command line before library b. Library a is considered to
use b is b is present either in the sources of a or in its
requirements. To explicitly specify the use relationship one can use the
&lt;use&gt; feature. For example, both of the following lines will cause
a to appear before b on the command line:
<para>
The <code>&amp;&amp;</code> symbols separate property values and specify
that their order should be preserved. You are advised to use this feature
only when the order of properties really matters and not as a convenient
shortcut. Using it everywhere might negatively affect performance.
</para>
</section>
<section>
<title>
How to control the library linking order on Unix?
</title>
<para>
On Unix-like operating systems, the order in which static libraries are
specified when invoking the linker is important, because by default, the
linker uses one pass though the libraries list. Passing the libraries in
the incorrect order will lead to a link error. Further, this behaviour is
often used to make one library override symbols from another. So,
sometimes it is necessary to force specific library linking order.
</para>
<para>
Boost.Build tries to automatically compute the right order. The primary
rule is that if library <code>a</code> "uses" library <code>b</code>, then
library <code>a</code> will appear on the command line before library
<code>b</code>. Library <code>a</code> is considered to use <code>b</code>
if <code>b</code> is present either in the <code>a</code> library's
sources or its usage is listed in its requirements. To explicitly specify
the <literal>use</literal> relationship one can use the
<literal>&lt;use&gt;</literal> feature. For example, both of the following
lines will cause <code>a</code> to appear before <code>b</code> on the
command line:
<programlisting>
lib a : a.cpp b ;
lib a : a.cpp : &lt;use&gt;b ;
@@ -194,21 +235,22 @@ lib a : a.cpp : &lt;use&gt;b ;
</para>
<para>
The same approach works for searched libraries, too:
The same approach works for searched libraries as well:
<programlisting>
lib z ;
lib png : : &lt;use&gt;z ;
exe viewer : viewer png z ;
</programlisting>
</para>
</section>
<section id="bbv2.faq.external">
<title>Can I get output of external program as a variable in a Jamfile?
<title>
Can I get capture external program output using a Boost.Jam variable?
</title>
<para>The <code>SHELL</code> builtin can be used for the purpose:
<para>
The <literal>SHELL</literal> builtin rule may be used for this purpose:
<programlisting>
local gtk_includes = [ SHELL "gtk-config --cflags" ] ;
</programlisting>
@@ -216,7 +258,8 @@ local gtk_includes = [ SHELL "gtk-config --cflags" ] ;
</section>
<section>
<title>How to get the project root (a.k.a. Jamroot.jam) location?
<title>
How to get the project root (a.k.a. Jamroot) location?
</title>
<para>
@@ -230,18 +273,20 @@ path-constant TOP : . ;
</section>
<section>
<title>How to change compilation flags for one file?
<title>
How to change compilation flags for one file?
</title>
<para>If one file must be compiled with special options, you need to
explicitly declare an <code>obj</code> target for that file and then use
that target in your <code>exe</code> or <code>lib</code> target:
<para>
If one file must be compiled with special options, you need to explicitly
declare an <code>obj</code> target for that file and then use that target
in your <code>exe</code> or <code>lib</code> target:
<programlisting>
exe a : a.cpp b ;
obj b : b.cpp : &lt;optimization&gt;off ;
</programlisting>
Of course you can use other properties, for example to specify specific
compiler options:
C/C++ compiler options:
<programlisting>
exe a : a.cpp b ;
obj b : b.cpp : &lt;cflags&gt;-g ;
@@ -252,149 +297,162 @@ obj b : b.cpp : &lt;cflags&gt;-g ;
exe a : a.cpp b ;
obj b : b.cpp : &lt;variant&gt;release:&lt;optimization&gt;off ;
</programlisting>
</para>
</section>
<section id="bbv2.faq.dll-path">
<title>Why are the <code>dll-path</code> and
<code>hardcode-dll-paths</code> properties useful?
<title>
Why are the <literal>dll-path</literal> and <literal>hardcode-dll-paths
</literal> properties useful?
</title>
<para>(This entry is specific to Unix system.)Before answering the
questions, let's recall a few points about shared libraries. Shared
libraries can be used by several applications, or other libraries,
without physically including the library in the application. This can
greatly decrease the total size of applications. It's also possible to
upgrade a shared library when the application is already
installed. Finally, shared linking can be faster.
</para>
<para>However, the shared library must be found when the application is
started. The dynamic linker will search in a system-defined list of
paths, load the library and resolve the symbols. Which means that you
should either change the system-defined list, given by the
<envar>LD_LIBRARY_PATH</envar> environment variable, or install the
libraries to a system location. This can be inconvenient when
developing, since the libraries are not yet ready to be installed, and
cluttering system paths is undesirable. Luckily, on Unix there's another
way.
</para>
<para>An executable can include a list of additional library paths, which
will be searched before system paths. This is excellent for development,
because the build system knows the paths to all libraries and can include
them in executables. That's done when the <code>hardcode-dll-paths</code>
feature has the <literal>true</literal> value, which is the
default. When the executables should be installed, the story is
different.
<note>
<para>
This entry is specific to Unix systems.
</para>
</note>
<para>
Before answering the questions, let us recall a few points about shared
libraries. Shared libraries can be used by several applications, or other
libraries, without physically including the library in the application
which can greatly decrease the total application size. It is also possible
to upgrade a shared library when the application is already installed.
</para>
<para>
Obviously, installed executable should not hardcode paths to your
development tree. (The <code>stage</code> rule explicitly disables the
<code>hardcode-dll-paths</code> feature for that reason.) However, you
can use the <code>dll-path</code> feature to add explicit paths
However, in order for application depending on shared libraries to be
started the OS may need to find the shared library when the application is
started. The dynamic linker will search in a system-defined list of paths,
load the library and resolve the symbols. Which means that you should
either change the system-defined list, given by the <envar>LD_LIBRARY_PATH
</envar> environment variable, or install the libraries to a system
location. This can be inconvenient when developing, since the libraries
are not yet ready to be installed, and cluttering system paths may be
undesirable. Luckily, on Unix there is another way.
</para>
<para>
An executable can include a list of additional library paths, which will
be searched before system paths. This is excellent for development because
the build system knows the paths to all libraries and can include them in
the executables. That is done when the <literal>hardcode-dll-paths
</literal> feature has the <literal>true</literal> value, which is the
default. When the executables should be installed, the story is different.
</para>
<para>
Obviously, installed executable should not contain hardcoded paths to your
development tree. <!-- Make the following parenthised sentence a footer
note --> (The <literal>install</literal> rule explicitly disables the
<literal>hardcode-dll-paths</literal> feature for that reason.) However,
you can use the <literal>dll-path</literal> feature to add explicit paths
manually. For example:
<programlisting>
stage installed : application : &lt;dll-path&gt;/usr/lib/snake
&lt;location&gt;/usr/bin ;
install installed : application : &lt;dll-path&gt;/usr/lib/snake
&lt;location&gt;/usr/bin ;
</programlisting>
will allow the application to find libraries placed to
<filename>/usr/lib/snake</filename>.
will allow the application to find libraries placed in the <filename>
/usr/lib/snake</filename> directory.
</para>
<para>If you install libraries to a nonstandard location and add an
explicit path, you get more control over libraries which will be used. A
library of the same name in a system location will not be inadvertently
used. If you install libraries to a system location and do not add any
paths, the system administrator will have more control. Each library can
be individually upgraded, and all applications will use the new library.
<para>
If you install libraries to a nonstandard location and add an explicit
path, you get more control over libraries which will be used. A library of
the same name in a system location will not be inadvertently used. If you
install libraries to a system location and do not add any paths, the
system administrator will have more control. Each library can be
individually upgraded, and all applications will use the new library.
</para>
<para>Which approach is best depends on your situation. If the libraries
are relatively standalone and can be used by third party applications,
they should be installed in the system location. If you have lots of
libraries which can be used only by your application, it makes sense to
install it to a nonstandard directory and add an explicit path, like the
example above shows. Please also note that guidelines for different
systems differ in this respect. The Debian guidelines prohibit any
additional search paths, and Solaris guidelines suggest that they should
<para>
Which approach is best depends on your situation. If the libraries are
relatively standalone and can be used by third party applications, they
should be installed in the system location. If you have lots of libraries
which can be used only by your application, it makes sense to install them
to a nonstandard directory and add an explicit path, like the example
above shows. Please also note that guidelines for different systems differ
in this respect. For example, the Debian GNU guidelines prohibit any
additional search paths while Solaris guidelines suggest that they should
always be used.
</para>
</section>
<section id="bbv2.recipies.site-config">
<title>Targets in site-config.jam</title>
<para>It is desirable to declare standard libraries available on a
given system. Putting target declaration in Jamfile is not really
good, since locations of the libraries can vary. The solution is
to declare the targets in site-config.jam:</para>
<para>
It is desirable to declare standard libraries available on a given system.
Putting target declaration in a specific project's Jamfile is not really
good, since locations of the libraries can vary between different
development machines and then such declarations would need to be
duplicated in different projects. The solution is to declare the targets
in Boost.Build's <filename>site-config.jam</filename> configuration file:
<programlisting>
project site-config ;
lib zlib : : &lt;name&gt;z ;
</programlisting>
</para>
<para>Recall that both <filename>site-config.jam</filename> and
<filename>user-config.jam</filename> are projects, and everything
you can do in a Jamfile you can do in those files. So, you declare
a project id and a target. Now, one can write:
<para>
Recall that both <filename>site-config.jam</filename> and
<filename>user-config.jam</filename> are projects, and everything you can
do in a Jamfile you can do in those files as well. So, you declare a
project id and a target. Now, one can write:
<programlisting>
exe hello : hello.cpp /site-config//zlib ;
</programlisting>
in any Jamfile.</para>
in any Jamfile.
</para>
</section>
<section id="bbv2.faq.header-only-libraries">
<title>Header-only libraries</title>
<para>In modern C++, libraries often consist of just header files, without
any source files to compile. To use such libraries, you need to add proper
includes and, maybe, defines, to your project. But with large number of
external libraries it becomes problematic to remember which libraries are
header only, and which are "real" ones. However, with Boost.Build a
header-only library can be declared as Boost.Build target and all
dependents can use such library without remebering if it's header-only or not.
</para>
<para>Header-only libraries are declared using the <code>alias</code> rule,
that specifies only usage requirements, for example:
<programlisting>
alias mylib
: # no sources
: # no build requirements
: # no default build
: &lt;include&gt;whatever
;
</programlisting>
The includes specified in usage requirements of <code>mylib</code> are
automatically added to build properties of all dependents. The dependents
need not care if <code>mylib</code> is header-only or not, and it's possible
to later make <code>mylib</code> into a regular compiled library.
<para>
In modern C++, libraries often consist of just header files, without any
source files to compile. To use such libraries, you need to add proper
includes and possibly defines to your project. But with a large number of
external libraries it becomes problematic to remember which libraries are
header only, and which ones you have to link to. However, with Boost.Build
a header-only library can be declared as Boost.Build target and all
dependents can use such library without having to remeber whether it is a
header-only library or not.
</para>
<para>
If you already have proper usage requirements declared for project where
header-only library is defined, you don't need to duplicate them for
Header-only libraries may be declared using the <code>alias</code> rule,
specifying their include path as a part of its usage requirements, for
example:
<programlisting>
alias my-lib
: # no sources
: # no build requirements
: # no default build
: &lt;include&gt;whatever ;
</programlisting>
The includes specified in usage requirements of <code>my-lib</code> are
automatically added to all of its dependants' build properties. The
dependants need not care if <code>my-lib</code> is a header-only or not,
and it is possible to later make <code>my-lib</code> into a regular
compiled library without having to that its dependants' declarations.
</para>
<para>
If you already have proper usage requirements declared for a project where
a header-only library is defined, you do not need to duplicate them for
the <code>alias</code> target:
<programlisting>
project my : usage-requirements &lt;include&gt;whatever ;
alias mylib ;
</programlisting>
</programlisting>
</para>
</section>
</chapter>
</chapter>
<!--
Local Variables:
mode: nxml
sgml-indent-data: t
sgml-indent-data: t
sgml-parent-document: ("userman.xml" "chapter")
sgml-set-face: t
End:
-->
-->

View File

@@ -420,41 +420,55 @@ path-constant DATA : data/a.txt ;
<listitem>
<para>
A feature that combines several low-level features, making
it easy to request common build configurations.
A feature combining several low-level features, making it easy to
request common build configurations.
</para>
<para><emphasis role="bold">Allowed values:</emphasis> <literal>debug</literal>, <literal>release</literal>,
<literal>profile</literal>.</para>
<para>
<emphasis role="bold">Allowed values:</emphasis>
<literal>debug</literal>, <literal>release</literal>,
<literal>profile</literal>.
</para>
<para>The value <literal>debug</literal> expands to</para>
<para>
The value <literal>debug</literal> expands to
</para>
<programlisting>
&lt;optimization&gt;off &lt;debug-symbols&gt;on &lt;inlining&gt;off &lt;runtime-debugging&gt;on
</programlisting>
<para>The value <literal>release</literal> expands to</para>
<para>
The value <literal>release</literal> expands to
</para>
<programlisting>
&lt;optimization&gt;speed &lt;debug-symbols&gt;off &lt;inlining&gt;full &lt;runtime-debugging&gt;off
</programlisting>
<para>The value <literal>profile</literal> expands to the same as
<literal>release</literal>, plus:</para>
<para>
The value <literal>profile</literal> expands to the same as
<literal>release</literal>, plus:
</para>
<programlisting>
&lt;profiling&gt;on &lt;debug-symbols&gt;on
</programlisting>
<para>User can define his own build variants using the <code>variant</code> rule from the <code>common</code>
module.</para>
<para><emphasis role="bold">Notee:</emphasis> Runtime
debugging is on in debug builds to suit the expectations of
people used to various IDEs.
<!-- Define "runtime debugging." Why will those people expect it to be on in debug builds? -->
<para>
Users can define their own build variants using the
<code>variant</code> rule from the <code>common</code> module.
</para>
</listitem></varlistentry>
<para>
<emphasis role="bold">Note:</emphasis> Runtime debugging is on in
debug builds to suit the expectations of people used to various
IDEs.
<!-- Define "runtime debugging". Why will those people expect it to
be on in debug builds? -->
</para>
</listitem>
</varlistentry>
<varlistentry id="bbv2.advanced.builtins.features.link">
<term><literal>link</literal></term>
@@ -465,10 +479,11 @@ path-constant DATA : data/a.txt ;
<literal>static</literal></para>
<simpara>
A feature that controls how libraries are built.
A feature controling how libraries are built.
</simpara>
</listitem></varlistentry>
</listitem>
</varlistentry>
<varlistentry id="bbv2.advanced.builtins.features.runtime-link">
<indexterm><primary>runtime linking</primary></indexterm>
@@ -720,7 +735,10 @@ path-constant DATA : data/a.txt ;
<varlistentry><term><literal>instruction-set</literal></term>
<indexterm><primary>instruction-set</primary></indexterm>
<listitem>
<para>Allowed values for this feature depend on used toolset.</para>
<para>
<emphasis role="bold">Allowed values:</emphasis> depend on the used
toolset.
</para>
<para>The <literal>instruction-set</literal> specifies for which
specific instruction set the code should be generated. The
@@ -751,6 +769,31 @@ path-constant DATA : data/a.txt ;
</listitem>
</varlistentry>
<varlistentry><term><literal>c++-template-depth</literal></term>
<listitem>
<para>
<emphasis role="bold">Allowed values:</emphasis> Any positive
integer.
</para>
<para>
This feature allows configuring a C++ compiler with the maximal
template instantiation depth parameter. Specific toolsets may or may
not provide support for this feature depending on whether their
compilers provide a corresponding command-line option.
</para>
<para>
<emphasis role="bold">Note:</emphasis> Due to some internal details
in the current Boost Build implementation it is not possible to have
features whose valid values are all positive integer. As a
workaround a large set of allowed values has been defined for this
feature and, if a different one is needed, user can easily add it by
calling the feature.extend rule.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
@@ -970,9 +1013,9 @@ using msvc : &toolset_ops; ;
</varlistentry>
<varlistentry>
<term><literal>setup-amd64></literal></term>
<term><literal>setup-i386></literal></term>
<term><literal>setup-ia64></literal></term>
<term><literal>setup-amd64</literal></term>
<term><literal>setup-i386</literal></term>
<term><literal>setup-ia64</literal></term>
<listitem><para>The filename of the target platform specific
environment setup script to run before invoking any of the tools

File diff suppressed because it is too large Load Diff

View File

@@ -3,9 +3,9 @@ Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example show how to add a new target type and a new tool
support to Boost.Build. Please refer to extender manual for
complete description of this example.
This example show how to add a new target type and a new tool support to
Boost.Build. Please refer to extender manual for a complete description of this
example.
Note that this example requires Python. If cygwin Python on Windows is
to be used, please go to "verbatim.jam" and follow instructions there.
Note that this example requires Python. If cygwin Python on Windows is to be
used, please go to "verbatim.jam" and follow instructions there.

View File

@@ -1,6 +1,5 @@
usage
Usage: codegen class_name
This program takes a template of C++ code and replaces of
occurences of %class_name% with the passed 'class_name'
parameter.
This program takes a template of C++ code and replaces of all occurrences of
%class_name% with the passed 'class_name' parameter.

View File

@@ -1,15 +1,11 @@
This example shows the 'generate' rule, that
allows you to construct target using any arbitrary
set of transformation and commands.
The rule is similar to 'make' and 'notfile', but
unlike those, you can operate in terms of
Boost.Build 'virtual targets', which is more
flexible.
Please consult the docs for more explanations.
# Copyright 2007 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows the 'generate' rule, that allows you to construct target
using any arbitrary set of transformation and commands.
The rule is similar to 'make' and 'notfile', but unlike those, you can operate
in terms of Boost.Build 'virtual targets', which is more flexible.
Please consult the docs for more explanations.

View File

@@ -1,7 +1,6 @@
This example shows how to declare a new generator class. It's necessary
when generator's logic is more complex that just running a single tool.
# Copyright 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows how to declare a new generator class. It is necessary when
generator's logic is more complex that just running a single tool.

View File

@@ -1,14 +1,14 @@
Copyright 2003 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)
Copyright 2003 Vladimir Prus
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows how it's possible to used GNU gettext utilities with
This example shows how it is possible to use GNU gettext utilities with
Boost.Build.
A simple translation file is compiled and installed as message catalog for
russian. The main application explicitly switches to russian locale and
output the translation of "hello".
russian. The main application explicitly switches to russian locale and outputs
the translation of "hello".
To test:
@@ -22,4 +22,3 @@ To test even more:
- edit "russian.po"
- run bjam
- run "main"

View File

@@ -1,2 +1 @@
exe hello : hello.cpp ;

View File

@@ -0,0 +1,7 @@
Copyright 2008 Jurko Gospodnetic
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows a very basic Boost Build project set up so it compiles a
single executable from a single source file.

View File

@@ -1,23 +0,0 @@
import notfile ;
import common ;
exe main : main.cpp ;
# Create 'main.cpp' from 'main.cpp.pro' using action
# 'do-something' defined below.
#
make main.cpp : main_cpp.pro : @do-something ;
# In this example, we'll just copy a file.
# Need to find out the name of a command to copy a file.
CP = [ common.copy-command ] ;
# The action itself.
# The 'CP' variable is defined below
# $(>) is source
# $(<) is target
actions do-something
{
$(CP) $(>) $(<)
}

2
v2/example/make/foo.py Normal file
View File

@@ -0,0 +1,2 @@
import sys
open(sys.argv[2], "w").write(open(sys.argv[1]).read())

View File

@@ -0,0 +1,10 @@
import toolset ;
path-constant HERE : . ;
make main.cpp : main_cpp.pro : @do-something ;
toolset.flags do-something PYTHON : <python.interpreter> ;
actions do-something
{
"$(PYTHON:E=python)" "$(HERE)/foo.py" "$(>)" "$(<)"
}

View File

@@ -1,2 +1 @@
int main() { return 0; }
int main() {}

View File

@@ -1,7 +1,7 @@
Copyright 2002, 2005 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)
Copyright 2002, 2005 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)
Example of using custom command to create one file from
another, using the builtin 'make' rule.
Example of using custom command to create one file from another, using the
built-in 'make' rule.

View File

@@ -1,6 +1,6 @@
Copyright 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)
Copyright 2006 Vladimir Prus
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows how you can use Python modules from Boost.Build.
@@ -8,8 +8,9 @@ This example shows how you can use Python modules from Boost.Build.
In order to do this, you need to build bjam with Python support, by running:
./build.sh --with-python=/usr
in jam directory. (Replace /usr with the root of your Python installation.)
The integration between Python and bjam is very basic now, but enough to
be useful.
in the jam/src directory (replace /usr with the root of your Python
installation).
The integration between Python and bjam is very basic now, but enough to be
useful.

View File

@@ -7,15 +7,14 @@ This directory contains Boost.Build examples for the Qt library
(http://www.trolltech.com/products/qt/index.html).
The current examples are:
1. Basic setup -- application with several sources and moccable header.
2. Using of .ui source file.
3. Running .cpp files via the moc tool.
1. Basic setup -- application with several sources and moccable header.
2. Using of .ui source file.
3. Running .cpp files via the moc tool.
For convenience, there are examples both for 3.* and 4.* version of Qt,
they are mostly identical and differ only in source code.
For convenience, there are examples both for 3.* and 4.* version of Qt, they are
mostly identical and differ only in source code.
All examples assumes that you just installed Boost.Build and that QTDIR
environment variables is set (typical values can be /usr/share/qt3 and
/usr/share/qt4). After adding "using qt ..." to your user-config.jam, you'd
have to removing "using qt ; " statements from Jamroot file of examples.
/usr/share/qt4). After adding "using qt ..." to your user-config.jam, you would
have to remove "using qt ; " statements from example Jamroot files.

View File

@@ -1,13 +0,0 @@
# Copyright 2004 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)
# By default, build the project with two variants
# we've defined in project-root.jam
project
: default-build crazy super_release
;
exe a : a.cpp libs//l ;

View File

@@ -0,0 +1,11 @@
# Copyright 2004 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)
# By default, build the project with the two variants we have defined in
# jamroot.jam.
project : default-build crazy super_release ;
exe a : a.cpp libs//l ;

View File

@@ -1,11 +1,11 @@
Copyright 2004 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)
Copyright 2004 Vladimir Prus
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
This example shows how user can create his own build variants.
Two variants are defined: "crazy", which is just random combination
of properties, and "super-release", which is inherited from "release",
and differs by a single define.
This example shows how user can create his own build variants. Two variants are
defined: "crazy", which is just a random combination of properties, and
"super-release", which is inherited from "release", and differs by a single
define.
See the project-root.jam for the definitions.
See the jamroot.jam for the definitions.

View File

@@ -1,731 +0,0 @@
# Copyright 2003 Dave Abrahams
# 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)
# Importing by a different name keeps PyChecker happy
from __future__ import generators as generators_
import sys
class Generator(object):
"""Representation of a transformation from source to target types.
sources and targets may be either strings or sequences of
strings.
>>> print Generator(('obj*', 'lib*'), 'exe')
exe <- obj*,lib*
>>> assert Generator('c','o').unary
"""
def __init__(self, sources, targets):
"""
>>> g = Generator(['obj*', 'z', 'cpp'], ['lib','dll'])
>>> g.signature
[('cpp', 1, 1), ('obj', 0, '*'), ('z', 1, 1)]
>>> g = Generator('cpp', 'obj')
>>> g.signature
[('cpp', 1, 1)]
"""
self.sources = _sequence_of_strings(sources)
self.targets =_sequence_of_strings(targets)
self.targets_ = _string_multiset(targets)
signature = {}
stars = {}
for s in self.sources:
if s.endswith('*'):
stars[s[:-1]] = 1
signature.setdefault(s[:-1],0)
else:
signature[s] = signature.get(s,0) + 1
self.signature = []
for t, n in signature.items():
if t in stars:
self.signature.append((t, n, '*'))
else:
self.signature.append((t, n, n))
self.signature.sort() # makes doctests nicer
# Remember whether the signature is strictly unary
self.unary = not stars and len(self.sources) == 1
def match(self, group):
"""If group satisfies an element of the signature, returns an
amended signature that consists of all other elements.
Otherwise, returns None.
>>> g = Generator(['obj*', 'z', 'cpp', 'z'], ['lib','dll'])
>>> g.match(TargetTypeGroup('obj',12))
[('cpp', 1, 1), ('z', 2, 2)]
>>> g.match(TargetTypeGroup('cpp',12)) # None
>>> g.match(TargetTypeGroup('cpp',1))
[('obj', 0, '*'), ('z', 2, 2)]
>>> g.match(TargetTypeGroup('z',2))
[('cpp', 1, 1), ('obj', 0, '*')]
>>> Generator('cpp','obj').match(TargetTypeGroup('cpp',1))
[]
>>> Generator('cpp','obj').match(TargetTypeGroup('cpp',12))
[]
"""
if self in group.generators:
return None
for i in range(len(self.signature)):
e = self.signature[i]
if e[0] == group.target_type:
if self.unary:
return []
if e[1] > group.size or e[2] < group.size:
return None
else:
return self.signature[:i] + self.signature[i+1:]
return None
def __str__(self):
"""Make a nice human-readable representation
>>> g = Generator(['obj*', 'z', 'cpp'], ['lib','dll'])
>>> print g
lib,dll <- obj*,z,cpp
"""
return ','.join(self.targets) + ' <- ' + ','.join(self.sources)
def __type_list_rep(self, type_list):
if len(type_list) == 1:
return repr(type_list[0])
else:
return repr(type_list)
def __repr__(self):
return (
self.__module__ + '.' + type(self).__name__ + '(' +
self.__type_list_rep(self.sources)
+ ', ' + self.__type_list_rep(self.targets) + ')'
)
def _dict_tuple(d):
l = d.items()
l.sort()
return tuple(l)
def _sorted(list):
list.sort()
return list
class GeneratorSet(object):
def __init__(self):
self.all_generators = {}
self.generators_by_type = {}
def __iadd__(self, generator):
"""Add a generator to the set
>>> s = GeneratorSet()
>>> s += Generator('foo', 'bar')
>>> s += Generator('foo', 'baz')
>>> s += Generator(['foo','bar'], 'baz')
>>> s += Generator('bar', ['baz', 'mumble'])
>>> s += Generator('bar*', ['bing'])
>>> print s
{
bar:
baz <- foo,bar
baz,mumble <- bar
bing <- bar*
foo:
bar <- foo
baz <- foo
baz <- foo,bar
}
"""
if not generator in self.all_generators:
self.all_generators[generator] = 1
for t in generator.sources:
if t.endswith('*'):
t = t[:-1]
l = self[t]
l.append(generator)
return self
def __isub__(self, generator):
for t in generator.sources:
if t.endswith('*'):
t = t[:-1]
self[t].remove(generator)
return self
def __getitem__(self, t):
"""Given a target type, return a list of all the generators
which can consume that target type.
"""
return self.generators_by_type.setdefault(t,[])
def __str__(self):
# import pprint
# return pprint.pformat(self.generators_by_type)
s = []
for k,v in _sorted(self.generators_by_type.items()):
s += [ ' ' + k + ':\n ' + '\n '.join([ str(x) for x in v ]) ]
return '{\n' + '\n'.join(s) + '\n}'
def _dicts_intersect(d1, d2):
"""True iff d1 and d2 have a key in common
>>> assert _dicts_intersect({1:0, 2:0}, {2:0})
>>> assert _dicts_intersect({2:0}, {1:0, 2:0})
>>> assert not _dicts_intersect({1:0, 3:0}, {2:0})
>>> assert not _dicts_intersect({2:0}, {1:0, 3:0})
"""
if len(d2) < len(d1):
tmp = d1
d1 = d2
d2 = tmp
for k in d1.iterkeys():
if k in d2:
return True
return False
class TargetTypeGroup(object):
instances = 0
def __init__(
self
, target_type
, size # how many of that type are in the group.
, parents = ()
, generator = None):
"""
>>> g1 = TargetTypeGroup('x', 1)
>>> assert not g1.extra_targets
>>> assert g1.consumed_sources == { g1:1 }
>>> g2 = TargetTypeGroup('x', 1)
>>> g3 = TargetTypeGroup('x', 1, [g1,g2])
>>> assert g1 in g3.consumed_sources
>>> assert g2 in g3.consumed_sources
>>> assert not g3 in g3.consumed_sources
"""
self.target_type = target_type
self.size = size
self.parents = parents
self.generator = generator
self.siblings = None
self.id = TargetTypeGroup.instances
self.ambiguous = reduce(lambda x,y: x or y.ambiguous and 1,
parents, None)
self.generators = { generator : 1 } # it doesn't hurt to store None here
ignored = [ self.generators.update(p.generators) for p in parents ]
self.__constituents = None
self.__extra_targets = None
TargetTypeGroup.instances += 1
if generator:
self.moves = { (id(generator),id(parents)) : 1 }
else:
self.moves = {}
if not parents:
self.consumed_sources = {self:1}
self.__extra_targets = ()
else:
ignored = [ self.moves.update(p.moves) for p in parents ]
if len(parents) == 1:
self.consumed_sources = parents[0].consumed_sources
else:
self.consumed_sources = {}
for c in parents:
self.consumed_sources.update(c.consumed_sources)
# constituents property - the set of all target groups consumed in
# creating this group
def __get_constituents(self):
if self.__constituents is None:
self.__constituents = {self:1}
for c in self.parents:
self.__constituents.update(c.constituents)
return self.__constituents
constituents = property(__get_constituents)
cost = property(lambda self: len(self.moves))
# extra targets property - in general, every target group sits at
# the root of a DAG. The extra targets are the ones produced by
# generators that run in this DAG but which are not part of the
# DAG, i.e. are not constituents. In the example below, X and Y
# are extra targets of A.
#
# A X B,C <- D
# / \ / C,Y <- E
# B C Y A,X <- B,C
# \ / \ /
# Sources: D E
#
# We use the extra targets to determine the equivalence of two
# search States
def __get_extra_targets(self):
if self.__extra_targets is None:
if len(self.parents) == 1 and not self.siblings:
self.__extra_targets = self.parents[0].extra_targets
else:
# all siblings are created incidentally
if self.siblings:
t = tuple([s for s in self.siblings if s != self])
else:
t = ()
# Any groups created incidentally as part of generating my
# parents are also incidental to my generation
for c in self.parents:
for i in c.extra_targets:
if i not in self.constituents:
t += (i,)
self.__extra_targets = _sort_tuple(t)
return self.__extra_targets
extra_targets = property(__get_extra_targets)
def set_siblings(self, sibs):
assert self.__extra_targets is None, \
"can't set siblings after extra targets already computed."
assert self.parents, "original source nodes don't have siblings"
self.siblings = sibs
def __repr__(self):
return '%s.%s(#%s$%s)' % (self.size,self.target_type,self.id,self.cost)
def is_compatible_with(self, other):
"""True iff self and other can be used to trigger a generator.
>>> g1 = TargetTypeGroup('x', 1)
>>> g2 = TargetTypeGroup('x', 1)
>>> assert g1.is_compatible_with(g2)
>>> g3 = TargetTypeGroup('x', 1, [g1])
>>> g4 = TargetTypeGroup('x', 1, [g2])
>>> assert g3.is_compatible_with(g4)
>>> assert g3.is_compatible_with(g2)
>>> assert not g3.is_compatible_with(g1)
>>> assert not g2.is_compatible_with(g4)
>>> g5 = TargetTypeGroup('x', 1, [g3])
>>> assert not g5.is_compatible_with(g1)
"""
return not _dicts_intersect(
self.constituents, other.constituents)
def all_compatible(self, others):
"""True iff self is_compatible with every element of other
"""
for o in others:
if not self.is_compatible_with(o):
return False
return True
def atoms(self):
"""If this group was formed by combining other groups without
a generator, return a set of its nearest parent groups which
were not formed that way. Otherwise, return a set
containing only this group.
>>> g1 = TargetTypeGroup('x',1)
>>> g2 = TargetTypeGroup('x',1)
>>> a = TargetTypeGroup('x',2, [g1,g2]).atoms()
>>> assert g1 in a and g2 in a and len(a) == 2
"""
if self.generator or not self.parents:
return (self,)
x = ()
for p in self.parents:
x += p.atoms()
return x
def consumes(self, others):
"""True iff not self is_compatible with every element of other
"""
for o in others:
if self.is_compatible_with(o):
return False
return True
def _string_multiset(s):
x = {}
for t in _sequence_of_strings(s):
x[t] = x.get(t,0) + 1
return x
def parent_sets(chosen, signature, all_groups, generator):
"""Given an already-chosen tuple of TargetTypeGroups and a signature
of the groups left to choose, generates all mutually-compatible
combinations of groups starting with chosen
>>> TargetTypeGroup.instances = 0
>>> groups = {
... 'x': [ TargetTypeGroup('x', 1) ],
... 'y': [ TargetTypeGroup('y', 1), TargetTypeGroup('y',2) ],
... 'z': [ TargetTypeGroup('z', 1) ]
... }
>>> signature = (('y',0,'*'),('z',1,'1'))
>>> chosen = (groups['x'][0],)
>>> [ x for x in parent_sets(chosen, signature, groups, Generator('x',('y*', 'z'))) ]
[(1.x(#0$0), 1.z(#3$0)), (1.x(#0$0), 1.y(#1$0), 1.z(#3$0)), (1.x(#0$0), 2.y(#2$0), 1.z(#3$0))]
"""
if len(signature) == 0:
# The entire signature was satisfied; we can just yield the
# one result
yield chosen
else:
# find all ways to satisfy the next element of the signature
# which are compatible with the already-chosen groups. If
# there are no ways, we will fall off the end here and the
# ultimate result will be empty.
t, min, max = signature[0]
if min == 0:
for s in parent_sets(chosen, signature[1:], all_groups, generator):
yield s
for g in all_groups[t]:
# can only use a generator once in any path
if generator in g.generators:
continue
if (g.size >= min and g.size <= max and
g.all_compatible(chosen)):
for s in parent_sets(
chosen + (g,), signature[1:], all_groups, generator
):
yield s
debug = None
def _sort_tuple(t):
"""copies the given sequence into a new tuple in sorted order"""
l = list(t)
l.sort()
return tuple(l)
def _sequence_of_strings(s_or_l):
"""if the argument is a string, wraps a tuple around it.
Otherwise, returns the argument untouched
"""
if isinstance(s_or_l, type('')):
return (s_or_l,)
else:
return s_or_l
def _examine_edge(states, queue, g):
"""Handle a possible new state in the search.
"""
g_state = State(g)
v = states.setdefault(g_state, g_state)
if v.group is g:
queue.append(g_state)
return False
if v.group.cost > g.cost:
if debug:
print 'reducing cost of state(%s) via %s' % (v.group,g)
v.group.ambiguous = None
v.group = g
elif v.group.cost < g.cost:
if debug:
print 'discarding %s due to lower cost state(%s)' % (g, v.group)
elif not (g.generator or v.group.generator) \
and _sort_tuple(g.atoms()) == _sort_tuple(v.group.atoms()):
# These are two different ways of combining the same groups of
# a given type to produce a larger group, without using a generator
if debug:
print 'discarding %s as a redundant formulation of %s' % (g,v.group)
else:
if debug:
print '%s is an ambiguous path due to %s' % (v.group, g)
# Remember the group which caused the ambiguity
v.group.ambiguous = g
return True
class State(object):
"""A wrapper around a TargetTypeGroup which makes it hashable on
the part of its data which determines its ability to contribute to
producing the goal target type.
"""
def __init__(self, group):
self.group = group
def __hash__(self):
g = self.group
x = g.consumed_sources.keys()
x.sort()
return hash((g.target_type, g.size, _sort_tuple(g.extra_targets), tuple(x)))
def __eq__(self, other):
return (
self.group.target_type == other.group.target_type
and self.group.size == other.group.size
and self.group.extra_targets == other.group.extra_targets
and self.group.consumed_sources == other.group.consumed_sources)
queue_moves = 0
def optimal_graphs(target_type, source_groups, generators):
"""A 'simple generator' that produces the sequence of least-cost
solutions for producing the
target type from a subset of the source_groups, using the given
generators.
"""
# An index from target type to lists of groups with that type.
all_groups = {}
# Prime the priority Queue
q = [ State(g) for g in source_groups ]
# Keep a record of all known states in the search
states = dict([ (s,s) for s in q ])
solution_cost = None
while q:
# remove a group from the queue
g = q[0].group
del q[0]
global queue_moves
queue_moves += 1
global debug
if debug:
print '-------'
print graph(g)
if g.target_type == target_type: # and g.consumes(source_groups):
solution_cost = g.cost
yield g
if g.consumes(source_groups): # Nothing left to find
return
# combine with all like groups which are compatible
for g2 in all_groups.get(g.target_type,()):
if g2.is_compatible_with(g):
_examine_edge(
states, q,
TargetTypeGroup(g.target_type, g2.size + g.size, (g,g2)))
# expand with all generators which can be triggered as a
# result of adding this group
for generator in generators[g.target_type]:
match = generator.match(g)
if match is None:
continue
if debug:
print generator,' matched with ', match
# for all sets of parents which match the generator and
# include g
for s in parent_sets((g,), match, all_groups, generator):
# Create the products of running this generator with
# the given parent set
siblings = ()
for t,n in generator.targets_.items():
# Unary generators run as many times as necessary
# to consume the group
if (generator.unary):
n *= g.size
siblings += (TargetTypeGroup(t, n, s, generator),)
# Make sure groups know about their siblings
if len(siblings) > 1:
for product in siblings:
product.set_siblings(siblings)
if debug:
print siblings, '<-', list(s)
# Add new search states to the queue
for sib in siblings:
_examine_edge(states, q, sib)
# Add to the set of all groups so that we can combine it with
# others in future iterations
l = all_groups.get(g.target_type)
if l is None:
l = all_groups.setdefault(g.target_type,[])
l.append(g)
# Sort the queue; in 'real life' use a priority queue
q.sort(lambda v1,v2: v1.group.cost - v2.group.cost)
def graph(group, indent = 0, visited = None):
"""Produce a string representation of the search graph
that produced the given group.
"""
if (visited is None):
visited = {}
s = indent * ' '
s += repr(group)
if group in visited:
s += '...\n'
else:
visited[group] = True
s += '[%s]' % group.generator
if group.ambiguous:
s += ' *ambiguous* '
if type(group.ambiguous) is not type(1):
s += 'due to %s' % group.ambiguous
if group.siblings:
s += ' siblings ' + str([sib for sib in group.siblings if
sib != group])
s += '\n' + '\n'.join(
[graph(g,indent+1,visited) for g in group.parents])
return s
def ambiguities(group):
"""Returns a list of groups that caused ambiguities with this one
or its constituents.
"""
result = []
for g in group.parents:
result.extend(ambiguities(g))
if group.ambiguous and type(group.ambiguous) is not type(1):
result.append(group.ambiguous)
return result
def search(generators, targets, sources):
import sys
global queue_moves
TargetTypeGroup.instances = 0
queue_moves = 0
# Remember what we started with
source_groups = tuple([
TargetTypeGroup(i[0],i[1])
for i in _string_multiset(sources).items() ])
solutions = {}
max_consumed = 0
for g in optimal_graphs(targets, source_groups, generators):
if len(g.consumed_sources) > max_consumed:
max_consumed = len(g.consumed_sources)
g2 = solutions.setdefault(len(g.consumed_sources), g)
if g2 is not g:
if g2.cost == g.cost:
g2.ambiguous = g
if max_consumed:
g = solutions[max_consumed]
print 80 * '='
print graph(g)
if g.ambiguous:
print 40 * '-'
print 'ambiguities:'
for a in ambiguities(g):
print graph(a)
print
print queue_moves, 'queue_moves'
print 80 * '='
sys.stdout.flush()
print queue_moves, 'queue moves'
print '\n\n*****\n\n'
def test():
"""Runs Volodya's example, but doesn't get the result he'd like.
"""
# EST_EXE <- OBJ*
# OBJ <- CPP
# {CPP,STATIC_DATA} <- NM_ASM
# {CPP,CPP} <- ECPP (only first CPP must be further converted into NM_ASM)
# NM_ASM <- CPP
# {CPP,STATIC_DATA} <- STATIC_DATA*
# STATIC_DATA <- NM_ASM
# NM_OBJ <- NM_ASM
# NM_EXE <- NM_OBJ*
generators = GeneratorSet()
generators += Generator('OBJ*', 'EST_EXE')
generators += Generator('CPP', 'OBJ')
generators += Generator('NM_ASM', ('CPP', 'STATIC_DATA'))
generators += Generator('ECPP', ('CPP','CPP'))
generators += Generator('CPP', 'NM_ASM')
generators += Generator('STATIC_DATA*', ('CPP', 'STATIC_DATA'))
generators += Generator('NM_ASM', 'NM_OBJ')
generators += Generator('NM_OBJ*', 'NM_EXE')
search(generators, 'EST_EXE', ('CPP', 'NM_ASM', 'ECPP'))
# Try the same search with a source type that can't be consumed.
# This will exhaust all transformations before stopping.
search(generators, 'EST_EXE', ('CPP', 'NM_ASM', 'ECPP', 'FOO'))
search(generators, 'NM_EXE', ('CPP'))
def run(args = None):
import doctest
import sys
if args is not None:
sys.argv = args
error = doctest.testmod(sys.modules.get(__name__))
if not error[0]:
global debug
if '--debug' in sys.argv:
debug = 1
test()
return error
if __name__ == '__main__':
import sys
sys.exit(run()[0])

View File

@@ -67,7 +67,7 @@ div.sidebar p.rubric {
<input type="submit" value="Search">
</form> -->
</ul>
<li><a href="http://zigzag.cs.msu.su/boost.build">Bug tracker</a>
<li><a href="http://zigzag.lvk.cs.msu.su/boost.build">Bug tracker</a>
<!-- <li>Rate Boost.Build: <a href="http://freshmeat.net/rate/38012/">Freshmeat</a> -->
</ul>
</p>

View File

@@ -242,11 +242,13 @@ rule import ( module-names + : rules-opt * : rename-opt * )
{
if ( $(rules-opt) = * || ! $(rules-opt) ) && $(rename-opt)
{
import errors ;
errors.error "Rule aliasing is only available for explicit imports." ;
}
if $(module-names[2]) && ( $(rules-opt) || $(rename-opt) )
{
import errors ;
errors.error "When loading multiple modules, no specific rules or"
"renaming is allowed" ;
}

View File

@@ -25,5 +25,5 @@ echo "Building packages and uploading docs"
./roll.sh > ../roll-log 2>&1
cd ..
echo "Uploading packages"
scp boost-build.zip boost-build.tar.bz2 vladimir_prus@shell.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2 > scp-log
scp boost-build.zip boost-build.tar.bz2 vladimir_prus,boost@web.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2 > scp-log
echo "Nightly build successful"

View File

@@ -26,7 +26,7 @@ rm -rf example/versioned
find . -maxdepth 1 -type f | egrep -v "boost-build.jam|timestamp.txt|roll.sh|bootstrap.jam|build-system.jam|boost_build.png|index.html|hacking.txt|site-config.jam|user-config.jam" | xargs rm -f
# Build the documentation
touch doc/project-root.jam
touch doc/jamroot.jam
export BOOST_BUILD_PATH=`pwd`
cd doc
/home/ghost/Work/Boost/boost-svn/tools/jam/src/bin.linuxx86/bjam --v2
@@ -65,5 +65,5 @@ urchinTracker();
EOF`
echo $x
perl -pi -e "s|</body>|$x</body>|" `find doc -name '*.html'`
scp -r doc example boost_build.png *.html hacking.txt vladimir_prus@shell.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2
scp ../userman.pdf vladimir_prus@shell.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2/doc
scp -r doc example boost_build.png *.html hacking.txt vladimir_prus,boost@web.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2
scp ../userman.pdf vladimir_prus,boost@web.sourceforge.net:/home/groups/b/bo/boost/htdocs/boost-build2/doc

View File

@@ -67,7 +67,7 @@ def get_toolset():
# Detect the host OS.
windows = False
if os.environ.get('OS','').lower().startswith('windows') or \
if os.environ.get('OS', '').lower().startswith('windows') or \
os.__dict__.has_key('uname') and \
os.uname()[0].lower().startswith('cygwin'):
windows = True
@@ -88,13 +88,13 @@ def prepare_suffix_map(toolset):
suffixes['.lib'] = '.a' # static libs have '.a' suffix with mingw...
suffixes['.obj'] = '.o'
suffixes['.implib'] = '.lib'
if os.__dict__.has_key('uname') and os.uname()[0] == 'Darwin':
if os.__dict__.has_key('uname') and (os.uname()[0] == 'Darwin'):
suffixes['.dll'] = '.dylib'
def re_remove(sequence, regex):
me = re.compile(regex)
result = filter( lambda x: me.match(x), sequence )
result = filter(lambda x: me.match(x), sequence)
if 0 == len(result):
raise ValueError()
for r in result:
@@ -102,7 +102,7 @@ def re_remove(sequence, regex):
def glob_remove(sequence, pattern):
result = fnmatch.filter(sequence,pattern)
result = fnmatch.filter(sequence, pattern)
if 0 == len(result):
raise ValueError()
for r in result:
@@ -206,7 +206,7 @@ class Tester(TestCmd.TestCmd):
jam_build_dir = ""
if os.name == 'nt':
jam_build_dir = "bin.ntx86"
elif os.name == 'posix' and os.__dict__.has_key('uname'):
elif (os.name == 'posix') and os.__dict__.has_key('uname'):
if os.uname()[0].lower().startswith('cygwin'):
jam_build_dir = "bin.cygwinx86"
if 'TMP' in os.environ and os.environ['TMP'].find('~') != -1:
@@ -238,7 +238,7 @@ class Tester(TestCmd.TestCmd):
else:
raise "Don't know directory where Jam is built for this system: " + os.name
# Find where jam_src is located. Try for the debug version if it's
# Find where jam_src is located. Try for the debug version if it is
# lying around.
dirs = [os.path.join('../../../jam/src', jam_build_dir + '.debug'),
os.path.join('../../../jam/src', jam_build_dir),
@@ -445,7 +445,7 @@ class Tester(TestCmd.TestCmd):
finally:
self.last_build_time_finish = time.time()
if status != None and _failed(self, status):
if (status != None) and _failed(self, status):
expect = ''
if status != 0:
expect = " (expected %d)" % status
@@ -456,7 +456,7 @@ class Tester(TestCmd.TestCmd):
annotation("reason", "error returned by bjam")
self.fail_test(1)
if not stdout is None and not match(self.stdout(), stdout):
if not (stdout is None) and not match(self.stdout(), stdout):
annotation("failure", "Unexpected stdout")
annotation("Expected STDOUT", stdout)
annotation("Actual STDOUT", self.stdout())
@@ -470,7 +470,7 @@ class Tester(TestCmd.TestCmd):
intel_workaround = re.compile("^xi(link|lib): executing.*\n", re.M)
actual_stderr = re.sub(intel_workaround, "", self.stderr())
if not stderr is None and not match(actual_stderr, stderr):
if not (stderr is None) and not match(actual_stderr, stderr):
annotation("failure", "Unexpected stderr")
annotation("Expected STDERR", stderr)
annotation("Actual STDERR", self.stderr())
@@ -480,7 +480,7 @@ class Tester(TestCmd.TestCmd):
if not expected_duration is None:
actual_duration = self.last_build_time_finish - self.last_build_time_start
if ( actual_duration > expected_duration ):
if (actual_duration > expected_duration):
print "Test run lasted %f seconds while it was expected to " \
"finish in under %f seconds." % (actual_duration,
expected_duration)
@@ -493,9 +493,9 @@ class Tester(TestCmd.TestCmd):
def glob_file(self, name):
result = None
if hasattr(self,'difference'):
if hasattr(self, 'difference'):
for f in self.difference.added_files+self.difference.modified_files+self.difference.touched_files:
if fnmatch.fnmatch(f,name):
if fnmatch.fnmatch(f, name):
result = self.native_file_name(f)
break
if not result:
@@ -510,7 +510,7 @@ class Tester(TestCmd.TestCmd):
name = string.replace(name, "$toolset", self.toolset+"*")
name = self.glob_file(name)
openMode = "r"
if ( binary ):
if binary:
openMode += "b"
else:
openMode += "U"
@@ -564,7 +564,7 @@ class Tester(TestCmd.TestCmd):
def expect_addition(self, names):
for name in self.adjust_names(names):
try:
glob_remove(self.unexpected_difference.added_files,name)
glob_remove(self.unexpected_difference.added_files, name)
except:
print "File %s not added as expected" % name
self.fail_test(1)
@@ -575,7 +575,7 @@ class Tester(TestCmd.TestCmd):
def expect_removal(self, names):
for name in self.adjust_names(names):
try:
glob_remove(self.unexpected_difference.removed_files,name)
glob_remove(self.unexpected_difference.removed_files, name)
except:
print "File %s not removed as expected" % name
self.fail_test(1)
@@ -586,13 +586,14 @@ class Tester(TestCmd.TestCmd):
def expect_modification(self, names):
for name in self.adjust_names(names):
try:
glob_remove(self.unexpected_difference.modified_files,name)
glob_remove(self.unexpected_difference.modified_files, name)
except:
print "File %s not modified as expected" % name
self.fail_test(1)
def ignore_modification(self, wildcard):
self.ignore_elements(self.unexpected_difference.modified_files, wildcard)
self.ignore_elements(self.unexpected_difference.modified_files, \
wildcard)
def expect_touch(self, names):
d = self.unexpected_difference
@@ -607,7 +608,7 @@ class Tester(TestCmd.TestCmd):
while filesets:
try:
glob_remove(filesets[-1],name)
glob_remove(filesets[-1], name)
break
except ValueError:
filesets.pop()
@@ -646,7 +647,7 @@ class Tester(TestCmd.TestCmd):
self.fail_test(1)
def expect_nothing_more(self):
# Not totally sure about this change, but I don't see a good
# Not totally sure about this change, but I do not see a good
# alternative.
if windows:
self.ignore('*.ilk') # MSVC incremental linking files.
@@ -676,12 +677,12 @@ class Tester(TestCmd.TestCmd):
break
if expected_to_exist and not found:
annotation( "failure",
annotation("failure",
"Did not find expected line:\n%s\nin output:\n%s" %
(expected, content))
self.fail_test(1)
if not expected_to_exist and found:
annotation( "failure",
annotation("failure",
"Found an unexpected line:\n%s\nin output:\n%s" %
(expected, content))
self.fail_test(1)
@@ -712,22 +713,22 @@ class Tester(TestCmd.TestCmd):
matched = False
if exact:
matched = fnmatch.fnmatch(actual,content)
matched = fnmatch.fnmatch(actual, content)
else:
def sorted_(x):
x.sort()
return x
actual_ = map(lambda x: sorted_(x.split()),actual.splitlines())
content_ = map(lambda x: sorted_(x.split()),content.splitlines())
actual_ = map(lambda x: sorted_(x.split()), actual.splitlines())
content_ = map(lambda x: sorted_(x.split()), content.splitlines())
if len(actual_) == len(content_):
matched = map(
lambda x,y: map(lambda n,p: fnmatch.fnmatch(n,p),x,y),
actual_, content_ )
lambda x, y: map(lambda n, p: fnmatch.fnmatch(n, p), x, y),
actual_, content_)
matched = reduce(
lambda x,y: x and reduce(
lambda a,b: a and b,
y ),
matched )
lambda x, y: x and reduce(
lambda a, b: a and b,
y),
matched)
if not matched:
print "Expected:\n"
@@ -744,7 +745,7 @@ class Tester(TestCmd.TestCmd):
open(a, "w").write(actual)
print "DIFFERENCE"
if os.system("diff -u " + e + " " + a):
print "Unable to compute difference: diff -u %s %s" % (e,a)
print "Unable to compute difference: diff -u %s %s" % (e, a)
os.unlink(e)
os.unlink(a)
else:
@@ -793,7 +794,7 @@ class Tester(TestCmd.TestCmd):
tail = "lib" + tail
result = os.path.join(head, tail)
# If we want to use this name in a Jamfile, we better convert \ to /, as
# otherwise we'd have to quote \.
# otherwise we would have to quote \.
result = string.replace(result, "\\", "/")
return result
@@ -873,7 +874,7 @@ class List:
return str(self.l)
def __repr__(self):
return ( self.__module__ + '.List('
return (self.__module__ + '.List('
+ repr(string.join(self.l, ' '))
+ ')')

View File

@@ -1,6 +1,6 @@
# Niklaus Giger, 2005-03-15
# Testing whether we may run a test in a absolute directories
# There are no tests for temporary directories as this is implictly tested in a lot of other cases
# Testing whether we may run a test in absolute directories. There are no tests
# for temporary directories as this is implictly tested in a lot of other cases.
import BoostBuild
import os
@@ -9,10 +9,10 @@ import string
t = BoostBuild.Tester(arguments="pwd", executable="jam", workdir=os.getcwd(),
pass_toolset=0)
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
actions print_pwd { pwd ; }
print_pwd pwd ;
Always pwd ;
ALWAYS pwd ;
""")
t.run_build_system(status=0)
@@ -30,5 +30,5 @@ if string.rfind(t.stdout(), 'build/v2/test') == -1:
t.run_build_system(status=1, subdir="/must/fail/with/absolute/path",
stderr=None)
t.cleanup
t.cleanup()

View File

@@ -1,72 +1,76 @@
#!/usr/bin/python
# Copyright 2003, 2004 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)
# Copyright 2003, 2004 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)
# Test that sources with absolute names are handled OK.
from BoostBuild import Tester
t = Tester()
import BoostBuild
t.write("project-root.jam", """
t = BoostBuild.Tester()
t.write("jamroot.jam", """
path-constant TOP : . ;
""")
t.write("Jamfile", """
t.write("jamfile.jam", """
local pwd = [ PWD ] ;
ECHO $(pwd) XXXXX ;
exe hello : $(pwd)/hello.cpp $(TOP)/empty.cpp ;
""")
t.write("hello.cpp", "int main() { return 0; }\n")
t.write("hello.cpp", "int main() {}\n")
t.write("empty.cpp", "\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/hello.exe")
# Test a contrived case. There, absolute name is used in
# standalone project (not Jamfile). Moreover, the target with
# absolute name is returned by 'alias' and used from other project.
t.write("a.cpp", """
int main()
{
return 0;
}
# Test a contrived case. There, absolute name is used in a standalone project
# (not Jamfile). Moreover, the target with an absolute name is returned by
# 'alias' and used from another project.
t.write("a.cpp", """
int main() {}
""")
t.write("Jamfile", """
exe a : /standalone//a ;
t.write("jamfile.jam", """
exe a : /standalone//a ;
""")
t.write("project-root.jam", """
import standalone ;
t.write("jamroot.jam", """
import standalone ;
""")
t.write("standalone.jam", """
t.write("standalone.jam", """
import project ;
project.initialize $(__name__) ;
project standalone ;
local pwd = [ PWD ] ;
alias a : $(pwd)/a.cpp ;
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/a.exe")
# Test absolute path in target ids
# Test absolute path in target ids.
t.rm(".")
t.write("d1/project-root.jam", "")
t.write("d1/Jamfile", """
t.write("d1/jamroot.jam", "")
t.write("d1/jamfile.jam", """
exe a : a.cpp ;
""")
t.write("d1/a.cpp", """
int main() { return 0; }
int main() {}
""")
t.write("d2/project-root.jam", "")
t.write("d2/Jamfile", """
t.write("d2/jamroot.jam", "")
t.write("d2/jamfile.jam", """
local pwd = [ PWD ] ;
alias x : $(pwd)/../d1//a ;
""")

View File

@@ -5,7 +5,7 @@
# 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)
from BoostBuild import Tester, List
import BoostBuild
################################################################################
@@ -19,7 +19,7 @@ def test_alias_rule(t):
"""Basic alias rule test.
"""
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe a : a.cpp ;
exe b : b.cpp ;
exe c : c.cpp ;
@@ -31,7 +31,7 @@ alias src : s.cpp ;
exe hello : hello.cpp src ;
""")
t.write("a.cpp", "int main() { return 0; }\n")
t.write("a.cpp", "int main() {}\n")
t.copy("a.cpp", "b.cpp")
t.copy("a.cpp", "c.cpp")
t.copy("a.cpp", "hello.cpp")
@@ -39,19 +39,20 @@ exe hello : hello.cpp src ;
# Check that targets to which "bin1" refers are updated, and only those.
t.run_build_system("bin1")
t.expect_addition(List("bin/$toolset/debug/") * "a.exe a.obj")
t.expect_addition(BoostBuild.List("bin/$toolset/debug/") * "a.exe a.obj")
t.expect_nothing_more()
# Try again with "bin2"
t.run_build_system("bin2")
t.expect_addition(List("bin/$toolset/debug/") * "b.exe b.obj")
t.expect_addition(BoostBuild.List("bin/$toolset/debug/") * "b.exe b.obj")
t.expect_nothing_more()
# Try building everything, making sure 'hello' target is created.
t.run_build_system()
t.expect_addition(List("bin/$toolset/debug/") * "hello.exe hello.obj")
t.expect_addition(BoostBuild.List("bin/$toolset/debug/") * \
"hello.exe hello.obj")
t.expect_addition("bin/$toolset/debug/s.obj")
t.expect_addition(List("bin/$toolset/debug/") * "c.exe c.obj")
t.expect_addition(BoostBuild.List("bin/$toolset/debug/") * "c.exe c.obj")
t.expect_nothing_more()
@@ -68,7 +69,7 @@ def test_alias_source_usage_requirements(t):
anywhere in the source.
"""
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
lib l : l.cpp : : : <define>WANT_MAIN ;
alias la : l ;
exe main : main.cpp la ;
@@ -84,7 +85,7 @@ foo() {}
t.write("main.cpp", """
#ifdef WANT_MAIN
int main() { return 0; }
int main() {}
#endif
""")
@@ -98,7 +99,7 @@ int main() { return 0; }
#
################################################################################
t = Tester()
t = BoostBuild.Tester()
test_alias_rule(t)
test_alias_source_usage_requirements(t)

View File

@@ -1,42 +1,44 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2003, 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)
# Copyright 2003 Dave Abrahams
# Copyright 2003, 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)
# Test main target alternatives.
from BoostBuild import Tester
from string import find
t = Tester()
import BoostBuild
import string
t = BoostBuild.Tester()
# Test that basic alternatives selection works.
t.write("project-root.jam", " ")
t.write("Jamfile", """
t.write("jamroot.jam", "")
t.write("jamfile.jam", """
exe a : a_empty.cpp ;
exe a : a.cpp : <variant>release ;
""")
t.write("a_empty.cpp", "")
t.write("a.cpp", "int main() { return 0; }\n")
t.write("a.cpp", "int main() {}\n")
t.run_build_system("release")
t.expect_addition("bin/$toolset/release/a.exe")
# Test that alternative selection works for ordinary
# properties, in particular user-defined.
t.write("project-root.jam", " ")
t.write("Jamfile", """
# Test that alternative selection works for ordinary properties, in particular
# user-defined.
t.write("jamroot.jam", "")
t.write("jamfile.jam", """
import feature ;
feature.feature X : off on : propagated ;
exe a : b.cpp ;
exe a : a.cpp : <X>on ;
""")
t.write("b.cpp", "int main() { return 0; }\n")
t.write("b.cpp", "int main() {}\n")
t.rm("bin")
@@ -48,11 +50,8 @@ t.expect_addition("bin/$toolset/debug/X-on/a.obj")
t.rm("bin")
# Test that everything works ok even with default
# build.
t.write("Jamfile", """
# Test that everything works ok even with default build.
t.write("jamfile.jam", """
exe a : a_empty.cpp : <variant>release ;
exe a : a.cpp : <variant>debug ;
""")
@@ -60,12 +59,10 @@ exe a : a.cpp : <variant>debug ;
t.run_build_system()
t.expect_addition("bin/$toolset/debug/a.exe")
# Test that only properties which are in build request
# matters when selection alternative. IOW, alternative
# with <variant>release is better than one with
# Test that only properties which are in build request matter for alternative
# selection. IOW, alternative with <variant>release is better than one with
# <variant>debug when building release version.
t.write("Jamfile", """
t.write("jamfile.jam", """
exe a : a_empty.cpp : <variant>debug ;
exe a : a.cpp : <variant>release ;
""")
@@ -73,10 +70,9 @@ exe a : a.cpp : <variant>release ;
t.run_build_system("release")
t.expect_addition("bin/$toolset/release/a.exe")
# Test that free properties do not matter. We really don't
# want <cxxflags> property in build request to affect
# alternative selection.
t.write("Jamfile", """
# Test that free properties do not matter. We really do not want <cxxflags>
# property in build request to affect alternative selection.
t.write("jamfile.jam", """
exe a : a_empty.cpp : <variant>debug <define>FOO <include>BAR ;
exe a : a.cpp : <variant>release ;
""")
@@ -85,28 +81,26 @@ t.rm("bin/$toolset/release/a.exe")
t.run_build_system("release define=FOO")
t.expect_addition("bin/$toolset/release/a.exe")
# Test that abibuity is reported correctly
t.write("Jamfile", """
# Test that ambiguity is reported correctly.
t.write("jamfile.jam", """
exe a : a_empty.cpp ;
exe a : a.cpp ;
""")
t.run_build_system("--no-error-backtrace", status=None)
t.fail_test(find(t.stdout(), "No best alternative") == -1)
t.fail_test(string.find(t.stdout(), "No best alternative") == -1)
# Another ambiguity test: two matches properties in one alternative are
# neither better nor worse than a single one in another alternative.
t.write("Jamfile", """
# Another ambiguity test: two matches properties in one alternative are neither
# better nor worse than a single one in another alternative.
t.write("jamfile.jam", """
exe a : a_empty.cpp : <optimization>off <profiling>off ;
exe a : a.cpp : <debug-symbols>on ;
""")
t.run_build_system("--no-error-backtrace", status=None)
t.fail_test(find(t.stdout(), "No best alternative") == -1)
t.fail_test(string.find(t.stdout(), "No best alternative") == -1)
# Test that we can have alternative without sources
t.write("Jamfile", """
# Test that we can have alternative without sources.
t.write("jamfile.jam", """
alias specific-sources ;
import feature ;
feature.extend os : MAGIC ;
@@ -116,5 +110,4 @@ exe a : a.cpp specific-sources ;
t.rm("bin")
t.run_build_system()
t.cleanup()
t.cleanup()

View File

@@ -1,23 +1,22 @@
#!/usr/bin/python
# Copyright 2003 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)
# Copyright 2003 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)
# Regression test: when directory of project root contained regex metacharacters,
# Boost.Build failed to work. Bug reported by Michael Stevens
# Regression test: when directory of project root contained regex
# metacharacters, Boost.Build failed to work. Bug reported by Michael Stevens.
from BoostBuild import Tester, List
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
t.write("bad[abc]dirname/Jamfile", """
t.write("bad[abc]dirname/jamfile.jam", """
""")
t.write("bad[abc]dirname/project-root.jam", """
t.write("bad[abc]dirname/jamroot.jam", """
""")
t.run_build_system(subdir="bad[abc]dirname")
t.cleanup()

View File

@@ -1,16 +1,16 @@
#!/usr/bin/python
# Copyright 2004, 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)
# Copyright 2004, 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)
from BoostBuild import Tester, List
import BoostBuild
import string
# Create a temporary working directory
t = Tester()
t = BoostBuild.Tester()
t.set_tree("boostbook")
# For some reason, the messages are sent to stderr.
t.run_build_system()
t.fail_test(string.find(t.stdout(), """Writing boost/A.html for refentry(boost.A)

View File

@@ -1,55 +1,52 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003, 2005 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)
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003, 2005 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)
# Test that we can change build directory using
# the 'build-dir' project attribute.
# Test that we can change build directory using the 'build-dir' project
# attribute.
from BoostBuild import Tester
import BoostBuild
import string
import os
t = Tester()
t = BoostBuild.Tester()
# Test that top-level project can affect build dir
t.write("project-root.jam", "import gcc ; ")
t.write("Jamfile", """
project
: build-dir build
;
# Test that top-level project can affect build dir.
t.write("jamroot.jam", "import gcc ;")
t.write("jamfile.jam", """
project : build-dir build ;
exe a : a.cpp ;
build-project src ;
build-project src ;
""")
t.write("a.cpp", "int main() { return 0; }\n")
t.write("src/Jamfile", "exe b : b.cpp ; ")
t.write("src/b.cpp", "int main() { return 0; }\n")
t.write("a.cpp", "int main() {}\n")
t.write("src/jamfile.jam", "exe b : b.cpp ; ")
t.write("src/b.cpp", "int main() {}\n")
t.run_build_system()
t.expect_addition(["build/$toolset/debug/a.exe",
"build/src/$toolset/debug/b.exe"])
# Test that building from child projects work
# Test that building from child projects work.
t.run_build_system(subdir='src')
t.expect_nothing_more()
# Test that project can override build dir
t.write("Jamfile", """
t.expect_nothing_more()
# Test that project can override build dir.
t.write("jamfile.jam", """
exe a : a.cpp ;
build-project src ;
""")
""")
t.write("src/Jamfile", """
project
: build-dir build
;
exe b : b.cpp ;
t.write("src/jamfile.jam", """
project : build-dir build ;
exe b : b.cpp ;
""")
t.run_build_system()
@@ -58,46 +55,44 @@ t.expect_addition(["bin/$toolset/debug/a.exe",
# Now test the '--build-dir' option.
t.rm(".")
t.write("Jamroot", "")
t.write("jamroot.jam", "")
# Test that we get an error when no project id is specified.
t.run_build_system("--build-dir=foo")
t.fail_test(string.find(t.stdout(),
"warning: the --build-dir option will be ignored") == -1)
t.write("Jamroot", """
t.write("jamroot.jam", """
project foo ;
exe a : a.cpp ;
build-project sub ;
""")
t.write("a.cpp", "int main() { return 0; }\n")
t.write("sub/Jamfile", "exe b : b.cpp ;\n")
t.write("sub/b.cpp", "int main() { return 0; }\n")
t.write("a.cpp", "int main() {}\n")
t.write("sub/jamfile.jam", "exe b : b.cpp ;\n")
t.write("sub/b.cpp", "int main() {}\n")
t.run_build_system("--build-dir=build")
t.expect_addition(["build/foo/$toolset/debug/a.exe",
"build/foo/sub/$toolset/debug/b.exe"])
t.write("Jamroot", """
t.write("jamroot.jam", """
project foo : build-dir bin.v2 ;
exe a : a.cpp ;
build-project sub ;
""")
t.run_build_system("--build-dir=build")
t.expect_addition(["build/foo/bin.v2/$toolset/debug/a.exe",
t.expect_addition(["build/foo/bin.v2/$toolset/debug/a.exe",
"build/foo/bin.v2/sub/$toolset/debug/b.exe"])
# Try building in subdir. We expect that the entire build
# tree with be in 'sub/build'. Today, I'm not sure if
# this is what the user expects, but let it be.
# Try building in subdir. We expect that the entire build tree with be in
# 'sub/build'. Today, I am not sure if this is what the user expects, but let it
# be.
t.rm('build')
t.run_build_system("--build-dir=build", subdir="sub")
t.expect_addition(["sub/build/foo/bin.v2/sub/$toolset/debug/b.exe"])
t.write("Jamroot", """
t.write("jamroot.jam", """
project foo : build-dir %s ;
exe a : a.cpp ;
build-project sub ;
@@ -105,9 +100,6 @@ build-project sub ;
t.run_build_system("--build-dir=build", status=1)
t.fail_test(string.find(t.stdout(),
"Absolute directory specified via 'build-dir' project attribute") == -1)
"Absolute directory specified via 'build-dir' project attribute") == -1)
t.cleanup()
t.cleanup()

View File

@@ -22,18 +22,18 @@ import BoostBuild
def test_building_file_from_specific_project():
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
build-project sub ;
""")
t.write("hello.cpp", "int main() { return 0; }")
t.write("sub/Jamfile.jam", """
t.write("hello.cpp", "int main() {}")
t.write("sub/jamfile.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
exe sub : hello.cpp ;
""")
t.write("sub/hello.cpp", "int main() { return 0; }")
t.write("sub/hello.cpp", "int main() {}")
t.run_build_system("sub " + t.adjust_suffix("hello.obj"))
t.expect_output_line("*depends on itself*", False)
@@ -53,14 +53,14 @@ exe sub : hello.cpp ;
def test_building_file_from_specific_target():
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() { return 0; }")
t.write("hello2.cpp", "int main() { return 0; }")
t.write("hello3.cpp", "int main() { return 0; }")
t.write("hello1.cpp", "int main() {}")
t.write("hello2.cpp", "int main() {}")
t.write("hello3.cpp", "int main() {}")
t.run_build_system("hello1 " + t.adjust_suffix("hello1.obj"))
t.expect_addition("bin/$toolset/debug/hello1.obj")
@@ -79,17 +79,17 @@ exe hello3 : hello3.cpp ;
def test_building_missing_file_from_specific_target():
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() { return 0; }")
t.write("hello2.cpp", "int main() { return 0; }")
t.write("hello3.cpp", "int main() { return 0; }")
t.write("hello1.cpp", "int main() {}")
t.write("hello2.cpp", "int main() {}")
t.write("hello3.cpp", "int main() {}")
t.run_build_system("hello1 " + t.adjust_suffix("hello2.obj"), status=1)
t.expect_output_line("don't know how to make*hello2.obj")
t.expect_output_line("don't know how to make*" + t.adjust_suffix("hello2.obj"))
t.expect_nothing_more()
t.cleanup()
@@ -105,14 +105,14 @@ exe hello3 : hello3.cpp ;
def test_building_multiple_files_with_different_names():
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() { return 0; }")
t.write("hello2.cpp", "int main() { return 0; }")
t.write("hello3.cpp", "int main() { return 0; }")
t.write("hello1.cpp", "int main() {}")
t.write("hello2.cpp", "int main() {}")
t.write("hello3.cpp", "int main() {}")
t.run_build_system(
t.adjust_suffix("hello1.obj") + " " +
@@ -134,18 +134,18 @@ exe hello3 : hello3.cpp ;
def test_building_multiple_files_with_the_same_name():
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
build-project sub ;
""")
t.write("hello.cpp", "int main() { return 0; }")
t.write("sub/Jamfile.jam", """
t.write("hello.cpp", "int main() {}")
t.write("sub/jamfile.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
exe sub : hello.cpp ;
""")
t.write("sub/hello.cpp", "int main() { return 0; }")
t.write("sub/hello.cpp", "int main() {}")
t.run_build_system(t.adjust_suffix("hello.obj"))
t.expect_output_line("*depends on itself*", False)

View File

@@ -1,28 +1,22 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# 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)
# Copyright (C) Vladimir Prus 2006.
# 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)
# Tests that <build>no property prevents a target from being built.
from BoostBuild import Tester, List
import string
# Tests that <build>no property prevents a target from being built.
import BoostBuild
# Create a temporary working directory
t = Tester()
t = BoostBuild.Tester()
# Create the needed files
t.write("Jamroot", """
t.write("jamroot.jam", """
exe hello : hello.cpp : <variant>debug:<build>no ;
""")
t.write("hello.cpp", """
int main()
{
return 0;
}
t.write("hello.cpp", """
int main() {}
""")
t.run_build_system()
@@ -31,5 +25,4 @@ t.expect_nothing_more()
t.run_build_system("release")
t.expect_addition("bin/$toolset/release/hello.exe")
t.cleanup()

View File

@@ -1,26 +1,27 @@
#!/usr/bin/python
# Copyright 2003 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)
# Copyright 2003 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)
# Test that C files are compiled by C compiler
from BoostBuild import Tester, List
# Test that C files are compiled by a C compiler.
t = Tester()
import BoostBuild
t.write("project-root.jam", "")
t.write("Jamfile", """
t = BoostBuild.Tester()
t.write("jamroot.jam", """
project ;
exe hello : hello.cpp a.c ;
""")
t.write("hello.cpp", """
extern "C" int foo();
int main() { return foo(); }
""")
t.write("a.c", """
// This won't compile unless in C mode
// This will not compile unless in C mode.
int foo()
{
int new = 0;
@@ -28,6 +29,7 @@ int foo()
return new;
}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/hello.exe")

View File

@@ -5,38 +5,33 @@
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests that
# 1) the 'make' correctly assigns types to produced targets
# 2) than if 'make' create targets of type CPP, they are
# correctly used (there was a bug with it).
# This tests that :
# 1) the 'make' correctly assigns types to produced targets
# 2) if 'make' creates targets of type CPP, they are correctly used.
from BoostBuild import Tester
t = Tester()
import BoostBuild
# In order to correctly link this app, 'b.cpp', created by 'make'
# rule, should be compiled.
t.write("project-root.jam", "import gcc ;")
t.write("Jamfile", r'''
rule create ( dst : src * : properties * )
{
# hack to echo a space under NT
setup on $(dst) = "set x=int main(){ return 0; }" ;
}
t = BoostBuild.Tester()
# In order to correctly link this app, 'b.cpp', created by a 'make' rule, should
# be compiled.
t.write("jamroot.jam", "import gcc ;")
t.write("jamfile.jam", r'''
import modules ;
if [ modules.peek : NT ]
{
actions create
{
$(setup)
echo %x% > $(<)
echo int main() {} > $(<)
}
}
else
{
actions create
{
echo "int main(){ return 0; }" > $(<)
echo "int main() {}" > $(<)
}
}
@@ -44,13 +39,14 @@ IMPORT $(__name__) : create : : create ;
exe a : l dummy.cpp ;
# needs to be static lib for Windows - main cannot appear in DLL
# Needs to be static lib for Windows - main() cannot appear in DLL.
static-lib l : a.cpp b.cpp ;
make b.cpp : : create ;
''')
t.write("a.cpp", "")
t.write("dummy.cpp", "// msvc needs at least one object file\n")
t.run_build_system()

View File

@@ -5,21 +5,19 @@
# accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from BoostBuild import Tester, List
import string
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
t.write("a.cpp", """
t.write("a.cpp", """
int main() {}
""")
t.write("Jamroot", """
exe a : a.cpp sub1//sub1 sub2//sub2 sub3//sub3 ;
t.write("jamroot.jam", """
exe a : a.cpp sub1//sub1 sub2//sub2 sub3//sub3 ;
""")
t.write("sub1/Jamfile", """
t.write("sub1/jamfile.jam", """
lib sub1 : sub1.cpp sub1_2 ../sub2//sub2 ;
lib sub1_2 : sub1_2.cpp ;
""")
@@ -29,7 +27,6 @@ t.write("sub1/sub1.cpp", """
__declspec(dllexport)
#endif
void sub1() {}
""")
t.write("sub1/sub1_2.cpp", """
@@ -37,12 +34,10 @@ t.write("sub1/sub1_2.cpp", """
__declspec(dllexport)
#endif
void sub1() {}
""")
t.write("sub2/Jamfile", """
lib sub2 : sub2.cpp ;
t.write("sub2/jamfile.jam", """
lib sub2 : sub2.cpp ;
""")
t.write("sub2/sub2.cpp", """
@@ -50,11 +45,10 @@ t.write("sub2/sub2.cpp", """
__declspec(dllexport)
#endif
void sub2() {}
""")
t.write("sub3/Jamroot", """
lib sub3 : sub3.cpp ;
t.write("sub3/jamroot.jam", """
lib sub3 : sub3.cpp ;
""")
t.write("sub3/sub3.cpp", """
@@ -62,11 +56,9 @@ t.write("sub3/sub3.cpp", """
__declspec(dllexport)
#endif
void sub3() {}
""")
# The 'clean' should not remove files under separate Jamroot.
# The 'clean' should not remove files under separate jamroot.jam.
t.run_build_system()
t.run_build_system("--clean")
t.expect_removal("bin/$toolset/debug/a.obj")
@@ -77,15 +69,14 @@ t.expect_nothing("sub3/bin/$toolset/debug/sub3.obj")
# The 'clean-all' removes everything it can reach.
t.run_build_system()
t.run_build_system("--clean")
t.run_build_system("--clean-all")
t.expect_removal("bin/$toolset/debug/a.obj")
t.expect_removal("sub1/bin/$toolset/debug/sub1.obj")
t.expect_removal("sub1/bin/$toolset/debug/sub1_2.obj")
t.expect_removal("sub2/bin/$toolset/debug/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug/sub3.obj")
# The 'clean' together with project target removes
# only under that probject
# The 'clean' together with project target removes only under that project.
t.run_build_system()
t.run_build_system("sub1 --clean")
t.expect_nothing("bin/$toolset/debug/a.obj")
@@ -94,7 +85,7 @@ t.expect_removal("sub1/bin/$toolset/debug/sub1_2.obj")
t.expect_nothing("sub2/bin/$toolset/debug/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug/sub3.obj")
# And clean-all removes everything.
# And 'clean-all' removes everything.
t.run_build_system()
t.run_build_system("sub1 --clean-all")
t.expect_nothing("bin/$toolset/debug/a.obj")
@@ -103,9 +94,8 @@ t.expect_removal("sub1/bin/$toolset/debug/sub1_2.obj")
t.expect_removal("sub2/bin/$toolset/debug/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug/sub3.obj")
# If main target is explicitly named, we should not remove
# files from other targets.
# If main target is explicitly named, we should not remove files from other
# targets.
t.run_build_system()
t.run_build_system("sub1//sub1 --clean")
t.expect_removal("sub1/bin/$toolset/debug/sub1.obj")
@@ -113,18 +103,14 @@ t.expect_nothing("sub1/bin/$toolset/debug/sub1_2.obj")
t.expect_nothing("sub2/bin/$toolset/debug/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug/sub3.obj")
# Regression test: sources of the 'cast' rule were mistakenly
# deleted.
# Regression test: sources of the 'cast' rule were mistakenly deleted.
t.rm(".")
t.write("Jamroot", """
t.write("jamroot.jam", """
import cast ;
cast a cpp : a.h ;
""")
t.write("a.h", "")
t.run_build_system("--clean")
t.expect_nothing("a.h")
t.cleanup()

View File

@@ -4,26 +4,22 @@
# 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)
# Test that composite properties are handled correctly.
from BoostBuild import Tester, List
# Test that composite properties are handled correctly.
t = Tester()
import BoostBuild
t.write("project-root.jam", "")
t.write("Jamfile", """
t = BoostBuild.Tester()
t.write("jamroot.jam", """
exe hello : hello.cpp : <variant>release ;
""")
t.write("hello.cpp", """
int main()
{
return 0;
}
t.write("hello.cpp", """
int main() {}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/release/hello.exe")
t.cleanup()

View File

@@ -15,18 +15,18 @@ t = BoostBuild.Tester()
# define.
t.write("a.cpp", """
#ifdef STATIC
int main() { return 0; }
int main() {}
#endif
""")
# Test conditionals in target requirements.
t.write("Jamroot.jam", "exe a : a.cpp : <link>static:<define>STATIC ;")
t.write("jamroot.jam", "exe a : a.cpp : <link>static:<define>STATIC ;")
t.run_build_system("link=static")
t.expect_addition("bin/$toolset/debug/link-static/a.exe")
t.rm("bin")
# Test conditionals in project requirements.
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
project : requirements <link>static:<define>STATIC ;
exe a : a.cpp ;
""")
@@ -36,7 +36,7 @@ t.rm("bin")
# Regression test for a bug found by Ali Azarbayejani. Conditionals inside usage
# requirement were not being evaluated.
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
lib l : l.cpp : : : <link>static:<define>STATIC ;
exe a : a.cpp l ;
""")

View File

@@ -4,9 +4,9 @@
# 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)
# Regression test: it was possible that due to evaluation of conditional
# requirements, two different values of non-free features were present in
# property set.
# Regression test: it was possible that due to evaluation of conditional
# requirements, two different values of non-free features were present in a
# property set.
import BoostBuild
@@ -14,7 +14,7 @@ t = BoostBuild.Tester()
t.write("a.cpp", "")
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
import feature ;
import common ;

View File

@@ -4,23 +4,23 @@
# 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)
# Test that conditional properties work, even if property is free, and value
# includes a colon.
# Test that conditional properties work, even if property is free, and value
# includes a colon.
import BoostBuild
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
exe hello : hello.cpp : <variant>debug:<define>CLASS=Foo::Bar ;
""")
t.write("hello.cpp", """
namespace Foo { class Bar { } ; }
int main()
{
CLASS c;
c; // Disables the unused variable warning.
return 0;
}
""")

View File

@@ -21,7 +21,8 @@ def test_multiple_conditions():
"""Basic tests for properties conditioned on multiple other properties.
"""
t = BoostBuild.Tester("--ignore-regular-config toolset=testToolset", pass_toolset=False, use_test_config=False)
t = BoostBuild.Tester("--ignore-regular-config toolset=testToolset",
pass_toolset=False, use_test_config=False)
t.write("testToolset.jam", """
import feature ;
@@ -29,7 +30,7 @@ feature.extend toolset : testToolset ;
rule init ( ) { }
""")
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
import feature ;
import notfile ;
import toolset ;
@@ -134,7 +135,7 @@ feature.subfeature toolset %(toolset)s : version : 0 1 ;
rule init ( version ? ) { }
""" % {"toolset": toolset})
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
import feature ;
import notfile ;
import toolset ;

View File

@@ -23,7 +23,8 @@ def test_user_configuration():
path handling is tested.
"""
t = BoostBuild.Tester("--debug-configuration", pass_toolset=False, use_test_config=False)
t = BoostBuild.Tester("--debug-configuration", pass_toolset=False,
use_test_config=False)
implicitConfigLoadMessage = "notice: Loading user-config configuration file: *"
explicitConfigLoadMessage = "notice: Loading explicitly specified user configuration file:"
@@ -46,7 +47,7 @@ import feature ;
feature.extend toolset : %s ;
rule init ( ) { }
""" % toolsetName )
t.write("Jamroot.jam", "using %s ;" % toolsetName)
t.write("jamroot.jam", "using %s ;" % toolsetName)
t.run_build_system()
t.expect_output_line(explicitConfigLoadMessage, False)

View File

@@ -1,8 +1,8 @@
#!/usr/bin/python
# Copyright 2002, 2003 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)
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests correct handling of "-d1" and "-d2" options.
@@ -11,14 +11,9 @@ import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """
actions a {
}
actions quietly b {
}
actions a { }
actions quietly b { }
ALWAYS all ;
a all ;
b all ;
""")

View File

@@ -1,8 +1,8 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# 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)
# Copyright 2003 Dave Abrahams
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests the facilities for deleting modules.
@@ -28,12 +28,12 @@ module foo
{
EXIT DELETE_MODULE failed to kill foo's variables ;
}
rule bar { }
var = x y ;
DELETE_MODULE foo ;
if $(var)
{
EXIT internal DELETE_MODULE failed to kill foo's variables ;

View File

@@ -4,11 +4,12 @@
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests correct handling of dependencies, specifically, on
# generated sources, and from generated sources.
# This tests correct handling of dependencies, specifically, on generated
# sources, and from generated sources.
import BoostBuild
from string import find
import string
t = BoostBuild.Tester(pass_toolset=0)
@@ -37,20 +38,22 @@ HDRRULE on b foo.h bar.h = hdrrule ;
HDRSCAN on b foo.h bar.h = \"#include <(.*)>\" ;
"""
# This creates 'a' which depends on 'b', which is generated.
# The generated 'b' contains '#include <foo.h>' and no rules for
# foo.h are given. The system should error out on the first invocation.
# This creates 'a' which depends on 'b', which is generated. The generated 'b'
# contains '#include <foo.h>' and no rules for foo.h are given. The system
# should error out on the first invocation.
t.run_build_system("-f-", stdin=code)
t.fail_test(find(t.stdout(), "...skipped a for lack of foo.h...") == -1)
t.fail_test(string.find(t.stdout(), "...skipped a for lack of foo.h...") == -1)
t.rm('b')
# Now test that if target 'c' also depends on 'b', then it won't be built, as well.
# Now test that if target 'c' also depends on 'b', then it will not be built, as
# well.
t.run_build_system("-f-", stdin=code + " copy c : b ; DEPENDS c : b ; DEPENDS all : c ; ")
t.fail_test(find(t.stdout(), "...skipped c for lack of foo.h...") == -1)
t.fail_test(string.find(t.stdout(), "...skipped c for lack of foo.h...") == -1)
# Now add a rule for creating foo.h
t.rm('b')
# Now add a rule for creating foo.h.
code += """
actions create-foo
{
@@ -60,36 +63,35 @@ create-foo foo.h ;
"""
t.run_build_system("-f-", stdin=code)
# Run two times, adding explicit dependency from all to foo.h at
# the beginning and at the end, to make sure that foo.h is generated before
# 'a' in all cases.
# Run two times, adding explicit dependency from all to foo.h at the beginning
# and at the end, to make sure that foo.h is generated before 'a' in all cases.
def mk_right_order_func(s1, s2):
def right_order(s):
n1 = find(s, s1)
n2 = find(s, s2)
return n1 != -1 and n2 != -1 and n1 < n2
return right_order
def mk_correct_order_func(s1, s2):
def correct_order(s):
n1 = string.find(s, s1)
n2 = string.find(s, s2)
return ( n1 != -1 ) and ( n2 != -1 ) and ( n1 < n2 )
return correct_order
right_order = mk_right_order_func("create-foo", "copy a")
correct_order = mk_correct_order_func("create-foo", "copy a")
t.rm(["a", "b", "foo.h"])
t.run_build_system("-d+2 -f-", stdin=code + " DEPENDS all : foo.h ;")
t.fail_test(not right_order(t.stdout()))
t.fail_test(not correct_order(t.stdout()))
t.rm(["a", "b", "foo.h"])
t.run_build_system("-d+2 -f-", stdin=" DEPENDS all : foo.h ; " + code)
t.fail_test(not right_order(t.stdout()))
t.fail_test(not correct_order(t.stdout()))
# Now foo.h exists. Test include from b -> foo.h -> bar.h -> biz.h
# b and foo.h already have updating actions.
# Now foo.h exists. Test include from b -> foo.h -> bar.h -> biz.h. b and foo.h
# already have updating actions.
t.rm(["a", "b"])
t.write("foo.h", "#include <bar.h>")
t.write("bar.h", "#include <biz.h>")
t.run_build_system("-d+2 -f-", stdin=code)
t.fail_test(find(t.stdout(), "...skipped a for lack of biz.h...") == -1)
t.fail_test(string.find(t.stdout(), "...skipped a for lack of biz.h...") == -1)
# Add an action for biz.h
# Add an action for biz.h.
code += """
actions create-biz
{
@@ -97,15 +99,15 @@ actions create-biz
}
create-biz biz.h ;
"""
t.rm(["b"])
right_order = mk_right_order_func("create-biz", "copy a")
correct_order = mk_correct_order_func("create-biz", "copy a")
t.run_build_system("-d+2 -f-", stdin=code + " DEPENDS all : biz.h ;")
t.fail_test(not right_order(t.stdout()))
t.fail_test(not correct_order(t.stdout()))
t.rm(["a", "biz.h"])
t.run_build_system("-d+2 -f-", stdin=" DEPENDS all : biz.h ; " + code)
t.fail_test(not right_order(t.stdout()))
t.fail_test(not correct_order(t.stdout()))
t.write("a", "")
@@ -147,10 +149,9 @@ rule hdrrule
INCLUDES $(1) : d ;
}
"""
right_order = mk_right_order_func("create-d", "copy main")
correct_order = mk_correct_order_func("create-d", "copy main")
t.run_build_system("-d2 -f-", stdin=code)
t.fail_test(not right_order(t.stdout()))
t.fail_test(not correct_order(t.stdout()))
t.cleanup()

View File

@@ -1,24 +1,23 @@
#!/usr/bin/python
# Copyright 2003 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)
# Copyright 2003 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)
from BoostBuild import Tester, List
import BoostBuild
t = Tester(pass_toolset=0)
# Test that
t = BoostBuild.Tester(pass_toolset=0)
t.write("code", """
module a {
module a
{
rule r1 ( )
{
ECHO R1 ;
}
}
module a2 {
module a2
{
rule r2 ( )
{
ECHO R2 ;
@@ -26,9 +25,9 @@ module a2 {
}
IMPORT a2 : r2 : : a2.r2 ;
module b {
module b
{
IMPORT_MODULE a : b ;
rule test
{
# Call rule visible via IMPORT_MODULE
@@ -41,19 +40,19 @@ module b {
IMPORT b : test : : test ;
test ;
module c {
module c
{
rule test
{
ECHO CTEST ;
}
}
}
IMPORT_MODULE c : ;
c.test ;
actions do-nothing { }
do-nothing all ;
""")
t.run_build_system("-fcode", stdout="""R1

View File

@@ -1,18 +1,17 @@
#!/usr/bin/python
# Copyright 2003 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)
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests the "existing" and "updated" modifiers on actions.
import BoostBuild
from string import strip, replace
import string
t = BoostBuild.Tester(pass_toolset=0)
code = """
DEPENDS all : a ;
ALWAYS a ;
NOTFILE a ;
@@ -36,25 +35,17 @@ t.write("file.jam", code)
t.write("a-1", "")
t.run_build_system("-ffile.jam")
t.fail_test(strip(t.read("list")) != "a-1")
t.fail_test(string.strip(t.read("list")) != "a-1")
t.rm(["a-3", "list"])
code = replace(code, "existing", "updated")
code = string.replace(code, "existing", "updated")
t.write("file.jam", code)
t.run_build_system("-ffile.jam")
t.fail_test(strip(t.read("list")) != "a-3")
t.fail_test(string.strip(t.read("list")) != "a-3")
code = replace(code, "updated", "existing updated")
code = string.replace(code, "updated", "existing updated")
t.write("file.jam", code)
t.run_build_system("-ffile.jam")
t.fail_test(strip(t.read("list")) != "a-1 a-3")
t.fail_test(string.strip(t.read("list")) != "a-1 a-3")
t.cleanup()

View File

@@ -1,10 +1,10 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# 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)
# Copyright 2003 Dave Abrahams
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# This tests the core rule for enumerating the variable names in a module
# This tests the core rule for enumerating the variable names in a module.
import BoostBuild
@@ -34,4 +34,5 @@ NOTFILE xx ;
""")
t.run_build_system("-ffile.jam", status=0)
t.cleanup()

View File

@@ -4,19 +4,17 @@
# 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)
# Attempt to declare a generator for creating OBJ from RC files. That generator
# should be considered together with standard CPP->OBJ generators and
# successfully create the target. Since we do not have a RC compiler everywhere,
# we fake the action. The resulting OBJ will be unusable, but it must be
# created.
from BoostBuild import Tester, List
import BoostBuild
t = BoostBuild.Tester()
t = Tester()
# Attempt to declare a generator for creating OBJ from RC files.
# That generator should be considered together with standard
# CPP->OBJ generators and successfully create the target.
# Since we don't have RC compiler everywhere, we fake the action.
# The resulting OBJ will be unusable, but it must be created.
t.write("project-root.jam", """
t.write("jamroot.jam", """
import rcc ;
""")
@@ -25,8 +23,8 @@ import type ;
import generators ;
import print ;
# Use 'RCC' to avoid conflicts with definitions in
# the standard rc.jam and msvc.jam
# Use 'RCC' to avoid conflicts with definitions in the standard rc.jam and
# msvc.jam
type.register RCC : rcc ;
rule resource-compile ( targets * : sources * : properties * )
@@ -36,10 +34,9 @@ rule resource-compile ( targets * : sources * : properties * )
}
generators.register-standard rcc.resource-compile : RCC : OBJ ;
""")
t.write("Jamfile", """
t.write("jamfile.jam", """
obj r : r.rcc ;
""")
@@ -50,6 +47,3 @@ t.run_build_system()
t.expect_content("bin/$toolset/debug/r.obj", "rc-object")
t.cleanup()

View File

@@ -1,38 +1,38 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003 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)
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003 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)
# Test that default build clause actually has any effect.
from BoostBuild import Tester, List
t = Tester()
import BoostBuild
t.write("project-root.jam", "import gcc ;")
t.write("Jamfile", "exe a : a.cpp : : debug release ;")
t.write("a.cpp", "int main() { return 0; }\n")
t = BoostBuild.Tester()
t.write("jamroot.jam", "import gcc ;")
t.write("jamfile.jam", "exe a : a.cpp : : debug release ;")
t.write("a.cpp", "int main() {}\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/a.exe")
t.expect_addition("bin/$toolset/release/a.exe")
# Check that explictly-specified build variant supresses
# default-build
# Check that explictly-specified build variant supresses default-build.
t.rm("bin")
t.run_build_system("release")
t.expect_addition(List("bin/$toolset/release/") * "a.exe a.obj")
t.expect_addition(BoostBuild.List("bin/$toolset/release/") * "a.exe a.obj")
t.expect_nothing_more()
# Now check that we can specify explicit build request and
# default-build will be combined with it
# Now check that we can specify explicit build request and default-build will be
# combined with it.
t.run_build_system("optimization=space")
t.expect_addition("bin/$toolset/debug/optimization-space/a.exe")
t.expect_addition("bin/$toolset/release/optimization-space/a.exe")
# Test that default-build must be identical in all alternatives. Error case.
t.write("Jamfile", """
t.write("jamfile.jam", """
exe a : a.cpp : : debug ;
exe a : b.cpp : : ;
""")
@@ -44,40 +44,40 @@ differing from previous default build <variant>debug
"""
t.run_build_system("-n --no-error-backtrace", status=1, stdout=expected)
# Test that default-build must be identical in all alternatives. No Error case, empty default build.
t.write("Jamfile", """
# Test that default-build must be identical in all alternatives. No Error case,
# empty default build.
t.write("jamfile.jam", """
exe a : a.cpp : <variant>debug ;
exe a : b.cpp : <variant>release ;
""")
t.run_build_system("-n --no-error-backtrace", status=0)
# Now try a harder example: default build which contains <define>
# should cause <define> to be present when "b" is compiled.
# This happens only of "build-project b" is placed first.
t.write("Jamfile", """
project
: default-build <define>FOO
;
build-project a ;
build-project b ;
# Now try a harder example: default build which contains <define> should cause
# <define> to be present when "b" is compiled. This happens only if
# "build-project b" is placed first.
t.write("jamfile.jam", """
project : default-build <define>FOO ;
build-project a ;
build-project b ;
""")
t.write("a/Jamfile", """
exe a : a.cpp ../b//b ;
t.write("a/jamfile.jam", """
exe a : a.cpp ../b//b ;
""")
t.write("a/a.cpp", """
#ifdef _WIN32
__declspec(dllimport)
#endif
void foo();
int main() { foo(); return 0; }
int main() { foo(); }
""")
t.write("b/Jamfile", """
lib b : b.cpp ;
t.write("b/jamfile.jam", """
lib b : b.cpp ;
""")
t.write("b/b.cpp", """
#ifdef FOO
#ifdef _WIN32

View File

@@ -1,44 +1,39 @@
#!/usr/bin/python
# Copyright 2003 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)
# Copyright 2003 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)
# Test that features with default values are always present
# in build properties of any target.
# Test that features with default values are always present in build properties
# of any target.
from BoostBuild import Tester, List
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
# Declare *non-propagated* feature foo.
t.write("project-root.jam", """
t.write("jamroot.jam", """
import feature : feature ;
feature foo : on off ;
""")
# Note that '<foo>on' won't be propagated
# to 'd/l'.
t.write("Jamfile", """
# Note that '<foo>on' will not be propagated to 'd/l'.
t.write("jamfile.jam", """
exe hello : hello.cpp d//l ;
""")
t.write("hello.cpp", """
#ifdef _WIN32
__declspec(dllimport)
#endif
void foo();
int main()
{
foo();
return 1;
}
int main() { foo(); }
""")
t.write("d/Jamfile", """
t.write("d/jamfile.jam", """
lib l : l.cpp : <foo>on:<define>FOO ;
""")
t.write("d/l.cpp", """
#ifdef _WIN32
__declspec(dllexport)
@@ -46,7 +41,6 @@ __declspec(dllexport)
#ifdef FOO
void foo() {}
#endif
""")
t.run_build_system()

View File

@@ -58,7 +58,7 @@ rule init ( version ) { ECHO "%(message_initialized)s" ; }
'toolset_version_unused': toolset_version_unused})
# Main Boost Build project script.
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
import build-system ;
import errors ;
import feature ;
@@ -121,7 +121,7 @@ def test_default_toolset_on_os( os, expected_toolset ):
t = BoostBuild.Tester("--user-config= --ignore-site-config",
pass_toolset=False, use_test_config=False)
t.write("Jamroot.jam", "modules.poke os : .name : %s ;" % os)
t.write("jamroot.jam", "modules.poke os : .name : %s ;" % os)
# We need to tell the test system to ignore stderr output as attempting to
# load missing toolsets might cause random failures with which we are not
@@ -159,7 +159,7 @@ rule init ( ) { }
""" % {'toolset_name': toolset_name})
# Main Boost Build project script.
t.write("Jamroot.jam", """
t.write("jamroot.jam", """
import build-system ;
import errors ;
import feature ;

View File

@@ -4,30 +4,25 @@
# 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)
# Regression test: virtual targets with different dependency properties
# were considered different by 'virtual-target.register', but the code
# which determined target paths ignored dependency properties --- so both
# targets used to be placed to the same location.
# Regression test: virtual targets with different dependency properties were
# considered different by 'virtual-target.register', but the code which
# determined target paths ignored dependency properties --- so both targets used
# to be placed to the same location.
from BoostBuild import Tester, List
from string import find
import BoostBuild
import string
t = Tester()
t = BoostBuild.Tester()
t.write("project-root.jam", "")
t.write("Jamfile", """
t.write("jamroot.jam", """
lib foo : foo.cpp ;
exe hello : hello.cpp ;
exe hello2 : hello.cpp : <library>foo ;
""")
t.write("hello.cpp", """
int main()
{
return 0;
}
""")
t.write("hello.cpp", "int main() {}\n")
t.write("foo.cpp", """
#ifdef _WIN32
__declspec(dllexport)
@@ -36,6 +31,6 @@ void foo() {}
""")
t.run_build_system("--no-error-backtrace", status=1)
t.fail_test(find(t.stdout(), "Duplicate name of actual target") == -1)
t.fail_test(string.find(t.stdout(), "Duplicate name of actual target") == -1)
t.cleanup()

View File

@@ -5,19 +5,20 @@
# 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)
from BoostBuild import Tester, List
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
t.set_tree("dependency-test")
t.run_build_system()
# Check that main target 'c' was able to find 'x.h' from
# 'a's dependency graph
# Check that main target 'c' was able to find 'x.h' from 'a's dependency graph.
t.expect_addition("bin/$toolset/debug/c.exe")
# Check handling of first level includes.
# Both 'a' and 'b' include "a.h" and should be updated
# Both 'a' and 'b' include "a.h" and should be updated.
t.touch("a.h")
t.run_build_system()
@@ -26,14 +27,13 @@ t.expect_touch("bin/$toolset/debug/a.obj")
t.expect_touch("bin/$toolset/debug/a_c.obj")
t.expect_touch("bin/$toolset/debug/b.exe")
t.expect_touch("bin/$toolset/debug/b.obj")
# Now, <dependency> does not add dependency.
# It sound weird, but is intentional. Need
# to rename <dependency> eventually.
# Now, <dependency> does not add a dependency. It sound weird, but is
# intentional. Need to rename <dependency> eventually.
#t.expect_touch("bin/$toolset/debug/main-target-c/c.exe")
t.ignore("*.tds")
t.expect_nothing_more()
# Only 'a' include <a.h> and should be updated
# Only 'a' include <a.h> and should be updated.
t.touch("src1/a.h")
t.run_build_system()
@@ -43,7 +43,7 @@ t.expect_touch("bin/$toolset/debug/a_c.obj")
t.ignore("*.tds")
t.expect_nothing_more()
# "src/a.h" includes "b.h" (in the same dir)
# "src/a.h" includes "b.h" (in the same dir).
t.touch("src1/b.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug/a.exe")
@@ -52,8 +52,8 @@ t.expect_touch("bin/$toolset/debug/a_c.obj")
t.ignore("*.tds")
t.expect_nothing_more()
# included by "src/b.h". We had a bug: file included via "",
# like "b.h" is in this case was not scanned at all.
# Included by "src/b.h". We had a bug: file included via "", like "b.h" is in
# this case was not scanned at all.
t.touch("src1/c.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug/a.exe")
@@ -62,41 +62,38 @@ t.touch("b.h")
t.run_build_system()
t.expect_nothing_more()
# Test dependency on generated header.
# TODO: we have also to check that generated header is found correctly
# if it is different for different subvariants. Lacking any toolset
# support, this check will be implemented later.
# Test dependency on a generated header.
#
# TODO: we have also to check that generated header is found correctly if it is
# different for different subvariants. Lacking any toolset support, this check
# will be implemented later.
t.touch("x.foo")
t.run_build_system()
t.expect_touch("bin/$toolset/debug/a.obj")
t.expect_touch("bin/$toolset/debug/a_c.obj")
# Check that generated headers are scanned for dependencies as well
# Check that generated headers are scanned for dependencies as well.
t.touch("src1/z.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug/a.obj")
t.expect_touch("bin/$toolset/debug/a_c.obj")
# Regression test: on windows, <includes> with absolute paths
# were not considered when scanning dependencies.
# Regression test: on Windows, <includes> with absolute paths were not
# considered when scanning dependencies.
t.rm(".")
t.write("Jamroot", """
t.write("jamroot.jam", """
path-constant TOP : . ;
exe app : main.cpp
: <include>$(TOP)/include
;
exe app : main.cpp : <include>$(TOP)/include ;
""");
t.write("main.cpp", """
#include <dir/header.h>
int main() { return 0; }
int main() {}
""")
t.write("include/dir/header.h", "")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/main.obj")
@@ -104,6 +101,4 @@ t.touch("include/dir/header.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug/main.obj")
t.cleanup()

View File

@@ -1,46 +1,40 @@
#!/usr/bin/python
from BoostBuild import Tester, List
import os
from string import strip
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
# First check some startup
# First check some startup.
t.set_tree("direct-request-test")
t.run_build_system(extra_args="define=MACROS")
t.expect_addition("bin/$toolset/debug/"
* (List("a.obj b.obj b.dll a.exe")))
* (BoostBuild.List("a.obj b.obj b.dll a.exe")))
# When building debug version, the 'define' still applies
# When building a debug version, the 'define' still applies.
t.rm("bin")
t.run_build_system(extra_args="debug define=MACROS")
t.expect_addition("bin/$toolset/debug/"
* (List("a.obj b.obj b.dll a.exe")))
* (BoostBuild.List("a.obj b.obj b.dll a.exe")))
# When building release version, the 'define' should not
# apply: we'll have direct build request 'release <define>MACROS'
# and real build properties 'debug'.
t.copy("Jamfile2", "Jamfile")
# When building release version, the 'define' should not apply: we will have
# direct build request 'release <define>MACROS' and a real build property
# 'debug'.
t.copy("jamfile2.jam", "jamfile.jam")
t.copy("b_inverse.cpp", "b.cpp")
t.rm("bin")
t.run_build_system(extra_args="release define=MACROS")
# Regression test: direct build request was not working
# when there's more than one level of 'build-project'
# Regression test: direct build request was not working when there was more than
# one level of 'build-project'.
t.rm(".")
t.write('project-root.jam', '')
t.write('Jamfile', 'build-project a ;')
t.write('a/Jamfile', 'build-project b ;')
t.write('a/b/Jamfile', '')
t.write('jamroot.jam', '')
t.write('jamfile.jam', 'build-project a ;')
t.write('a/jamfile.jam', 'build-project b ;')
t.write('a/b/jamfile.jam', '')
t.run_build_system("release")
t.cleanup()

View File

@@ -1,30 +1,26 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# 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)
# Copyright (C) Vladimir Prus 2006.
# 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)
# Test that it's possible to add a suffix to a main target name to
# disambiguate that main target from another, and that this does not
# affect the names of the generated targets.
from BoostBuild import Tester, List
# Test that it is possible to add a suffix to a main target name to disambiguate
# that main target from another, and that this does not affect the names of the
# generated targets.
# Create a temporary working directory
t = Tester()
import BoostBuild
# Create the needed files
t.write("Jamroot", """
t = BoostBuild.Tester()
t.write("jamroot.jam", """
exe hello.exe : hello.obj ;
obj hello.obj : hello.cpp : <variant>debug ;
obj hello.obj2 : hello.cpp : <variant>release ;
""")
t.write("hello.cpp", """
int main()
{
return 0;
}
t.write("hello.cpp", """
int main() {}
""")
t.run_build_system()
@@ -33,5 +29,4 @@ t.expect_addition("bin/$toolset/debug/hello.exe")
t.expect_addition("bin/$toolset/debug/hello.obj")
t.expect_addition("bin/$toolset/release/hello.obj")
t.cleanup()

View File

@@ -1,41 +1,38 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2003. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
# Copyright (C) Vladimir Prus 2003. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
# Test that the <dll-path> property is correctly set when using
# <hardcode-dll-paths>true.
from BoostBuild import Tester, List
from string import find
# Test that the <dll-path> property is correctly set when using
# <hardcode-dll-paths>true.
import BoostBuild
t = Tester()
t = BoostBuild.Tester()
# The point of this test is to have exe "main" which uses library "b",
# which uses library "a". When "main" is built with <hardcode-dll-paths>true,
# paths to both libraries should be present as values of <dll-path> feature.
# We create a special target type which reports <dll-path> values on its sources
# and compare the list of found values with out expectations.
# The point of this test is to have exe "main" which uses library "b", which
# uses library "a". When "main" is built with <hardcode-dll-paths>true, paths to
# both libraries should be present as values of <dll-path> feature. We create a
# special target type which reports <dll-path> values on its sources and compare
# the list of found values with out expectations.
t.write("Jamfile", """
t.write("jamfile.jam", """
exe main : main.cpp b//b ;
explicit main ;
path-list mp : main ;
path-list mp : main ;
""")
t.write("main.cpp", """
int main() { return 0; }
t.write("main.cpp", """
int main() {}
""")
t.write("project-root.jam", """
using dll-paths ;
t.write("jamroot.jam", """
using dll-paths ;
""")
t.write("dll-paths.jam", """
t.write("dll-paths.jam", """
import type ;
import generators ;
import feature ;
@@ -43,18 +40,18 @@ import sequence ;
import print ;
import "class" : new ;
rule init ( )
{
rule init ( )
{
type.register PATH_LIST : pathlist ;
class dll-paths-list-generator : generator
class dll-paths-list-generator : generator
{
rule __init__ ( )
{
generator.__init__ dll-paths.list : EXE : PATH_LIST ;
}
rule generated-targets ( sources + : property-set : project name ? )
rule generated-targets ( sources + : property-set : project name ? )
{
local dll-paths ;
for local s in $(sources)
@@ -64,15 +61,15 @@ rule init ( )
{
local p = [ $(a).properties ] ;
dll-paths += [ $(p).get <dll-path> ] ;
}
}
}
return [ generator.generated-targets $(sources)
: [ $(property-set).add-raw $(dll-paths:G=<dll-path>) ] : $(project) $(name) ] ;
return [ generator.generated-targets $(sources) :
[ $(property-set).add-raw $(dll-paths:G=<dll-path>) ] :
$(project) $(name) ] ;
}
}
generators.register [ new dll-paths-list-generator ] ;
}
rule list ( target : sources * : properties * )
@@ -82,35 +79,30 @@ rule list ( target : sources * : properties * )
print.output $(target) ;
print.text $(paths) ;
}
""")
t.write("a/a.cpp", """
t.write("a/a.cpp", """
void
#if defined(_WIN32)
__declspec(dllexport)
#endif
foo() {}
""")
t.write("a/Jamfile", """
lib a : a.cpp ;
t.write("a/jamfile.jam", """
lib a : a.cpp ;
""")
t.write("b/b.cpp", """
t.write("b/b.cpp", """
void
#if defined(_WIN32)
__declspec(dllexport)
#endif
bar() {}
""")
t.write("b/Jamfile", """
lib b : b.cpp ../a//a ;
t.write("b/jamfile.jam", """
lib b : b.cpp ../a//a ;
""")
t.run_build_system("hardcode-dll-paths=true")
@@ -124,4 +116,3 @@ t.expect_content_line("bin/$toolset/debug/mp.pathlist", "*" + es1);
t.expect_content_line("bin/$toolset/debug/mp.pathlist", "*" + es2);
t.cleanup()

Some files were not shown because too many files have changed in this diff Show More