early-access version 3088

This commit is contained in:
pineappleEA
2022-11-05 15:35:56 +01:00
parent 4e4fc25ce3
commit b601909c6d
35519 changed files with 5996896 additions and 860 deletions

View File

@@ -0,0 +1,5 @@
# Copyright 2003 Dave Abrahams
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
boost-build . ;

View File

@@ -0,0 +1,265 @@
# Copyright 2003 Dave Abrahams
# Copyright 2003, 2005, 2006 Rene Rivera
# Copyright 2003, 2005, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# First of all, check the jam version.
if $(JAM_VERSION:J="") < 030112
{
ECHO "error: Boost.Jam version 3.1.12 or later required" ;
EXIT ;
}
local required-rules = GLOB-RECURSIVELY HAS_NATIVE_RULE ;
for local r in $(required-rules)
{
if ! $(r) in [ RULENAMES ]
{
ECHO "error: builtin rule '$(r)' is not present" ;
ECHO "error: your version of bjam is likely out of date" ;
ECHO "error: please get a fresh version from SVN." ;
EXIT ;
}
}
local native = regex transform 2 ;
while $(native)
{
if ! [ HAS_NATIVE_RULE $(native[1]) : $(native[2]) : $(native[3]) ]
{
ECHO "error: missing native rule '$(native[1]).$(native[2])'" ;
ECHO "error: or interface version of that rule is too low" ;
ECHO "error: your version of bjam is likely out of date" ;
ECHO "error: please get a fresh version from SVN." ;
EXIT ;
}
native = $(native[4-]) ;
}
# Check that the builtin .ENVIRON module is present. We do not have a builtin to
# check that a module is present, so we assume that the PATH environment
# variable is always set and verify that the .ENVIRON module has a non-empty
# value of that variable.
module .ENVIRON
{
local p = $(PATH) $(Path) $(path) ;
if ! $(p)
{
ECHO "error: no builtin module .ENVIRON is found" ;
ECHO "error: your version of bjam is likely out of date" ;
ECHO "error: please get a fresh version from SVN." ;
EXIT ;
}
}
# Check that @() functionality is present. Similarly to modules, we do not have
# a way to test this directly. Instead we check that $(TMPNAME) functionality is
# present which was added at roughly the same time (more precisely, it was added
# just before).
{
if ! $(TMPNAME)
{
ECHO "error: no @() functionality found" ;
ECHO "error: your version of b2 is likely out of date" ;
ECHO "error: please get a fresh version from SVN." ;
EXIT ;
}
}
# Make sure that \n escape is available.
if "\n" = "n"
{
if $(OS) = CYGWIN
{
ECHO "warning: escape sequences are not supported" ;
ECHO "warning: this will cause major misbehaviour on cygwin" ;
ECHO "warning: your version of b2 is likely out of date" ;
ECHO "warning: please get a fresh version from SVN." ;
}
}
# Bootstrap the module system. Then bring the import rule into the global module.
#
SEARCH on <module@>modules.jam = $(.bootstrap-file:D) ;
module modules { include <module@>modules.jam ; }
IMPORT modules : import : : import ;
{
# Add module subdirectories to the BOOST_BUILD_PATH, which allows us to make
# incremental refactoring steps by moving modules to appropriate
# subdirectories, thereby achieving some physical separation of different
# layers without changing all of our code to specify subdirectories in
# import statements or use an extra level of qualification on imported
# names.
local subdirs =
kernel # only the most-intrinsic modules: modules, errors
util # low-level substrate: string/number handling, etc.
build # essential elements of the build system architecture
tools # toolsets for handling specific build jobs and targets.
contrib # user contributed (unreviewed) modules
. # build-system.jam lives here
;
local whereami = [ NORMALIZE_PATH $(.bootstrap-file:DT) ] ;
BOOST_BUILD_PATH += $(whereami:D)/$(subdirs) ;
modules.poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ;
modules.poke : EXTRA_PYTHONPATH : $(whereami) ;
}
# Reload the modules, to clean up things. The modules module can tolerate being
# imported twice.
#
import modules ;
# Process option plugins first to allow them to prevent loading the rest of the
# build system.
#
import option ;
local dont-build = [ option.process ] ;
# Should we skip building, i.e. loading the build system, according to the
# options processed?
#
if ! $(dont-build)
{
if ! --python in $(ARGV)
{
# Allow users to override the build system file from the command-line
# (mostly for testing).
local build-system = [ MATCH --build-system=(.*) : $(ARGV) ] ;
build-system ?= build-system ;
# Use last element in case of multiple command-line options.
import $(build-system[-1]) ;
}
else
{
# Define additional interface exposed to Python code. Python code will
# also have access to select bjam builtins in the 'bjam' module, but
# some things are easier to define outside C.
module python_interface
{
rule load ( module-name : location )
{
USER_MODULE $(module-name) ;
# Make all rules in the loaded module available in the global
# namespace, so that we do not have to bother specifying the
# "correct" module when calling from Python.
module $(module-name)
{
__name__ = $(1) ;
include $(2) ;
local rules = [ RULENAMES $(1) ] ;
IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ;
}
}
rule peek ( module-name ? : variables + )
{
module $(<)
{
return $($(>)) ;
}
}
rule set-variable ( module-name : name : value * )
{
module $(<)
{
$(>) = $(3) ;
}
}
rule set-top-level-targets ( targets * )
{
DEPENDS all : $(targets) ;
}
rule call-in-module ( m : rulename : * )
{
module $(m)
{
return [ $(2) $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9)
: $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16)
: $(17) : $(18) : $(19) ] ;
}
}
rule set-update-action ( action : targets * : sources * :
properties * )
{
$(action) $(targets) : $(sources) : $(properties) ;
}
rule set-update-action-in-module ( m : action : targets * :
sources * : properties * )
{
module $(m)
{
$(2) $(3) : $(4) : $(5) ;
}
}
rule set-target-variable ( targets + : variable : value * : append ?
)
{
if $(append)
{
$(variable) on $(targets) += $(value) ;
}
else
{
$(variable) on $(targets) = $(value) ;
}
}
rule get-target-variable ( targets + : variable )
{
return [ on $(targets) return $($(variable)) ] ;
}
rule import-rules-from-parent ( parent-module : this-module :
user-rules * )
{
IMPORT $(parent-module) : $(user-rules) : $(this-module) :
$(user-rules) ;
EXPORT $(this-module) : $(user-rules) ;
}
rule mark-included ( targets * : includes * )
{
NOCARE $(includes) ;
INCLUDES $(targets) : $(includes) ;
ISFILE $(includes) ;
}
}
PYTHON_IMPORT_RULE bootstrap : bootstrap : PyBB : bootstrap ;
modules.poke PyBB : root : [ NORMALIZE_PATH $(.bootstrap-file:DT)/.. ] ;
module PyBB
{
local ok = [ bootstrap $(root) ] ;
if ! $(ok)
{
EXIT ;
}
}
#PYTHON_IMPORT_RULE boost.build.build_system : main : PyBB : main ;
#module PyBB
#{
# main ;
#}
}
}

View File

@@ -0,0 +1,25 @@
# Copyright 2009 Vladimir Prus
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import imp
import sys
def bootstrap(root_path):
"""Performs python-side bootstrapping of Boost.Build/Python.
This function arranges for 'b2.whatever' package names to work, while also
allowing to put python files alongside corresponding jam modules.
"""
m = imp.new_module("b2")
# Note that:
# 1. If __path__ is not list of strings, nothing will work
# 2. root_path is already list of strings.
m.__path__ = root_path
sys.modules["b2"] = m
import b2.build_system
return b2.build_system.main()

View File

@@ -0,0 +1,420 @@
# Copyright 2001, 2002, 2003 Dave Abrahams
# Copyright 2002, 2005 Rene Rivera
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Polymorphic class system built on top of core Jam facilities.
#
# Classes are defined by 'class' keywords:
#
# class myclass
# {
# rule __init__ ( arg1 ) # constructor
# {
# self.attribute = $(arg1) ;
# }
#
# rule method1 ( ) # method
# {
# return [ method2 ] ;
# }
#
# rule method2 ( ) # method
# {
# return $(self.attribute) ;
# }
# }
#
# The __init__ rule is the constructor, and sets member variables.
#
# New instances are created by invoking [ new <class> <args...> ]:
#
# local x = [ new myclass foo ] ; # x is a new myclass object
# assert.result foo : [ $(x).method1 ] ; # $(x).method1 returns "foo"
#
# Derived class are created by mentioning base classes in the declaration::
#
# class derived : myclass
# {
# rule __init__ ( arg )
# {
# myclass.__init__ $(arg) ; # call base __init__
#
# }
#
# rule method2 ( ) # method override
# {
# return $(self.attribute)XXX ;
# }
# }
#
# All methods operate virtually, replacing behavior in the base classes. For
# example::
#
# local y = [ new derived foo ] ; # y is a new derived object
# assert.result fooXXX : [ $(y).method1 ] ; # $(y).method1 returns "foo"
#
# Each class instance is its own core Jam module. All instance attributes and
# methods are accessible without additional qualification from within the class
# instance. All rules imported in class declaration, or visible in base classes
# are also visible. Base methods are available in qualified form:
# base-name.method-name. By convention, attribute names are prefixed with
# "self.".
import modules ;
import numbers ;
rule xinit ( instance : class )
{
module $(instance)
{
__class__ = $(2) ;
__name__ = $(1) ;
}
}
rule new ( class args * : * )
{
.next-instance ?= 1 ;
local id = object($(class))@$(.next-instance) ;
INSTANCE $(id) : class@$(class) ;
xinit $(id) : $(class) ;
IMPORT_MODULE $(id) ;
$(id).__init__ $(args) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
$(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) :
$(18) : $(19) ;
# Bump the next unique object name.
.next-instance = [ numbers.increment $(.next-instance) ] ;
# Return the name of the new instance.
return $(id) ;
}
rule bases ( class )
{
module class@$(class)
{
return $(__bases__) ;
}
}
rule is-derived ( class : bases + )
{
local stack = $(class) ;
local visited found ;
while ! $(found) && $(stack)
{
local top = $(stack[1]) ;
stack = $(stack[2-]) ;
if ! ( $(top) in $(visited) )
{
visited += $(top) ;
stack += [ bases $(top) ] ;
if $(bases) in $(visited)
{
found = true ;
}
}
}
return $(found) ;
}
# Returns true if the 'value' is a class instance.
#
rule is-instance ( value )
{
return [ MATCH "^(object\\()[^@]+\\)@.*" : $(value) ] ;
}
# Check if the given value is of the given type.
#
rule is-a (
instance # The value to check.
: type # The type to test for.
)
{
if [ is-instance $(instance) ]
{
return [ class.is-derived [ modules.peek $(instance) : __class__ ] : $(type) ] ;
}
}
local rule typecheck ( x )
{
local class-name = [ MATCH "^\\[(.*)\\]$" : [ BACKTRACE 1 ] ] ;
if ! [ is-a $(x) : $(class-name) ]
{
return "Expected an instance of "$(class-name)" but got \""$(x)"\" for argument" ;
}
}
rule __test__ ( )
{
import assert ;
import "class" : new ;
import errors : try catch ;
# This will be the construction function for a class called 'myclass'.
#
class myclass
{
import assert ;
rule __init__ ( x_ * : y_ * )
{
# Set some instance variables.
x = $(x_) ;
y = $(y_) ;
foo += 10 ;
}
rule set-x ( newx * )
{
x = $(newx) ;
}
rule get-x ( )
{
return $(x) ;
}
rule set-y ( newy * )
{
y = $(newy) ;
}
rule get-y ( )
{
return $(y) ;
}
rule f ( )
{
return [ g $(x) ] ;
}
rule g ( args * )
{
if $(x) in $(y)
{
return $(x) ;
}
else if $(y) in $(x)
{
return $(y) ;
}
else
{
return ;
}
}
rule get-class ( )
{
return $(__class__) ;
}
rule get-instance ( )
{
return $(__name__) ;
}
rule invariant ( )
{
assert.equal 1 : 1 ;
}
rule get-foo ( )
{
return $(foo) ;
}
} # class myclass ;
class derived1 : myclass
{
rule __init__ ( z_ )
{
myclass.__init__ $(z_) : X ;
z = $(z_) ;
}
# Override g.
#
rule g ( args * )
{
return derived1.g ;
}
rule h ( )
{
return derived1.h ;
}
rule get-z ( )
{
return $(z) ;
}
# Check that 'assert.equal' visible in base class is visible here.
#
rule invariant2 ( )
{
assert.equal 2 : 2 ;
}
# Check that 'assert.variable-not-empty' visible in base class is
# visible here.
#
rule invariant3 ( )
{
local v = 10 ;
assert.variable-not-empty v ;
}
} # class derived1 : myclass ;
class derived2 : myclass
{
rule __init__ ( )
{
myclass.__init__ 1 : 2 ;
}
# Override g.
#
rule g ( args * )
{
return derived2.g ;
}
# Test the ability to call base class functions with qualification.
#
rule get-x ( )
{
return [ myclass.get-x ] ;
}
} # class derived2 : myclass ;
class derived2a : derived2
{
rule __init__
{
derived2.__init__ ;
}
} # class derived2a : derived2 ;
local rule expect_derived2 ( [derived2] x ) { }
local a = [ new myclass 3 4 5 : 4 5 ] ;
local b = [ new derived1 4 ] ;
local b2 = [ new derived1 4 ] ;
local c = [ new derived2 ] ;
local d = [ new derived2 ] ;
local e = [ new derived2a ] ;
expect_derived2 $(d) ;
expect_derived2 $(e) ;
# Argument checking is set up to call exit(1) directly on failure, and we
# can not hijack that with try, so we should better not do this test by
# default. We could fix this by having errors look up and invoke the EXIT
# rule instead; EXIT can be hijacked (;-)
if --fail-typecheck in [ modules.peek : ARGV ]
{
try ;
{
expect_derived2 $(a) ;
}
catch
"Expected an instance of derived2 but got" instead
;
}
#try ;
#{
# new bad_subclass ;
#}
#catch
# bad_subclass.bad_subclass failed to call base class constructor
# myclass.__init__
# ;
#try ;
#{
# class bad_subclass ;
#}
#catch bad_subclass has already been declared ;
assert.result 3 4 5 : $(a).get-x ;
assert.result 4 5 : $(a).get-y ;
assert.result 4 : $(b).get-x ;
assert.result X : $(b).get-y ;
assert.result 4 : $(b).get-z ;
assert.result 1 : $(c).get-x ;
assert.result 2 : $(c).get-y ;
assert.result 4 5 : $(a).f ;
assert.result derived1.g : $(b).f ;
assert.result derived2.g : $(c).f ;
assert.result derived2.g : $(d).f ;
assert.result 10 : $(b).get-foo ;
$(a).invariant ;
$(b).invariant2 ;
$(b).invariant3 ;
# Check that the __class__ attribute is getting properly set.
assert.result myclass : $(a).get-class ;
assert.result derived1 : $(b).get-class ;
assert.result $(a) : $(a).get-instance ;
$(a).set-x a.x ;
$(b).set-x b.x ;
$(c).set-x c.x ;
$(d).set-x d.x ;
assert.result a.x : $(a).get-x ;
assert.result b.x : $(b).get-x ;
assert.result c.x : $(c).get-x ;
assert.result d.x : $(d).get-x ;
class derived3 : derived1 derived2
{
rule __init__ ( )
{
}
}
assert.result : bases myclass ;
assert.result myclass : bases derived1 ;
assert.result myclass : bases derived2 ;
assert.result derived1 derived2 : bases derived3 ;
assert.true is-derived derived1 : myclass ;
assert.true is-derived derived2 : myclass ;
assert.true is-derived derived3 : derived1 ;
assert.true is-derived derived3 : derived2 ;
assert.true is-derived derived3 : derived1 derived2 myclass ;
assert.true is-derived derived3 : myclass ;
assert.false is-derived myclass : derived1 ;
assert.true is-instance $(a) ;
assert.false is-instance bar ;
assert.true is-a $(a) : myclass ;
assert.true is-a $(c) : derived2 ;
assert.true is-a $(d) : myclass ;
assert.false is-a literal : myclass ;
}

View File

@@ -0,0 +1,287 @@
# Copyright 2003 Dave Abrahams
# Copyright 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Print a stack backtrace leading to this rule's caller. Each argument
# represents a line of output to be printed after the first line of the
# backtrace.
#
rule backtrace ( skip-frames prefix messages * : * )
{
local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ;
local drop-elements = $(frame-skips[$(skip-frames)]) ;
if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 )
{
ECHO "warning: backtrace doesn't support skipping $(skip-frames) "
"frames; using 1 instead." ;
drop-elements = 5 ;
}
local args = $(.args) ;
if $(.user-modules-only)
{
local bt = [ nearest-user-location ] ;
if $(bt)
{
ECHO $(prefix) at $(bt) ;
}
for local n in $(args)
{
if $($(n))-is-defined
{
ECHO $(prefix) $($(n)) ;
}
}
}
else
{
# Get the whole backtrace, then drop the initial quadruples
# corresponding to the frames that must be skipped.
local bt = [ BACKTRACE ] ;
bt = $(bt[$(drop-elements)-]) ;
while $(bt)
{
local m = [ MATCH ^(.+)\\.$ : $(bt[3]) ] ;
ECHO "$(bt[1]):$(bt[2]):" "in" $(bt[4]) "from module" $(m) ;
# The first time through, print each argument on a separate line.
for local n in $(args)
{
if $($(n))-is-defined
{
ECHO $(prefix) $($(n)) ;
}
}
args = ; # Kill args so that this never happens again.
# Move on to the next quadruple.
bt = $(bt[5-]) ;
}
}
}
.args ?= messages 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ;
.disabled ?= ;
.last-error-$(.args) ?= ;
# try-catch --
#
# This is not really an exception-handling mechanism, but it does allow us to
# perform some error-checking on our error-checking. Errors are suppressed after
# a try, and the first one is recorded. Use catch to check that the error
# message matched expectations.
# Begin looking for error messages.
#
rule try ( )
{
.disabled += true ;
.last-error-$(.args) = ;
}
# Stop looking for error messages; generate an error if an argument of messages
# is not found in the corresponding argument in the error call.
#
rule catch ( messages * : * )
{
.disabled = $(.disabled[2-]) ; # Pop the stack.
import sequence ;
if ! $(.last-error-$(.args))-is-defined
{
error-skip-frames 3 expected an error, but none occurred ;
}
else
{
for local n in $(.args)
{
if ! $($(n)) in $(.last-error-$(n))
{
local v = [ sequence.join $($(n)) : " " ] ;
v ?= "" ;
local joined = [ sequence.join $(.last-error-$(n)) : " " ] ;
.last-error-$(.args) = ;
error-skip-frames 3 expected \"$(v)\" in argument $(n) of error
: got \"$(joined)\" instead ;
}
}
}
}
rule error-skip-frames ( skip-frames messages * : * )
{
if ! $(.disabled)
{
backtrace $(skip-frames) "error:" $(messages) : $(2) : $(3) : $(4) : $(5)
: $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14)
: $(15) : $(16) : $(17) : $(18) : $(19) ;
EXIT ;
}
else if ! $(.last-error-$(.args))
{
for local n in $(.args)
{
# Add an extra empty string so that we always have something in the
# event of an error.
.last-error-$(n) = $($(n)) "" ;
}
}
}
if --no-error-backtrace in [ modules.peek : ARGV ]
{
.no-error-backtrace = true ;
}
# Print an error message with a stack backtrace and exit.
#
rule error ( messages * : * )
{
if $(.no-error-backtrace)
{
local first-printed ;
# Print each argument on a separate line.
for local n in $(.args)
{
if $($(n))-is-defined
{
if ! $(first-printed)
{
ECHO "error:" $($(n)) ;
first-printed = true ;
}
else
{
ECHO $($(n)) ;
}
}
}
EXIT ;
}
else
{
error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) :
$(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16)
: $(17) : $(18) : $(19) ;
}
}
# Same as 'error', but the generated backtrace will include only user files.
#
rule user-error ( messages * : * )
{
.user-modules-only = 1 ;
error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
$(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) :
$(18) : $(19) ;
}
# Print a warning message with a stack backtrace and exit.
#
rule warning
{
backtrace 2 "warning:" $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
$(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) :
$(18) : $(19) ;
}
# Convert an arbitrary argument list into a list with ":" separators and quoted
# elements representing the same information. This is mostly useful for
# formatting descriptions of arguments with which a rule was called when
# reporting an error.
#
rule lol->list ( * )
{
local result ;
local remaining = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ;
while $($(remaining))
{
local n = $(remaining[1]) ;
remaining = $(remaining[2-]) ;
if $(n) != 1
{
result += ":" ;
}
result += \"$($(n))\" ;
}
return $(result) ;
}
# Return the file:line for the nearest entry in backtrace which correspond to a
# user module.
#
rule nearest-user-location ( )
{
local bt = [ BACKTRACE ] ;
local result ;
while $(bt) && ! $(result)
{
local m = [ MATCH ^(.+)\\.$ : $(bt[3]) ] ;
local user-modules = "([Jj]amroot(.jam|.v2|)|([Jj]amfile(.jam|.v2|)|user-config.jam|site-config.jam|project-config.jam|project-root.jam)" ;
if [ MATCH $(user-modules) : $(bt[1]:D=) ]
{
result = "$(bt[1]):$(bt[2])" ;
}
bt = $(bt[5-]) ;
}
return $(result) ;
}
# If optimized rule is available in Jam, use it.
if NEAREST_USER_LOCATION in [ RULENAMES ]
{
rule nearest-user-location ( )
{
local r = [ NEAREST_USER_LOCATION ] ;
return "$(r[1]):$(r[2])" ;
}
}
rule __test__ ( )
{
# Show that we can correctly catch an expected error.
try ;
{
error an error occurred : somewhere ;
}
catch an error occurred : somewhere ;
# Show that unexpected errors generate real errors.
try ;
{
try ;
{
error an error occurred : somewhere ;
}
catch an error occurred : nowhere ;
}
catch expected \"nowhere\" in argument 2 ;
# Show that not catching an error where one was expected is an error.
try ;
{
try ;
{
}
catch ;
}
catch expected an error, but none occurred ;
}

View File

@@ -0,0 +1,364 @@
# Copyright 2003 Dave Abrahams
# Copyright 2003, 2005 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Essentially an include guard; ensures that no module is loaded multiple times.
.loaded ?= ;
# A list of modules currently being loaded for error reporting of circular
# dependencies.
.loading ?= ;
# A list of modules needing to be tested using their __test__ rule.
.untested ?= ;
# A list of modules which have been tested using their __test__ rule.
.tested ?= ;
# Runs internal Boost Build unit tests for the specified module. The module's
# __test__ rule is executed in its own module to eliminate any inadvertent
# effects of testing module dependencies (such as assert) on the module itself.
#
local rule run-module-test ( m )
{
local tested-modules = [ modules.peek modules : .tested ] ;
if ( ! $(m) in $(tested-modules) ) # Avoid recursive test invocations.
&& ( ( --debug in $(argv) ) || ( "--debug-module=$(m)" in $(argv) ) )
{
modules.poke modules : .tested : $(tested-modules) $(m) ;
if ! ( __test__ in [ RULENAMES $(m) ] )
{
local argv = [ peek : ARGV ] ;
if ! ( --quiet in $(argv) ) && ( --debug-tests in $(argv) )
{
ECHO "warning:" no __test__ rule defined in module $(m) ;
}
}
else
{
if ! ( --quiet in $(argv) )
{
ECHO testing module $(m)... ;
}
local test-module = __test-$(m)__ ;
IMPORT $(m) : [ RULENAMES $(m) ] : $(test-module) : [ RULENAMES $(m)
] ;
IMPORT $(m) : __test__ : $(test-module) : __test__ : LOCALIZE ;
module $(test-module)
{
__test__ ;
}
}
}
}
# Return the binding of the given module.
#
rule binding ( module )
{
return $($(module).__binding__) ;
}
# Sets the module-local value of a variable. This is the most reliable way to
# set a module-local variable in a different module; it eliminates issues of
# name shadowing due to dynamic scoping.
#
rule poke ( module-name ? : variables + : value * )
{
module $(<)
{
$(>) = $(3) ;
}
}
# Returns the module-local value of a variable. This is the most reliable way to
# examine a module-local variable in a different module; it eliminates issues of
# name shadowing due to dynamic scoping.
#
rule peek ( module-name ? : variables + )
{
module $(<)
{
return $($(>)) ;
}
}
# Call the given rule locally in the given module. Use this for rules accepting
# rule names as arguments, so that the passed rule may be invoked in the context
# of the rule's caller (for example, if the rule accesses module globals or is a
# local rule). Note that rules called this way may accept at most 18 parameters.
#
rule call-in ( module-name ? : rule-name args * : * )
{
module $(module-name)
{
return [ $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) :
$(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) ] ;
}
}
# Given a possibly qualified rule name and arguments, remove any initial module
# qualification from the rule and invoke it in that module. If there is no
# module qualification, the rule is invoked in the global module. Note that
# rules called this way may accept at most 18 parameters.
#
rule call-locally ( qualified-rule-name args * : * )
{
local module-rule = [ MATCH (.*)\\.(.*) : $(qualified-rule-name) ] ;
local rule-name = $(module-rule[2]) ;
rule-name ?= $(qualified-rule-name) ;
# We pass only 18 parameters here since Boost Jam allows at most 19 rule
# parameter positions and the call-in rule already uses up the initial
# position for the module name.
return [ call-in $(module-rule[1]) : $(rule-name) $(args) : $(2) : $(3) :
$(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13)
$(14) : $(15) : $(16) : $(17) : $(18) : $(19) ] ;
}
# Load the indicated module if it is not already loaded.
#
rule load (
module-name # Name of module to load. Rules will be defined in this
# module.
: filename ? # (partial) path to file; Defaults to $(module-name).jam.
: search * # Directories in which to search for filename. Defaults to
# $(BOOST_BUILD_PATH).
)
{
# Avoid loading modules twice.
if ! ( $(module-name) in $(.loaded) )
{
filename ?= $(module-name).jam ;
# Mark the module loaded so we do not try to load it recursively.
.loaded += $(module-name:B) ;
# Suppress tests if any module loads are already in progress.
local suppress-test = $(.loading[1]) ;
# Push this module on the loading stack.
.loading += $(module-name) ;
# Remember that it is untested.
.untested += $(module-name) ;
# Insert the new module's __name__ and __file__ globals.
poke $(module-name) : __name__ : $(module-name) ;
poke $(module-name) : __file__ : $(filename) ;
module $(module-name)
{
# Add some grist so that the module will have a unique target name.
local module-target = $(__file__:G=module@) ;
local search = $(3) ;
search ?= [ modules.peek : BOOST_BUILD_PATH ] ;
SEARCH on $(module-target) = $(search) ;
BINDRULE on $(module-target) = modules.record-binding ;
include $(module-target) ;
# Allow the module to see its own names with full qualification.
local rules = [ RULENAMES $(__name__) ] ;
IMPORT $(__name__) : $(rules) : $(__name__) : $(__name__).$(rules) ;
}
if $(module-name) != modules && ! [ binding $(module-name) ]
{
import errors ;
errors.error "Could not find module" $(module-name) in $(search) ;
}
# Pop the loading stack. Must happen before testing or we will run into
# a circular loading dependency.
.loading = $(.loading[1--2]) ;
# Run any pending tests if this is an outer load.
if ! $(suppress-test)
{
local argv = [ peek : ARGV ] ;
for local m in $(.untested)
{
run-module-test $(m) ;
}
.untested = ;
}
}
else if $(module-name) in $(.loading)
{
import errors ;
errors.error loading \"$(module-name)\"
: circular module loading "dependency:"
: $(.loading)" ->" $(module-name) ;
}
}
# This helper is used by load (above) to record the binding (path) of each
# loaded module.
#
rule record-binding ( module-target : binding )
{
$(.loading[-1]).__binding__ = $(binding) ;
}
# Transform each path in the list, with all backslashes converted to forward
# slashes and all detectable redundancy removed. Something like this is probably
# needed in path.jam, but I am not sure of that, I do not understand it, and I
# am not ready to move all of path.jam into the kernel.
#
local rule normalize-raw-paths ( paths * )
{
local result ;
for p in $(paths:T)
{
result += [ NORMALIZE_PATH $(p) ] ;
}
return $(result) ;
}
.cwd = [ PWD ] ;
# Load the indicated module and import rule names into the current module. Any
# members of rules-opt will be available without qualification in the caller's
# module. Any members of rename-opt will be taken as the names of the rules in
# the caller's module, in place of the names they have in the imported module.
# If rules-opt = '*', all rules from the indicated module are imported into the
# caller's module. If rename-opt is supplied, it must have the same number of
# elements as rules-opt.
#
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" ;
}
local caller = [ CALLER_MODULE ] ;
# Import each specified module
for local m in $(module-names)
{
local module-name = $(m:B) ;
if ! $(module-name) in $(.loaded)
{
# If the importing module is not already in the BOOST_BUILD_PATH,
# prepend it to the path. We do not want to invert the search order
# of modules that are already there.
local caller-location ;
if $(caller)
{
caller-location = [ binding $(caller) ] ;
caller-location = $(caller-location:D) ;
caller-location = [ normalize-raw-paths
$(caller-location:R=$(.cwd)) ] ;
}
local search = [ peek : BOOST_BUILD_PATH ] ;
search = [ normalize-raw-paths $(search:R=$(.cwd)) ] ;
if $(caller-location) && ! $(caller-location) in $(search)
{
search = $(caller-location) $(search) ;
}
if $(m:D)
{
search = $(caller-location)/$(m:D) $(search)/$(m:D) $(search) ;
}
load $(module-name) : : $(search) ;
}
IMPORT_MODULE $(module-name) : $(caller) ;
if $(rules-opt)
{
local source-names ;
if $(rules-opt) = *
{
local all-rules = [ RULENAMES $(module-name) ] ;
source-names = $(all-rules) ;
}
else
{
source-names = $(rules-opt) ;
}
local target-names = $(rename-opt) ;
target-names ?= $(source-names) ;
IMPORT $(module-name) : $(source-names) : $(caller) : $(target-names) ;
}
}
}
# Define exported copies in $(target-module) of all rules exported from
# $(source-module). Also make them available in the global module with
# qualification, so that it is just as though the rules were defined originally
# in $(target-module).
#
rule clone-rules ( source-module target-module )
{
local r = [ RULENAMES $(source-module) ] ;
IMPORT $(source-module) : $(r) : $(target-module) : $(r) : LOCALIZE ;
EXPORT $(target-module) : $(r) ;
IMPORT $(target-module) : $(r) : : $(target-module).$(r) ;
}
# These rules need to be available in all modules to implement module loading
# itself and other fundamental operations.
local globalize = peek poke record-binding ;
IMPORT modules : $(globalize) : : modules.$(globalize) ;
rule __test__ ( )
{
import assert ;
import modules : normalize-raw-paths ;
module modules.__test__
{
foo = bar ;
}
assert.result bar : peek modules.__test__ : foo ;
poke modules.__test__ : foo : bar baz ;
assert.result bar baz : peek modules.__test__ : foo ;
assert.result c:/foo/bar : normalize-raw-paths c:/x/../foo/./xx/yy/../../bar ;
assert.result . : normalize-raw-paths . ;
assert.result .. : normalize-raw-paths .. ;
assert.result ../.. : normalize-raw-paths ../.. ;
assert.result .. : normalize-raw-paths ./.. ;
assert.result / / : normalize-raw-paths / \\ ;
assert.result a : normalize-raw-paths a ;
assert.result a : normalize-raw-paths a/ ;
assert.result /a : normalize-raw-paths /a/ ;
assert.result / : normalize-raw-paths /a/.. ;
}