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,321 @@
import bjam
import re
import types
from itertools import groupby
def safe_isinstance(value, types=None, class_names=None):
"""To prevent circular imports, this extends isinstance()
by checking also if `value` has a particular class name (or inherits from a
particular class name). This check is safe in that an AttributeError is not
raised in case `value` doesn't have a __class__ attribute.
"""
# inspect is being imported here because I seriously doubt
# that this function will be used outside of the type
# checking below.
import inspect
result = False
if types is not None:
result = result or isinstance(value, types)
if class_names is not None and not result:
# this doesn't work with inheritance, but normally
# either the class will already be imported within the module,
# or the class doesn't have any subclasses. For example: PropertySet
if isinstance(class_names, basestring):
class_names = [class_names]
# this is the part that makes it "safe".
try:
base_names = [class_.__name__ for class_ in inspect.getmro(value.__class__)]
for name in class_names:
if name in base_names:
return True
except AttributeError:
pass
return result
def is_iterable_typed(values, type_):
return is_iterable(values) and all(isinstance(v, type_) for v in values)
def is_iterable(value):
"""Returns whether value is iterable and not a string."""
return not isinstance(value, basestring) and hasattr(value, '__iter__')
def is_iterable_or_none(value):
return is_iterable(value) or value is None
def is_single_value(value):
# some functions may specify a bjam signature
# that is a string type, but still allow a
# PropertySet to be passed in
return safe_isinstance(value, (basestring, type(None)), 'PropertySet')
if __debug__:
from textwrap import dedent
message = dedent(
"""The parameter "{}" was passed in a wrong type for the "{}()" function.
Actual:
\ttype: {}
\tvalue: {}
Expected:
\t{}
"""
)
bjam_types = {
'*': is_iterable_or_none,
'+': is_iterable_or_none,
'?': is_single_value,
'': is_single_value,
}
bjam_to_python = {
'*': 'iterable',
'+': 'iterable',
'?': 'single value',
'': 'single value',
}
def get_next_var(field):
it = iter(field)
var = it.next()
type_ = None
yield_var = False
while type_ not in bjam_types:
try:
# the first value has already
# been consumed outside of the loop
type_ = it.next()
except StopIteration:
# if there are no more values, then
# var still needs to be returned
yield_var = True
break
if type_ not in bjam_types:
# type_ is not a type and is
# another variable in the same field.
yield var, ''
# type_ is the next var
var = type_
else:
# otherwise, type_ is a type for var
yield var, type_
try:
# the next value should be a var
var = it.next()
except StopIteration:
# if not, then we're done with
# this field
break
if yield_var:
yield var, ''
# Decorator the specifies bjam-side prototype for a Python function
def bjam_signature(s):
if __debug__:
from inspect import getcallargs
def decorator(fn):
function_name = fn.__module__ + '.' + fn.__name__
def wrapper(*args, **kwargs):
callargs = getcallargs(fn, *args, **kwargs)
for field in s:
for var, type_ in get_next_var(field):
try:
value = callargs[var]
except KeyError:
raise Exception(
'Bjam Signature specifies a variable named "{}"\n'
'but is not found within the python function signature\n'
'for function {}()'.format(var, function_name)
)
if not bjam_types[type_](value):
raise TypeError(
message.format(var, function_name, type(type_), repr(value),
bjam_to_python[type_])
)
return fn(*args, **kwargs)
wrapper.__name__ = fn.__name__
wrapper.bjam_signature = s
return wrapper
return decorator
else:
def decorator(f):
f.bjam_signature = s
return f
return decorator
def metatarget(f):
f.bjam_signature = (["name"], ["sources", "*"], ["requirements", "*"],
["default_build", "*"], ["usage_requirements", "*"])
return f
class cached(object):
def __init__(self, function):
self.function = function
self.cache = {}
def __call__(self, *args):
try:
return self.cache[args]
except KeyError:
v = self.function(*args)
self.cache[args] = v
return v
def __get__(self, instance, type):
return types.MethodType(self, instance, type)
def unquote(s):
if s and s[0] == '"' and s[-1] == '"':
return s[1:-1]
else:
return s
_extract_jamfile_and_rule = re.compile("(Jamfile<.*>)%(.*)")
def qualify_jam_action(action_name, context_module):
if action_name.startswith("###"):
# Callable exported from Python. Don't touch
return action_name
elif _extract_jamfile_and_rule.match(action_name):
# Rule is already in indirect format
return action_name
else:
ix = action_name.find('.')
if ix != -1 and action_name[:ix] == context_module:
return context_module + '%' + action_name[ix+1:]
return context_module + '%' + action_name
def set_jam_action(name, *args):
m = _extract_jamfile_and_rule.match(name)
if m:
args = ("set-update-action-in-module", m.group(1), m.group(2)) + args
else:
args = ("set-update-action", name) + args
return bjam.call(*args)
def call_jam_function(name, *args):
m = _extract_jamfile_and_rule.match(name)
if m:
args = ("call-in-module", m.group(1), m.group(2)) + args
return bjam.call(*args)
else:
return bjam.call(*((name,) + args))
__value_id = 0
__python_to_jam = {}
__jam_to_python = {}
def value_to_jam(value, methods=False):
"""Makes a token to refer to a Python value inside Jam language code.
The token is merely a string that can be passed around in Jam code and
eventually passed back. For example, we might want to pass PropertySet
instance to a tag function and it might eventually call back
to virtual_target.add_suffix_and_prefix, passing the same instance.
For values that are classes, we'll also make class methods callable
from Jam.
Note that this is necessary to make a bit more of existing Jamfiles work.
This trick should not be used to much, or else the performance benefits of
Python port will be eaten.
"""
global __value_id
r = __python_to_jam.get(value, None)
if r:
return r
exported_name = '###_' + str(__value_id)
__value_id = __value_id + 1
__python_to_jam[value] = exported_name
__jam_to_python[exported_name] = value
if methods and type(value) == types.InstanceType:
for field_name in dir(value):
field = getattr(value, field_name)
if callable(field) and not field_name.startswith("__"):
bjam.import_rule("", exported_name + "." + field_name, field)
return exported_name
def record_jam_to_value_mapping(jam_value, python_value):
__jam_to_python[jam_value] = python_value
def jam_to_value_maybe(jam_value):
if type(jam_value) == type(""):
return __jam_to_python.get(jam_value, jam_value)
else:
return jam_value
def stem(filename):
i = filename.find('.')
if i != -1:
return filename[0:i]
else:
return filename
def abbreviate_dashed(s):
"""Abbreviates each part of string that is delimited by a '-'."""
r = []
for part in s.split('-'):
r.append(abbreviate(part))
return '-'.join(r)
def abbreviate(s):
"""Apply a set of standard transformations to string to produce an
abbreviation no more than 4 characters long.
"""
if not s:
return ''
# check the cache
if s in abbreviate.abbreviations:
return abbreviate.abbreviations[s]
# anything less than 4 characters doesn't need
# an abbreviation
if len(s) < 4:
# update cache
abbreviate.abbreviations[s] = s
return s
# save the first character in case it's a vowel
s1 = s[0]
s2 = s[1:]
if s.endswith('ing'):
# strip off the 'ing'
s2 = s2[:-3]
# reduce all doubled characters to one
s2 = ''.join(c for c, _ in groupby(s2))
# remove all vowels
s2 = s2.translate(None, "AEIOUaeiou")
# shorten remaining consonants to 4 characters
# and add the first char back to the front
s2 = s1 + s2[:4]
# update cache
abbreviate.abbreviations[s] = s2
return s2
# maps key to its abbreviated form
abbreviate.abbreviations = {}

View File

@@ -0,0 +1,346 @@
# Copyright 2001, 2002, 2003 Dave Abrahams
# Copyright 2006 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)
import errors ;
import modules ;
################################################################################
#
# Private implementation details.
#
################################################################################
# Rule added as a replacement for the regular Jam = operator but which does not
# ignore trailing empty string elements.
#
local rule exact-equal-test ( lhs * : rhs * )
{
local lhs_extended = $(lhs) xxx ;
local rhs_extended = $(rhs) xxx ;
if $(lhs_extended) = $(rhs_extended)
{
return true ;
}
}
# Two lists are considered set-equal if they contain the same elements, ignoring
# duplicates and ordering.
#
local rule set-equal-test ( set1 * : set2 * )
{
if ( $(set1) in $(set2) ) && ( $(set2) in $(set1) )
{
return true ;
}
}
################################################################################
#
# Public interface.
#
################################################################################
# Assert the equality of A and B, ignoring trailing empty string elements.
#
rule equal ( a * : b * )
{
if $(a) != $(b)
{
errors.error-skip-frames 3 assertion "failure:" \"$(a)\" "==" \"$(b)\"
(ignoring trailing empty strings) ;
}
}
# Assert that the result of calling RULE-NAME on the given arguments has a false
# logical value (is either an empty list or all empty strings).
#
rule false ( rule-name args * : * )
{
local result ;
module [ CALLER_MODULE ]
{
modules.poke assert : result : [ $(1) : $(2) : $(3) : $(4) : $(5) : $(6)
: $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15)
: $(16) : $(17) : $(18) : $(19) ] ;
}
if $(result)
{
errors.error-skip-frames 3 assertion "failure:" Expected false result from
"[" $(rule-name) [ errors.lol->list $(args) : $(2) : $(3) : $(4) :
$(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) :
$(14) : $(15) : $(16) : $(17) : $(18) : $(19) ] "]" : "Got:" "["
\"$(result)\" "]" ;
}
}
# Assert that ELEMENT is present in LIST.
#
rule "in" ( element : list * )
{
if ! $(element) in $(list)
{
errors.error-skip-frames 3 assertion "failure:" Expected \"$(element)\" in
"[" \"$(list)\" "]" ;
}
}
# Assert the inequality of A and B, ignoring trailing empty string elements.
#
rule not-equal ( a * : b * )
{
if $(a) = $(b)
{
errors.error-skip-frames 3 assertion "failure:" \"$(a)\" "!=" \"$(b)\"
(ignoring trailing empty strings) ;
}
}
# Assert that ELEMENT is not present in LIST.
#
rule not-in ( element : list * )
{
if $(element) in $(list)
{
errors.error-skip-frames 3 assertion "failure:" Did not expect
\"$(element)\" in "[" \"$(list)\" "]" ;
}
}
# Assert the inequality of A and B as sets.
#
rule not-set-equal ( a * : b * )
{
if [ set-equal-test $(a) : $(b) ]
{
errors.error-skip-frames 3 assertion "failure:" Expected "[" \"$(a)\" "]"
and "[" \"$(b)\" "]" to not be equal as sets ;
}
}
# Assert that A and B are not exactly equal, not ignoring trailing empty string
# elements.
#
rule not-exact-equal ( a * : b * )
{
if [ exact-equal-test $(a) : $(b) ]
{
errors.error-skip-frames 3 assertion "failure:" \"$(a)\" "!=" \"$(b)\" ;
}
}
# Assert that EXPECTED is the result of calling RULE-NAME with the given
# arguments.
#
rule result ( expected * : rule-name args * : * )
{
local result ;
module [ CALLER_MODULE ]
{
modules.poke assert : result : [ $(2) : $(3) : $(4) : $(5) : $(6) : $(7)
: $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) :
$(16) : $(17) : $(18) : $(19) ] ;
}
if ! [ exact-equal-test $(result) : $(expected) ]
{
errors.error-skip-frames 3 assertion "failure:" "[" $(rule-name) [
errors.lol->list $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
$(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17)
: $(18) : $(19) ] "]" : "Expected:" "[" \"$(expected)\" "]" : "Got:" "["
\"$(result)\" "]" ;
}
}
# Assert that EXPECTED is set-equal (i.e. duplicates and ordering are ignored)
# to the result of calling RULE-NAME with the given arguments. Note that rules
# called this way may accept at most 18 parameters.
#
rule result-set-equal ( expected * : rule-name args * : * )
{
local result ;
module [ CALLER_MODULE ]
{
modules.poke assert : result : [ $(2) : $(3) : $(4) : $(5) : $(6) : $(7)
: $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) :
$(16) : $(17) : $(18) : $(19) ] ;
}
if ! [ set-equal-test $(result) : $(expected) ]
{
errors.error-skip-frames 3 assertion "failure:" "[" $(rule-name) [
errors.lol->list $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) :
$(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17)
: $(18) : $(19) ] "]" : "Expected:" "[" \"$(expected)\" "]" : "Got:" "["
\"$(result)\" "]" ;
}
}
# Assert the equality of A and B as sets.
#
rule set-equal ( a * : b * )
{
if ! [ set-equal-test $(a) : $(b) ]
{
errors.error-skip-frames 3 assertion "failure:" Expected "[" \"$(a)\" "]"
and "[" \"$(b)\" "]" to be equal as sets ;
}
}
# Assert that the result of calling RULE-NAME on the given arguments has a true
# logical value (is neither an empty list nor all empty strings).
#
rule true ( rule-name args * : * )
{
local result ;
module [ CALLER_MODULE ]
{
modules.poke assert : result : [ $(1) : $(2) : $(3) : $(4) : $(5) : $(6)
: $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15)
: $(16) : $(17) : $(18) : $(19) ] ;
}
if ! $(result)
{
errors.error-skip-frames 3 assertion "failure:" Expected true result from
"[" $(rule-name) [ errors.lol->list $(args) : $(2) : $(3) : $(4) :
$(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) :
$(14) : $(15) : $(16) : $(17) : $(18) : $(19) ] "]" ;
}
}
# Assert the exact equality of A and B, not ignoring trailing empty string
# elements.
#
rule exact-equal ( a * : b * )
{
if ! [ exact-equal-test $(a) : $(b) ]
{
errors.error-skip-frames 3 assertion "failure:" \"$(a)\" "==" \"$(b)\" ;
}
}
# Assert that the given variable is not an empty list.
#
rule variable-not-empty ( name )
{
local value = [ modules.peek [ CALLER_MODULE ] : $(name) ] ;
if ! $(value)-is-not-empty
{
errors.error-skip-frames 3 assertion "failure:" Expected variable
\"$(name)\" not to be an empty list ;
}
}
rule __test__ ( )
{
# Helper rule used to avoid test duplication related to different list
# equality test rules.
#
local rule run-equality-test ( equality-assert : ignore-trailing-empty-strings ? )
{
local not-equality-assert = not-$(equality-assert) ;
# When the given equality test is expected to ignore trailing empty
# strings some of the test results should be inverted.
local not-equality-assert-i = not-$(equality-assert) ;
if $(ignore-trailing-empty-strings)
{
not-equality-assert-i = $(equality-assert) ;
}
$(equality-assert) : ;
$(equality-assert) "" "" : "" "" ;
$(not-equality-assert-i) : "" "" ;
$(equality-assert) x : x ;
$(not-equality-assert) : x ;
$(not-equality-assert) "" : x ;
$(not-equality-assert) "" "" : x ;
$(not-equality-assert-i) x : x "" ;
$(equality-assert) x "" : x "" ;
$(not-equality-assert) x : "" x ;
$(equality-assert) "" x : "" x ;
$(equality-assert) 1 2 3 : 1 2 3 ;
$(not-equality-assert) 1 2 3 : 3 2 1 ;
$(not-equality-assert) 1 2 3 : 1 5 3 ;
$(not-equality-assert) 1 2 3 : 1 "" 3 ;
$(not-equality-assert) 1 2 3 : 1 1 2 3 ;
$(not-equality-assert) 1 2 3 : 1 2 2 3 ;
$(not-equality-assert) 1 2 3 : 5 6 7 ;
# Extra variables used here just to make sure Boost Jam or Boost Build
# do not handle lists with empty strings differently depending on
# whether they are literals or stored in variables.
local empty = ;
local empty-strings = "" "" ;
local x-empty-strings = x "" "" ;
local empty-strings-x = "" "" x ;
$(equality-assert) : $(empty) ;
$(not-equality-assert-i) "" : $(empty) ;
$(not-equality-assert-i) "" "" : $(empty) ;
$(not-equality-assert-i) : $(empty-strings) ;
$(not-equality-assert-i) "" : $(empty-strings) ;
$(equality-assert) "" "" : $(empty-strings) ;
$(equality-assert) $(empty) : $(empty) ;
$(equality-assert) $(empty-strings) : $(empty-strings) ;
$(not-equality-assert-i) $(empty) : $(empty-strings) ;
$(equality-assert) $(x-empty-strings) : $(x-empty-strings) ;
$(equality-assert) $(empty-strings-x) : $(empty-strings-x) ;
$(not-equality-assert) $(empty-strings-x) : $(x-empty-strings) ;
$(not-equality-assert-i) x : $(x-empty-strings) ;
$(not-equality-assert) x : $(empty-strings-x) ;
$(not-equality-assert-i) x : $(x-empty-strings) ;
$(not-equality-assert-i) x "" : $(x-empty-strings) ;
$(equality-assert) x "" "" : $(x-empty-strings) ;
$(not-equality-assert) x : $(empty-strings-x) ;
$(not-equality-assert) "" x : $(empty-strings-x) ;
$(equality-assert) "" "" x : $(empty-strings-x) ;
}
# ---------------
# Equality tests.
# ---------------
run-equality-test equal : ignore-trailing-empty-strings ;
run-equality-test exact-equal ;
# -------------------------
# assert.set-equal() tests.
# -------------------------
set-equal : ;
not-set-equal "" "" : ;
set-equal "" "" : "" ;
set-equal "" "" : "" "" ;
set-equal a b c : a b c ;
set-equal a b c : b c a ;
set-equal a b c a : a b c ;
set-equal a b c : a b c a ;
not-set-equal a b c : a b c d ;
not-set-equal a b c d : a b c ;
}

View File

@@ -0,0 +1,339 @@
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003 Rene Rivera
# Copyright 2002, 2003, 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Various container classes.
# Base for container objects. This lets us construct recursive structures. That
# is containers with containers in them, specifically so we can tell literal
# values from node values.
#
class node
{
rule __init__ (
value ? # Optional value to set node to initially.
)
{
self.value = $(value) ;
}
# Set the value of this node, passing nothing will clear it.
#
rule set ( value * )
{
self.value = $(value) ;
}
# Get the value of this node.
#
rule get ( )
{
return $(self.value) ;
}
}
# A simple vector. Interface mimics the C++ std::vector and std::list, with the
# exception that indices are one (1) based to follow Jam standard.
#
# TODO: Possibly add assertion checks.
#
class vector : node
{
import numbers ;
import utility ;
import sequence ;
rule __init__ (
values * # Initial contents of vector.
)
{
node.__init__ ;
self.value = $(values) ;
}
# Get the value of the first element.
#
rule front ( )
{
return $(self.value[1]) ;
}
# Get the value of the last element.
#
rule back ( )
{
return $(self.value[-1]) ;
}
# Get the value of the element at the given index, one based. Access to
# elements of recursive structures is supported directly. Specifying
# additional index values recursively accesses the elements as containers.
# For example: [ $(v).at 1 : 2 ] would retrieve the second element of our
# first element, assuming the first element is a container.
#
rule at (
index # The element index, one based.
: * # Additional indices to access recursively.
)
{
local r = $(self.value[$(index)]) ;
if $(2)
{
r = [ $(r).at $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ] ;
}
return $(r) ;
}
# Get the value contained in the given element. This has the same
# functionality and interface as "at" but in addition gets the value of the
# referenced element, assuming it is a "node".
#
rule get-at (
index # The element index, one based.
: * # Additional indices to access recursively.
)
{
local r = $(self.value[$(index)]) ;
if $(2)
{
r = [ $(r).at $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ] ;
}
return [ $(r).get ] ;
}
# Insert the given value into the front of the vector pushing the rest of
# the elements back.
#
rule push-front (
value # Value to become first element.
)
{
self.value = $(value) $(self.value) ;
}
# Remove the front element from the vector. Does not return the value. No
# effect if vector is empty.
#
rule pop-front ( )
{
self.value = $(self.value[2-]) ;
}
# Add the given value at the end of the vector.
#
rule push-back (
value # Value to become back element.
)
{
self.value += $(value) ;
}
# Remove the back element from the vector. Does not return the value. No
# effect if vector is empty.
#
rule pop-back ( )
{
self.value = $(self.value[1--2]) ;
}
# Insert the given value at the given index, one based. The values at and to
# the right of the index are pushed back to make room for the new value.
# If the index is passed the end of the vector the element is added to the
# end.
#
rule insert (
index # The index to insert at, one based.
: value # The value to insert.
)
{
local left = $(self.value[1-$(index)]) ;
local right = $(self.value[$(index)-]) ;
if $(right)-is-not-empty
{
left = $(left[1--2]) ;
}
self.value = $(left) $(value) $(right) ;
}
# Remove one or more elements from the vector. The range is inclusive, and
# not specifying an end is equivalent to the [start, start] range.
#
rule erase (
start # Index of first element to remove.
end ? # Optional, index of last element to remove.
)
{
end ?= $(start) ;
local left = $(self.value[1-$(start)]) ;
left = $(left[1--2]) ;
local right = $(self.value[$(end)-]) ;
right = $(right[2-]) ;
self.value = $(left) $(right) ;
}
# Remove all elements from the vector.
#
rule clear ( )
{
self.value = ;
}
# The number of elements in the vector.
#
rule size ( )
{
return [ sequence.length $(self.value) ] ;
}
# Returns "true" if there are NO elements in the vector, empty otherwise.
#
rule empty ( )
{
if ! $(self.value)-is-not-empty
{
return true ;
}
}
# Returns the textual representation of content.
#
rule str ( )
{
return "[" [ sequence.transform utility.str : $(self.value) ] "]" ;
}
# Sorts the vector inplace, calling 'utility.less' for comparisons.
#
rule sort ( )
{
self.value = [ sequence.insertion-sort $(self.value) : utility.less ] ;
}
# Returns true if content is equal to the content of other vector. Uses
# 'utility.equal' for comparison.
#
rule equal ( another )
{
local mismatch ;
local size = [ size ] ;
if $(size) = [ $(another).size ]
{
for local i in [ numbers.range 1 $(size) ]
{
if ! [ utility.equal [ at $(i) ] [ $(another).at $(i) ] ]
{
mismatch = true ;
}
}
}
else
{
mismatch = true ;
}
if ! $(mismatch)
{
return true ;
}
}
}
rule __test__ ( )
{
import assert ;
import "class" : new ;
local v1 = [ new vector ] ;
assert.true $(v1).equal $(v1) ;
assert.true $(v1).empty ;
assert.result 0 : $(v1).size ;
assert.result "[" "]" : $(v1).str ;
$(v1).push-back b ;
$(v1).push-front a ;
assert.result "[" a b "]" : $(v1).str ;
assert.result a : $(v1).front ;
assert.result b : $(v1).back ;
$(v1).insert 2 : d ;
$(v1).insert 2 : c ;
$(v1).insert 4 : f ;
$(v1).insert 4 : e ;
$(v1).pop-back ;
assert.result 5 : $(v1).size ;
assert.result d : $(v1).at 3 ;
$(v1).pop-front ;
assert.result c : $(v1).front ;
assert.false $(v1).empty ;
$(v1).erase 3 4 ;
assert.result 2 : $(v1).size ;
local v2 = [ new vector q w e r t y ] ;
assert.result 6 : $(v2).size ;
$(v1).push-back $(v2) ;
assert.result 3 : $(v1).size ;
local v2-alias = [ $(v1).back ] ;
assert.result e : $(v2-alias).at 3 ;
$(v1).clear ;
assert.true $(v1).empty ;
assert.false $(v2-alias).empty ;
$(v2).pop-back ;
assert.result t : $(v2-alias).back ;
local v3 = [ new vector ] ;
$(v3).push-back [ new vector 1 2 3 4 5 ] ;
$(v3).push-back [ new vector a b c ] ;
assert.result "[" "[" 1 2 3 4 5 "]" "[" a b c "]" "]" : $(v3).str ;
$(v3).push-back [ new vector [ new vector x y z ] [ new vector 7 8 9 ] ] ;
assert.result 1 : $(v3).at 1 : 1 ;
assert.result b : $(v3).at 2 : 2 ;
assert.result a b c : $(v3).get-at 2 ;
assert.result 7 8 9 : $(v3).get-at 3 : 2 ;
local v4 = [ new vector 4 3 6 ] ;
$(v4).sort ;
assert.result 3 4 6 : $(v4).get ;
assert.false $(v4).equal $(v3) ;
local v5 = [ new vector 3 4 6 ] ;
assert.true $(v4).equal $(v5) ;
# Check that vectors of different sizes are considered non-equal.
$(v5).pop-back ;
assert.false $(v4).equal $(v5) ;
local v6 = [ new vector [ new vector 1 2 3 ] ] ;
assert.true $(v6).equal [ new vector [ new vector 1 2 3 ] ] ;
local v7 = [ new vector 111 222 333 ] ;
assert.true $(v7).equal $(v7) ;
$(v7).insert 4 : 444 ;
assert.result 111 222 333 444 : $(v7).get ;
$(v7).insert 999 : xxx ;
assert.result 111 222 333 444 xxx : $(v7).get ;
local v8 = [ new vector "" "" "" ] ;
assert.true $(v8).equal $(v8) ;
assert.false $(v8).empty ;
assert.result 3 : $(v8).size ;
assert.result "" : $(v8).at 1 ;
assert.result "" : $(v8).at 2 ;
assert.result "" : $(v8).at 3 ;
assert.result : $(v8).at 4 ;
$(v8).insert 2 : 222 ;
assert.result 4 : $(v8).size ;
assert.result "" 222 "" "" : $(v8).get ;
$(v8).insert 999 : "" ;
assert.result 5 : $(v8).size ;
assert.result "" 222 "" "" "" : $(v8).get ;
$(v8).insert 999 : xxx ;
assert.result 6 : $(v8).size ;
assert.result "" 222 "" "" "" xxx : $(v8).get ;
# Regression test for a bug causing vector.equal to compare only the first
# and the last element in the given vectors.
local v9 = [ new vector 111 xxx 222 ] ;
local v10 = [ new vector 111 yyy 222 ] ;
assert.false $(v9).equal $(v10) ;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
# Copyright 2003 Dave Abrahams
# Copyright 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)
import modules ;
import numbers ;
# The pattern that indirect rules must match: module%rule
.pattern = "^([^%]*)%([^%]+)$" ;
#
# Type checking rules.
#
local rule indirect-rule ( x )
{
if ! [ MATCH $(.pattern) : $(x) ]
{
return "expected a string of the form module%rule, but got \""$(x)"\" for argument" ;
}
}
# Make an indirect rule which calls the given rule. If context is supplied it is
# expected to be the module in which to invoke the rule by the 'call' rule
# below. Otherwise, the rule will be invoked in the module of this rule's
# caller.
#
rule make ( rulename bound-args * : context ? )
{
if [ MATCH $(.pattern) : $(rulename) ]
{
return $(rulename) $(bound-args) ;
}
else
{
context ?= [ CALLER_MODULE ] ;
context ?= "" ;
return $(context)%$(rulename) $(bound-args) ;
}
}
# Make an indirect rule which calls the given rule. 'rulename' may be a
# qualified rule; if so it is returned unchanged. Otherwise, if frames is not
# supplied, the result will be invoked (by 'call', below) in the module of the
# caller. Otherwise, frames > 1 specifies additional call frames to back up in
# order to find the module context.
#
rule make-qualified ( rulename bound-args * : frames ? )
{
if [ MATCH $(.pattern) : $(rulename) ]
{
return $(rulename) $(bound-args) ;
}
else
{
frames ?= 1 ;
# If the rule name includes a Jamfile module, grab it.
local module-context = [ MATCH "^(Jamfile<[^>]*>)\\..*" : $(rulename) ] ;
if ! $(module-context)
{
# Take the first dot-separated element as module name. This disallows
# module names with dots, but allows rule names with dots.
module-context = [ MATCH "^([^.]*)\\..*" : $(rulename) ] ;
}
module-context ?= [ CALLER_MODULE $(frames) ] ;
return [ make $(rulename) $(bound-args) : $(module-context) ] ;
}
}
# Returns the module name in which the given indirect rule will be invoked.
#
rule get-module ( [indirect-rule] x )
{
local m = [ MATCH $(.pattern) : $(x) ] ;
if ! $(m[1])
{
m = ;
}
return $(m[1]) ;
}
# Returns the rulename that will be called when x is invoked.
#
rule get-rule ( [indirect-rule] x )
{
local m = [ MATCH $(.pattern) : $(x) ] ;
return $(m[2]) ;
}
# Invoke the given indirect-rule.
#
rule call ( [indirect-rule] r args * : * )
{
return [ modules.call-in [ get-module $(r) ] : [ get-rule $(r) ] $(args) :
$(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) :
$(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) ] ;
}
.parts_regex = "^([^@]*)@" "([^%]*)%" "([^%]+)$" ;
.parts_regex = $(.parts_regex:J=) ;
# Get the three parts of an indirect reference in a feature.
#
rule parts ( x )
{
return [ MATCH "$(.parts_regex)" : $(x) ] ;
}
# Compute teh difference between two lists containing indirect references.
# The context of the references are not considered for equality comparisons.
#
rule difference ( s1 * : s2 * )
{
local result ;
local s2-min ;
for local s2i in $(s2)
{
local m = [ parts $(s2i) ] ;
if $(m)
{
s2-min += "$(m[1])@%$(m[3])" ;
}
}
for local s1i in $(s1)
{
local m = [ parts $(s1i) ] ;
if $(m)
{
if ! ( $(m[1])@%$(m[3]) in $(s2-min) )
{
result += $(s1i) ;
}
}
else
{
result += $(s1i) ;
}
}
return $(result) ;
}
rule __test__
{
import assert ;
rule foo-barr! ( x )
{
assert.equal $(x) : x ;
}
assert.equal [ get-rule [ make foo-barr! ] ] : foo-barr! ;
assert.equal [ get-module [ make foo-barr! ] ] : [ CALLER_MODULE ] ;
call [ make foo-barr! ] x ;
call [ make foo-barr! x ] ;
call [ make foo-barr! : [ CALLER_MODULE ] ] x ;
}

View File

@@ -0,0 +1,15 @@
# Status: minimally ported. This module is not supposed to be used much
# with Boost.Build/Python.
#
# Copyright 2003 Dave Abrahams
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
from b2.util import call_jam_function, bjam_signature
def call(*args):
a1 = args[0]
name = a1[0]
a1tail = a1[1:]
call_jam_function(name, *((a1tail,) + args[1:]))

View File

@@ -0,0 +1,46 @@
# Copyright Pedro Ferreira 2005. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE.txt or copy at https://www.bfgroup.xyz/b2/LICENSE.txt)
import sys
class NullLogger:
def __init__ (self):
self.indent_ = ''
def log (self, source_name, *args):
if self.on () and self.interesting (source_name):
self.do_log (self.indent_)
for i in args:
self.do_log (i)
self.do_log ('\n')
def increase_indent (self):
if self.on ():
self.indent_ += ' '
def decrease_indent (self):
if self.on () and len (self.indent_) > 4:
self.indent_ = self.indent_ [-4:]
def do_log (self, *args):
pass
def interesting (self, source_name):
return False
def on (self):
return True
class TextLogger (NullLogger):
def __init__ (self):
NullLogger.__init__ (self)
def do_log (self, arg):
sys.stdout.write (str (arg))
def interesting (self, source_name):
return True
def on (self):
return True

View File

@@ -0,0 +1,241 @@
# Copyright 2021 Nikita Kniazev
# Copyright 2001, 2002 Dave Abrahams
# Copyright 2002, 2003 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 errors ;
rule trim-leading-zeroes ( value )
{
return [ CALC $(value) + 0 ] ;
}
rule check ( numbers * )
{
for local n in $(numbers)
{
switch $(n)
{
case *[^0-9]* :
errors.error $(n) "in" $(numbers) : is not a number ;
}
}
}
rule increment ( number )
{
return [ CALC $(number) + 1 ] ;
}
rule decrement ( number )
{
return [ CALC $(number) - 1 ] ;
}
rule range ( start finish ? : step ? )
{
if ! $(finish)
{
finish = $(start) ;
start = 1 ;
}
step ?= 1 ;
check $(start) $(finish) $(step) ;
if $(finish) != 0
{
local result ;
while [ less $(start) $(finish) ] || $(start) = $(finish)
{
result += $(start) ;
start = [ CALC $(start) + $(step) ] ;
}
return $(result) ;
}
}
rule equal ( n1 n2 )
{
if [ CALC $(n2) - $(n1) ] = 0
{
return true ;
}
return ;
}
rule less ( n1 n2 )
{
switch [ CALC $(n2) - $(n1) ]
{
case [1-9]* : return true ;
}
}
rule log10 ( number )
{
switch $(number)
{
case *[^0-9]* : errors.error $(number) is not a number ;
case 0 : errors.error can't take log of zero ;
case [1-9] : return 0 ;
case [1-9]? : return 1 ;
case [1-9]?? : return 2 ;
case [1-9]??? : return 3 ;
case [1-9]???? : return 4 ;
case [1-9]????? : return 5 ;
case [1-9]?????? : return 6 ;
case [1-9]??????? : return 7 ;
case [1-9]???????? : return 8 ;
case [1-9]????????? : return 9 ;
case * :
{
import sequence ;
import string ;
local chars = [ string.chars $(number) ] ;
while $(chars[1]) = 0
{
chars = $(chars[2-]) ;
}
if ! $(chars)
{
errors.error can't take log of zero ;
}
else
{
return [ decrement [ sequence.length $(chars) ] ] ;
}
}
}
}
rule __test__ ( )
{
import assert ;
assert.result 1 : increment 0 ;
assert.result 2 : increment 1 ;
assert.result 1 : decrement 2 ;
assert.result 0 : decrement 1 ;
assert.result 50 : increment 49 ;
assert.result 49 : decrement 50 ;
assert.result 99 : increment 98 ;
assert.result 99 : decrement 100 ;
assert.result 100 : increment 99 ;
assert.result 999 : decrement 1000 ;
assert.result 1000 : increment 999 ;
assert.result 1 2 3 : range 3 ;
assert.result 1 2 3 4 5 6 7 8 9 10 11 12 : range 12 ;
assert.result 3 4 5 6 7 8 9 10 11 : range 3 11 ;
assert.result : range 0 ;
assert.result 1 4 7 10 : range 10 : 3 ;
assert.result 2 4 6 8 10 : range 2 10 : 2 ;
assert.result 25 50 75 100 : range 25 100 : 25 ;
assert.result 0 : trim-leading-zeroes 0 ;
assert.result 1234 : trim-leading-zeroes 1234 ;
assert.result 123456 : trim-leading-zeroes 0000123456 ;
assert.result 1000123456 : trim-leading-zeroes 1000123456 ;
assert.result 10000 : trim-leading-zeroes 10000 ;
assert.result 10000 : trim-leading-zeroes 00010000 ;
assert.true less 1 2 ;
assert.true less 1 12 ;
assert.true less 1 21 ;
assert.true less 005 217 ;
assert.false less 0 0 ;
assert.false less 03 3 ;
assert.false less 3 03 ;
assert.true less 005 217 ;
assert.true less 0005 217 ;
assert.true less 5 00217 ;
assert.true equal 0 0 ;
assert.true equal 00 0 ;
assert.true equal 0 00 ;
assert.true equal 00 00 ;
assert.true equal 00 000 ;
assert.true equal 123 123 ;
assert.true equal 0123 123 ;
assert.true equal 123 0123 ;
assert.false equal 0 1 ;
assert.false equal 123 124 ;
assert.false equal 124 123 ;
# TEMPORARY disabled, because nested "try"/"catch" do not work and I do no
# have the time to fix that right now.
if $(0)
{
try ;
{
decrement 0 ;
}
catch can't decrement zero! ;
try ;
{
check foo ;
}
catch : not a number ;
try ;
{
increment foo ;
}
catch : not a number ;
try ;
{
log10 0 ;
}
catch can't take log of zero ;
try ;
{
log10 000 ;
}
catch can't take log of zero ;
}
assert.result 0 : log10 1 ;
assert.result 0 : log10 9 ;
assert.result 1 : log10 10 ;
assert.result 1 : log10 99 ;
assert.result 2 : log10 100 ;
assert.result 2 : log10 101 ;
assert.result 2 : log10 125 ;
assert.result 2 : log10 999 ;
assert.result 3 : log10 1000 ;
assert.result 10 : log10 12345678901 ;
for local x in [ range 75 110 : 5 ]
{
for local y in [ range $(x) 111 : 3 ]
{
if $(x) != $(y)
{
assert.true less $(x) $(y) ;
}
}
}
for local x in [ range 90 110 : 2 ]
{
for local y in [ range 80 $(x) : 4 ]
{
assert.false less $(x) $(y) ;
}
}
}

View File

@@ -0,0 +1,109 @@
# Copyright (c) 2005 Vladimir Prus.
#
# Use, modification and distribution is subject to the Boost Software
# License Version 1.0. (See accompanying file LICENSE.txt or
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import modules ;
# Set a value for a named option, to be used when not overridden on the command
# line.
rule set ( name : value ? )
{
.option.$(name) = $(value) ;
}
rule get ( name : default-value ? : implied-value ? )
{
local m = [ MATCH --$(name)=(.*) : [ modules.peek : ARGV ] ] ;
if $(m)
{
return $(m[1]) ;
}
else
{
m = [ MATCH (--$(name)) : [ modules.peek : ARGV ] ] ;
if $(m) && $(implied-value)
{
return $(implied-value) ;
}
else if $(.option.$(name))
{
return $(.option.$(name)) ;
}
else
{
return $(default-value) ;
}
}
}
# Check command-line args as soon as possible. For each option try to load
# module named after option. Is that succeeds, invoke 'process' rule in the
# module. The rule may return "true" to indicate that the regular build process
# should not be attempted.
#
# Options take the general form of: --<name>[=<value>] [<value>]
#
rule process ( )
{
local ARGV = [ modules.peek : ARGV ] ;
local BOOST_BUILD_PATH = [ modules.peek : BOOST_BUILD_PATH ] ;
local dont-build ;
local args = $(ARGV) ;
while $(args)
{
local arg = [ MATCH ^--(.*) : $(args[1]) ] ;
while $(args[2-]) && ! $(arg)
{
args = $(args[2-]) ;
arg = [ MATCH ^--(.*) : $(args[1]) ] ;
}
args = $(args[2-]) ;
if $(arg)
{
local split = [ MATCH "^(([^-=]+)[^=]*)(=?)(.*)$" : $(arg) ] ;
local full-name = $(split[1]) ;
local prefix = $(split[2]) ;
local values ;
if $(split[3])
{
values = $(split[4]) ;
}
if $(args) && ! [ MATCH ^(--).* : $(args[1]) ]
{
values += $(args[1]) ;
args = $(args[2-]) ;
}
# Jook in options subdirectories of BOOST_BUILD_PATH for modules
# matching the full option name and then its prefix.
local plugin-dir = options ;
local option-files = [ GLOB $(plugin-dir:D=$(BOOST_BUILD_PATH)) :
$(full-name).jam $(prefix).jam ] ;
if $(option-files)
{
# Load the file into a module named for the option.
local f = $(option-files[1]) ;
local module-name = --$(f:D=:S=) ;
modules.load $(module-name) : $(f:D=) : $(f:D) ;
# If there is a process rule, call it with the full option name
# and its value (if any). If there was no "=" in the option, the
# value will be empty.
if process in [ RULENAMES $(module-name) ]
{
dont-build += [ modules.call-in $(module-name) : process
--$(full-name) : $(values) ] ;
}
}
}
}
return $(dont-build) ;
}

View File

@@ -0,0 +1,35 @@
# Copyright (c) 2005-2010 Vladimir Prus.
#
# Use, modification and distribution is subject to the Boost Software
# License Version 1.0. (See accompanying file LICENSE.txt or
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import sys
import re
import b2.util.regex
options = {}
# Set a value for a named option, to be used when not overridden on the command
# line.
def set(name, value=None):
global options
options[name] = value
def get(name, default_value=None, implied_value=None):
global options
matches = b2.util.regex.transform(sys.argv, "--" + re.escape(name) + "=(.*)")
if matches:
return matches[-1]
else:
m = b2.util.regex.transform(sys.argv, "--(" + re.escape(name) + ")")
if m and implied_value:
return implied_value
elif options.get(name) is not None:
return options[name]
else:
return default_value

View File

@@ -0,0 +1,173 @@
# Copyright (C) 2003 Vladimir Prus
# Use, modification, and distribution is subject to the Boost Software
# License, Version 1.0. (See accompanying file LICENSE.txt or copy
# at https://www.bfgroup.xyz/b2/LICENSE.txt)
# This module defines a class which allows to order arbitrary object with
# regard to arbitrary binary relation.
#
# The primary use case is the gcc toolset, which is sensitive to library order:
# if library 'a' uses symbols from library 'b', then 'a' must be present before
# 'b' on the linker's command line.
#
# This requirement can be lifted for gcc with GNU ld, but for gcc with Solaris
# LD (and for Solaris toolset as well), the order always matters.
#
# So, we need to store order requirements and then order libraries according to
# them. It is not possible to use the dependency graph as order requirements.
# What we need is a "use symbols" relationship while dependency graph provides
# the "needs to be updated" relationship.
#
# For example::
# lib a : a.cpp b;
# lib b ;
#
# For static linking, library 'a' need not depend on 'b'. However, it should
# still come before 'b' on the command line.
class order
{
rule __init__ ( )
{
}
# Adds the constraint that 'first' should preceede 'second'.
rule add-pair ( first second )
{
.constraits += $(first)--$(second) ;
}
NATIVE_RULE class@order : add-pair ;
# Given a list of objects, reorder them so that the constraints specified by
# 'add-pair' are satisfied.
#
# The algorithm was adopted from an awk script by Nikita Youshchenko
# (yoush at cs dot msu dot su)
rule order ( objects * )
{
# The algorithm used is the same is standard transitive closure, except
# that we're not keeping in-degree for all vertices, but rather removing
# edges.
local result ;
if $(objects)
{
local constraints = [ eliminate-unused-constraits $(objects) ] ;
# Find some library that nobody depends upon and add it to the
# 'result' array.
local obj ;
while $(objects)
{
local new_objects ;
while $(objects)
{
obj = $(objects[1]) ;
if [ has-no-dependents $(obj) : $(constraints) ]
{
# Emulate break ;
new_objects += $(objects[2-]) ;
objects = ;
}
else
{
new_objects += $(obj) ;
obj = ;
objects = $(objects[2-]) ;
}
}
if ! $(obj)
{
errors.error "Circular order dependencies" ;
}
# No problem with placing first.
result += $(obj) ;
# Remove all constraints where 'obj' comes first, since they are
# already satisfied.
constraints = [ remove-satisfied $(constraints) : $(obj) ] ;
# Add the remaining objects for further processing on the next
# iteration
objects = $(new_objects) ;
}
}
return $(result) ;
}
NATIVE_RULE class@order : order ;
# Eliminate constraints which mention objects not in 'objects'. In
# graph-theory terms, this is finding a subgraph induced by ordered
# vertices.
rule eliminate-unused-constraits ( objects * )
{
local result ;
for local c in $(.constraints)
{
local m = [ MATCH (.*)--(.*) : $(c) ] ;
if $(m[1]) in $(objects) && $(m[2]) in $(objects)
{
result += $(c) ;
}
}
return $(result) ;
}
# Returns true if there's no constraint in 'constaraints' where 'obj' comes
# second.
rule has-no-dependents ( obj : constraints * )
{
local failed ;
while $(constraints) && ! $(failed)
{
local c = $(constraints[1]) ;
local m = [ MATCH (.*)--(.*) : $(c) ] ;
if $(m[2]) = $(obj)
{
failed = true ;
}
constraints = $(constraints[2-]) ;
}
if ! $(failed)
{
return true ;
}
}
rule remove-satisfied ( constraints * : obj )
{
local result ;
for local c in $(constraints)
{
local m = [ MATCH (.*)--(.*) : $(c) ] ;
if $(m[1]) != $(obj)
{
result += $(c) ;
}
}
return $(result) ;
}
}
rule __test__ ( )
{
import "class" : new ;
import assert ;
c1 = [ new order ] ;
$(c1).add-pair l1 l2 ;
assert.result l1 l2 : $(c1).order l1 l2 ;
assert.result l1 l2 : $(c1).order l2 l1 ;
$(c1).add-pair l2 l3 ;
assert.result l1 l2 : $(c1).order l2 l1 ;
$(c1).add-pair x l2 ;
assert.result l1 l2 : $(c1).order l2 l1 ;
assert.result l1 l2 l3 : $(c1).order l2 l3 l1 ;
# The output should be stable for unconstrained
# elements.
assert.result l4 l5 : $(c1).order l4 l5 ;
}

View File

@@ -0,0 +1,121 @@
# Copyright (C) 2003 Vladimir Prus
# Use, modification, and distribution is subject to the Boost Software
# License, Version 1.0. (See accompanying file LICENSE.txt or copy
# at https://www.bfgroup.xyz/b2/LICENSE.txt)
class Order:
"""Allows ordering arbitrary objects with regard to arbitrary binary relation.
The primary use case is the gcc toolset, which is sensitive to
library order: if library 'a' uses symbols from library 'b',
then 'a' must be present before 'b' on the linker's command line.
This requirement can be lifted for gcc with GNU ld, but for gcc with
Solaris LD (and for Solaris toolset as well), the order always matters.
So, we need to store order requirements and then order libraries
according to them. It it not possible to use dependency graph as
order requirements. What we need is "use symbols" relationship
while dependency graph provides "needs to be updated" relationship.
For example::
lib a : a.cpp b;
lib b ;
For static linking, the 'a' library need not depend on 'b'. However, it
still should come before 'b' on the command line.
"""
def __init__ (self):
self.constraints_ = []
def add_pair (self, first, second):
""" Adds the constraint that 'first' should precede 'second'.
"""
self.constraints_.append ((first, second))
def order (self, objects):
""" Given a list of objects, reorder them so that the constains specified
by 'add_pair' are satisfied.
The algorithm was adopted from an awk script by Nikita Youshchenko
(yoush at cs dot msu dot su)
"""
# The algorithm used is the same is standard transitive closure,
# except that we're not keeping in-degree for all vertices, but
# rather removing edges.
result = []
if not objects:
return result
constraints = self.__eliminate_unused_constraits (objects)
# Find some library that nobody depends upon and add it to
# the 'result' array.
obj = None
while objects:
new_objects = []
while objects:
obj = objects [0]
if self.__has_no_dependents (obj, constraints):
# Emulate break ;
new_objects.extend (objects [1:])
objects = []
else:
new_objects.append (obj)
obj = None
objects = objects [1:]
if not obj:
raise BaseException ("Circular order dependencies")
# No problem with placing first.
result.append (obj)
# Remove all contains where 'obj' comes first,
# since they are already satisfied.
constraints = self.__remove_satisfied (constraints, obj)
# Add the remaining objects for further processing
# on the next iteration
objects = new_objects
return result
def __eliminate_unused_constraits (self, objects):
""" Eliminate constraints which mention objects not in 'objects'.
In graph-theory terms, this is finding subgraph induced by
ordered vertices.
"""
result = []
for c in self.constraints_:
if c [0] in objects and c [1] in objects:
result.append (c)
return result
def __has_no_dependents (self, obj, constraints):
""" Returns true if there's no constraint in 'constraints' where
'obj' comes second.
"""
failed = False
while constraints and not failed:
c = constraints [0]
if c [1] == obj:
failed = True
constraints = constraints [1:]
return not failed
def __remove_satisfied (self, constraints, obj):
result = []
for c in constraints:
if c [0] != obj:
result.append (c)
return result

View File

@@ -0,0 +1,208 @@
# Copyright 2001, 2002, 2003, 2005 Dave Abrahams
# Copyright 2006 Rene Rivera
# Copyright 2003, 2005 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 modules ;
import string ;
# Return the value(s) of the given environment variable(s) at the time bjam was
# invoked.
rule environ ( variable-names + )
{
local result ;
for local var-name in $(variable-names)
{
# We check the various cases of the var name for a value to account
# for programs that change the casing of env vars. One such program
# is Python that upper-cases env var names on import, and resports
# them as upper-case instead of keeping the original case.
local value ;
value ?= [ modules.peek .ENVIRON : $(var-name) ] ;
value ?= [ modules.peek .ENVIRON : $(var-name:U) ] ;
value ?= [ modules.peek .ENVIRON : $(var-name:L) ] ;
result += $(value) ;
}
return $(result) ;
}
.name = [ modules.peek : OS ] ;
.platform = [ modules.peek : OSPLAT ] ;
.version = [ modules.peek : OSVER ] ;
local rule constant ( c : os ? )
{
os ?= $(.name) ;
# First look for a platform-specific name, then the general value.
local variables = .$(c)-$(os) .$(c) ;
local result = $($(variables)) ;
return $(result[1]) ;
}
rule get-constant ( os ? )
{
# Find the name of the constant being accessed, which is equal to the name
# used to invoke us.
local bt = [ BACKTRACE 1 ] ;
local rulename = [ MATCH "([^.]*)$" : $(bt[4]) ] ;
return [ constant $(rulename) : $(os) ] ;
}
# export all the common constants
.constants = name platform version shared-library-path-variable path-separator executable-path-variable executable-suffix ;
for local constant in $(.constants)
{
IMPORT $(__name__) : get-constant : $(__name__) : $(constant) ;
}
EXPORT $(__name__) : $(.constants) ;
.executable-path-variable-NT = PATH ;
# On Windows the case and capitalization of PATH is not always predictable, so
# let's find out what variable name was really set.
if $(.name) = NT
{
for local n in [ VARNAMES .ENVIRON ]
{
if $(n:L) = path
{
.executable-path-variable-NT = $(n) ;
}
}
}
# Specific constants for various platforms. There's no need to define any
# constant whose value would be the same as the default, below.
.shared-library-path-variable-NT = $(.executable-path-variable-NT) ;
.path-separator-NT = ";" ;
.path-separator-VXWORKS = ";" ;
.expand-variable-prefix-NT = % ;
.expand-variable-suffix-NT = % ;
.executable-suffix-NT = .exe ;
.shared-library-path-variable-CYGWIN = PATH ;
.shared-library-path-variable-MACOSX = DYLD_LIBRARY_PATH ;
.shared-library-path-variable-AIX = LIBPATH ;
.shared-library-path-variable-HAIKU = LIBRARY_PATH ;
.shared-library-path-variable-VMS = PATH ;
.path-separator-VMS = "," ;
.expand-variable-prefix-VMS = '' ;
.expand-variable-suffix-VMS = ' ;
.executable-suffix-VMS = .exe ;
# VxWorks uses the default LD_LIBRARY_PATH, but we need an alternate
# name on the cross build host to propagate to the target system
.shared-library-path-variable-VXWORKS = VSB_LD_LIBRARY_PATH ;
# Default constants
.shared-library-path-variable = LD_LIBRARY_PATH ;
.path-separator = ":" ;
.expand-variable-prefix = $ ;
.expand-variable-suffix = "" ;
.executable-path-variable = PATH ;
.executable-suffix = "" ;
# Return a list of the directories in the PATH. Yes, that information is (sort
# of) available in the global module, but jam code can change those values, and
# it isn't always clear what case/capitalization to use when looking. This rule
# is a more reliable way to get there.
rule executable-path ( )
{
return [ string.words [ environ [ constant executable-path-variable ] ]
: [ constant path-separator ] ] ;
}
# Initialize the list of home directories for the current user depending on the
# OS.
if $(.name) = NT
{
local home = [ environ HOMEDRIVE HOMEPATH ] ;
.home-directories = $(home[1])$(home[2]) [ environ HOME ] [ environ USERPROFILE ] ;
}
else
{
.home-directories = [ environ HOME ] ;
}
# Can't use 'constant' mechanism because it only returns 1-element values.
rule home-directories ( )
{
return $(.home-directories) ;
}
# Return the string needed to represent the expansion of the named shell
# variable.
rule expand-variable ( variable )
{
local prefix = [ constant expand-variable-prefix ] ;
local suffix = [ constant expand-variable-suffix ] ;
return $(prefix)$(variable)$(suffix) ;
}
# Returns true if running on windows, whether in cygwin or not.
rule on-windows ( )
{
local result ;
if [ modules.peek : NT ]
{
result = true ;
}
else if [ modules.peek : UNIX ]
{
switch [ modules.peek : JAMUNAME ]
{
case CYGWIN* :
{
result = true ;
}
}
}
return $(result) ;
}
rule on-vms ( )
{
local result ;
if [ modules.peek : VMS ]
{
result = true ;
}
return $(result) ;
}
if ! [ on-windows ] && ! [ on-vms ]
{
.on-unix = 1 ;
}
rule on-unix
{
return $(.on-unix) ;
}
rule __test__
{
import assert ;
if ! ( --quiet in [ modules.peek : ARGV ] )
{
ECHO "os:" name= [ name ] ;
ECHO "os:" version= [ version ] ;
}
assert.true name ;
}

View File

@@ -0,0 +1,24 @@
# Status: stub, just enough to make tests work.
#
# Named os_j to avoid conflicts with standard 'os'. See
# project.py:import for special-casing.
#
# Copyright 2001, 2002, 2003, 2005 Dave Abrahams
# Copyright 2006 Rene Rivera
# Copyright 2003, 2005 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 os
import bjam
__OS = bjam.call("peek", [], "OS")[0]
# Return Jam's name of OS to prevent existing code from burning
# when faced with Python naming
def name():
return __OS
def environ(keys):
return [os.environ[key] for key in keys if key in os.environ]

View File

@@ -0,0 +1,54 @@
# Copyright 2018 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Named parameters are represented as a list which has the
# argument name as the first element and the value as the
# remaining elements. This function sorts the parameters
# into the correct variables and removes the parameter names.
#
# Example::
#
# rule exe ( name : sources * : requirements * )
# {
# param.handle-named-params sources requirements ;
# # At this point $(sources) is test.cpp
# }
# exe test : requirements <link>shared : sources test.cpp ;
#
rule handle-named-params ( parameter-names * )
{
module [ CALLER_MODULE ]
{
# Uglify the variable names, because we're executing in an unknown module.
local found-8bef5c096d06a1b0 ;
local tmp-8bef5c096d06a1b0.$(1) ;
for local v-8bef5c096d06a1b0 in $(1)
{
if $($(v-8bef5c096d06a1b0)[1]) && $($(v-8bef5c096d06a1b0)[1]) in $(1)
{
if $(tmp-8bef5c096d06a1b0.$($(v-8bef5c096d06a1b0)[1]))
{
import errors ;
errors.error Parameter '$($(v-8bef5c096d06a1b0)[1])' passed more than once. ;
}
found-8bef5c096d06a1b0 = true ;
tmp-8bef5c096d06a1b0.$($(v-8bef5c096d06a1b0)[1]) = $($(v-8bef5c096d06a1b0)[2-]) ;
}
else if $($(v-8bef5c096d06a1b0))-is-defined
{
if $(found-8bef5c096d06a1b0)
{
import errors ;
errors.error "Positional arguments must appear first." ;
}
tmp-8bef5c096d06a1b0.$(v-8bef5c096d06a1b0) = $($(v-8bef5c096d06a1b0)) ;
}
}
for local v-8bef5c096d06a1b0 in $(1)
{
$(v-8bef5c096d06a1b0) = $(tmp-8bef5c096d06a1b0.$(v-8bef5c096d06a1b0)) ;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,937 @@
# Status: this module is ported on demand by however needs something
# from it. Functionality that is not needed by Python port will
# be dropped.
# Copyright (C) Vladimir Prus 2002. 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.
# Performs various path manipulations. Path are always in a 'normilized'
# representation. In it, a path may be either:
#
# - '.', or
#
# - ['/'] [ ( '..' '/' )* (token '/')* token ]
#
# In plain english, path can be rooted, '..' elements are allowed only
# at the beginning, and it never ends in slash, except for path consisting
# of slash only.
import os.path
from utility import to_seq
from glob import glob as builtin_glob
from b2.util import bjam_signature
@bjam_signature((["path", "root"],))
def root (path, root):
""" If 'path' is relative, it is rooted at 'root'. Otherwise, it's unchanged.
"""
if os.path.isabs (path):
return path
else:
return os.path.join (root, path)
@bjam_signature((["native"],))
def make (native):
""" Converts the native path into normalized form.
"""
# TODO: make os selection here.
return make_UNIX (native)
@bjam_signature([['native']])
def make_UNIX (native):
# VP: I have no idea now 'native' can be empty here! But it can!
assert (native)
return os.path.normpath (native)
@bjam_signature((["path"],))
def native (path):
""" Builds a native representation of the path.
"""
# TODO: make os selection here.
return native_UNIX (path)
def native_UNIX (path):
return path
def pwd ():
""" Returns the current working directory.
# TODO: is it a good idea to use the current dir? Some use-cases
may not allow us to depend on the current dir.
"""
return make (os.getcwd ())
def is_rooted (path):
""" Tests if a path is rooted.
"""
return path and path [0] == '/'
###################################################################
# Still to port.
# Original lines are prefixed with "# "
#
# # Copyright (C) Vladimir Prus 2002. 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.
#
# # Performs various path manipulations. Path are always in a 'normilized'
# # representation. In it, a path may be either:
# #
# # - '.', or
# #
# # - ['/'] [ ( '..' '/' )* (token '/')* token ]
# #
# # In plain english, path can be rooted, '..' elements are allowed only
# # at the beginning, and it never ends in slash, except for path consisting
# # of slash only.
#
# import modules ;
# import sequence ;
# import regex ;
# import errors : error ;
#
#
# os = [ modules.peek : OS ] ;
# if [ modules.peek : UNIX ]
# {
# local uname = [ modules.peek : JAMUNAME ] ;
# switch $(uname)
# {
# case CYGWIN* :
# os = CYGWIN ;
#
# case * :
# os = UNIX ;
# }
# }
#
# #
# # Tests if a path is rooted.
# #
# rule is-rooted ( path )
# {
# return [ MATCH "^(/)" : $(path) ] ;
# }
#
# #
# # Tests if a path has a parent.
# #
# rule has-parent ( path )
# {
# if $(path) != / {
# return 1 ;
# } else {
# return ;
# }
# }
#
# #
# # Returns the path without any directory components.
# #
# rule basename ( path )
# {
# return [ MATCH "([^/]+)$" : $(path) ] ;
# }
#
# #
# # Returns parent directory of the path. If no parent exists, error is issued.
# #
# rule parent ( path )
# {
# if [ has-parent $(path) ] {
#
# if $(path) = . {
# return .. ;
# } else {
#
# # Strip everything at the end of path up to and including
# # the last slash
# local result = [ regex.match "((.*)/)?([^/]+)" : $(path) : 2 3 ] ;
#
# # Did we strip what we shouldn't?
# if $(result[2]) = ".." {
# return $(path)/.. ;
# } else {
# if ! $(result[1]) {
# if [ is-rooted $(path) ] {
# result = / ;
# } else {
# result = . ;
# }
# }
# return $(result[1]) ;
# }
# }
# } else {
# error "Path '$(path)' has no parent" ;
# }
# }
#
# #
# # Returns path2 such that "[ join path path2 ] = .".
# # The path may not contain ".." element or be rooted.
# #
# rule reverse ( path )
# {
# if $(path) = .
# {
# return $(path) ;
# }
# else
# {
# local tokens = [ regex.split $(path) "/" ] ;
# local tokens2 ;
# for local i in $(tokens) {
# tokens2 += .. ;
# }
# return [ sequence.join $(tokens2) : "/" ] ;
# }
# }
def reverse(path):
"""Returns path2 such that `os.path.join(path, path2) == '.'`.
`path` may not contain '..' or be rooted.
Args:
path (str): the path to reverse
Returns:
the string of the reversed path
Example:
>>> p1 = 'path/to/somewhere'
>>> p2 = reverse('path/to/somewhere')
>>> p2
'../../..'
>>> os.path.normpath(os.path.join(p1, p2))
'.'
"""
if is_rooted(path) or '..' in path:
from b2.manager import get_manager
get_manager().errors()(
'reverse(path): path is either rooted or contains ".." in the path')
if path == '.':
return path
path = os.path.normpath(path)
# os.sep.join() is being used over os.path.join() due
# to an extra '..' that is created by os.path.join()
return os.sep.join('..' for t in path.split(os.sep))
# #
# # Auxiliary rule: does all the semantic of 'join', except for error checking.
# # The error checking is separated because this rule is recursive, and I don't
# # like the idea of checking the same input over and over.
# #
# local rule join-imp ( elements + )
# {
# return [ NORMALIZE_PATH $(elements:J="/") ] ;
# }
#
# #
# # Contanenates the passed path elements. Generates an error if
# # any element other than the first one is rooted.
# #
# rule join ( elements + )
# {
# if ! $(elements[2])
# {
# return $(elements[1]) ;
# }
# else
# {
# for local e in $(elements[2-])
# {
# if [ is-rooted $(e) ]
# {
# error only first element may be rooted ;
# }
# }
# return [ join-imp $(elements) ] ;
# }
# }
def glob (dirs, patterns):
""" Returns the list of files matching the given pattern in the
specified directory. Both directories and patterns are
supplied as portable paths. Each pattern should be non-absolute
path, and can't contain "." or ".." elements. Each slash separated
element of pattern can contain the following special characters:
- '?', which match any character
- '*', which matches arbitrary number of characters.
A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3
if and only if e1 matches p1, e2 matches p2 and so on.
For example:
[ glob . : *.cpp ]
[ glob . : */build/Jamfile ]
"""
# {
# local result ;
# if $(patterns:D)
# {
# # When a pattern has a directory element, we first glob for
# # directory, and then glob for file name is the found directories.
# for local p in $(patterns)
# {
# # First glob for directory part.
# local globbed-dirs = [ glob $(dirs) : $(p:D) ] ;
# result += [ glob $(globbed-dirs) : $(p:D="") ] ;
# }
# }
# else
# {
# # When a pattern has not directory, we glob directly.
# # Take care of special ".." value. The "GLOB" rule simply ignores
# # the ".." element (and ".") element in directory listings. This is
# # needed so that
# #
# # [ glob libs/*/Jamfile ]
# #
# # don't return
# #
# # libs/../Jamfile (which is the same as ./Jamfile)
# #
# # On the other hand, when ".." is explicitly present in the pattern
# # we need to return it.
# #
# for local dir in $(dirs)
# {
# for local p in $(patterns)
# {
# if $(p) != ".."
# {
# result += [ sequence.transform make
# : [ GLOB [ native $(dir) ] : $(p) ] ] ;
# }
# else
# {
# result += [ path.join $(dir) .. ] ;
# }
# }
# }
# }
# return $(result) ;
# }
#
# TODO: (PF) I replaced the code above by this. I think it should work but needs to be tested.
result = []
dirs = to_seq (dirs)
patterns = to_seq (patterns)
splitdirs = []
for dir in dirs:
splitdirs += dir.split (os.pathsep)
for dir in splitdirs:
for pattern in patterns:
p = os.path.join (dir, pattern)
import glob
result.extend (glob.glob (p))
return result
#
# Find out the absolute name of path and returns the list of all the parents,
# starting with the immediate one. Parents are returned as relative names.
# If 'upper_limit' is specified, directories above it will be pruned.
#
def all_parents(path, upper_limit=None, cwd=None):
if not cwd:
cwd = os.getcwd()
path_abs = os.path.join(cwd, path)
if upper_limit:
upper_limit = os.path.join(cwd, upper_limit)
result = []
while path_abs and path_abs != upper_limit:
(head, tail) = os.path.split(path)
path = os.path.join(path, "..")
result.append(path)
path_abs = head
if upper_limit and path_abs != upper_limit:
raise BaseException("'%s' is not a prefix of '%s'" % (upper_limit, path))
return result
# Search for 'pattern' in parent directories of 'dir', up till and including
# 'upper_limit', if it is specified, or till the filesystem root otherwise.
#
def glob_in_parents(dir, patterns, upper_limit=None):
result = []
parent_dirs = all_parents(dir, upper_limit)
for p in parent_dirs:
result = glob(p, patterns)
if result: break
return result
#
# #
# # Assuming 'child' is a subdirectory of 'parent', return the relative
# # path from 'parent' to 'child'
# #
# rule relative ( child parent )
# {
# if $(parent) = "."
# {
# return $(child) ;
# }
# else
# {
# local split1 = [ regex.split $(parent) / ] ;
# local split2 = [ regex.split $(child) / ] ;
#
# while $(split1)
# {
# if $(split1[1]) = $(split2[1])
# {
# split1 = $(split1[2-]) ;
# split2 = $(split2[2-]) ;
# }
# else
# {
# errors.error $(child) is not a subdir of $(parent) ;
# }
# }
# return [ join $(split2) ] ;
# }
# }
#
# # Returns the minimal path to path2 that is relative path1.
# #
# rule relative-to ( path1 path2 )
# {
# local root_1 = [ regex.split [ reverse $(path1) ] / ] ;
# local split1 = [ regex.split $(path1) / ] ;
# local split2 = [ regex.split $(path2) / ] ;
#
# while $(split1) && $(root_1)
# {
# if $(split1[1]) = $(split2[1])
# {
# root_1 = $(root_1[2-]) ;
# split1 = $(split1[2-]) ;
# split2 = $(split2[2-]) ;
# }
# else
# {
# split1 = ;
# }
# }
# return [ join . $(root_1) $(split2) ] ;
# }
# Returns the list of paths which are used by the operating system
# for looking up programs
def programs_path ():
raw = []
names = ['PATH', 'Path', 'path']
for name in names:
raw.append(os.environ.get (name, ''))
result = []
for elem in raw:
if elem:
for p in elem.split(os.path.pathsep):
# it's possible that the user's Path has
# double path separators, thus it is possible
# for p to be an empty string.
if p:
result.append(make(p))
return result
# rule make-NT ( native )
# {
# local tokens = [ regex.split $(native) "[/\\]" ] ;
# local result ;
#
# # Handle paths ending with slashes
# if $(tokens[-1]) = ""
# {
# tokens = $(tokens[1--2]) ; # discard the empty element
# }
#
# result = [ path.join $(tokens) ] ;
#
# if [ regex.match "(^.:)" : $(native) ]
# {
# result = /$(result) ;
# }
#
# if $(native) = ""
# {
# result = "." ;
# }
#
# return $(result) ;
# }
#
# rule native-NT ( path )
# {
# local result = [ MATCH "^/?(.*)" : $(path) ] ;
# result = [ sequence.join [ regex.split $(result) "/" ] : "\\" ] ;
# return $(result) ;
# }
#
# rule make-CYGWIN ( path )
# {
# return [ make-NT $(path) ] ;
# }
#
# rule native-CYGWIN ( path )
# {
# local result = $(path) ;
# if [ regex.match "(^/.:)" : $(path) ] # win absolute
# {
# result = [ MATCH "^/?(.*)" : $(path) ] ; # remove leading '/'
# }
# return [ native-UNIX $(result) ] ;
# }
#
# #
# # split-VMS: splits input native path into
# # device dir file (each part is optional),
# # example:
# #
# # dev:[dir]file.c => dev: [dir] file.c
# #
# rule split-path-VMS ( native )
# {
# local matches = [ MATCH ([a-zA-Z0-9_-]+:)?(\\[[^\]]*\\])?(.*)?$ : $(native) ] ;
# local device = $(matches[1]) ;
# local dir = $(matches[2]) ;
# local file = $(matches[3]) ;
#
# return $(device) $(dir) $(file) ;
# }
#
# #
# # Converts a native VMS path into a portable path spec.
# #
# # Does not handle current-device absolute paths such
# # as "[dir]File.c" as it is not clear how to represent
# # them in the portable path notation.
# #
# # Adds a trailing dot (".") to the file part if no extension
# # is present (helps when converting it back into native path).
# #
# rule make-VMS ( native )
# {
# if [ MATCH ^(\\[[a-zA-Z0-9]) : $(native) ]
# {
# errors.error "Can't handle default-device absolute paths: " $(native) ;
# }
#
# local parts = [ split-path-VMS $(native) ] ;
# local device = $(parts[1]) ;
# local dir = $(parts[2]) ;
# local file = $(parts[3]) ;
# local elems ;
#
# if $(device)
# {
# #
# # rooted
# #
# elems = /$(device) ;
# }
#
# if $(dir) = "[]"
# {
# #
# # Special case: current directory
# #
# elems = $(elems) "." ;
# }
# else if $(dir)
# {
# dir = [ regex.replace $(dir) "\\[|\\]" "" ] ;
# local dir_parts = [ regex.split $(dir) \\. ] ;
#
# if $(dir_parts[1]) = ""
# {
# #
# # Relative path
# #
# dir_parts = $(dir_parts[2--1]) ;
# }
#
# #
# # replace "parent-directory" parts (- => ..)
# #
# dir_parts = [ regex.replace-list $(dir_parts) : - : .. ] ;
#
# elems = $(elems) $(dir_parts) ;
# }
#
# if $(file)
# {
# if ! [ MATCH (\\.) : $(file) ]
# {
# #
# # Always add "." to end of non-extension file
# #
# file = $(file). ;
# }
# elems = $(elems) $(file) ;
# }
#
# local portable = [ path.join $(elems) ] ;
#
# return $(portable) ;
# }
#
# #
# # Converts a portable path spec into a native VMS path.
# #
# # Relies on having at least one dot (".") included in the file
# # name to be able to differentiate it ftom the directory part.
# #
# rule native-VMS ( path )
# {
# local device = "" ;
# local dir = $(path) ;
# local file = "" ;
# local native ;
# local split ;
#
# #
# # Has device ?
# #
# if [ is-rooted $(dir) ]
# {
# split = [ MATCH ^/([^:]+:)/?(.*) : $(dir) ] ;
# device = $(split[1]) ;
# dir = $(split[2]) ;
# }
#
# #
# # Has file ?
# #
# # This is no exact science, just guess work:
# #
# # If the last part of the current path spec
# # includes some chars, followed by a dot,
# # optionally followed by more chars -
# # then it is a file (keep your fingers crossed).
# #
# split = [ regex.split $(dir) / ] ;
# local maybe_file = $(split[-1]) ;
#
# if [ MATCH ^([^.]+\\..*) : $(maybe_file) ]
# {
# file = $(maybe_file) ;
# dir = [ sequence.join $(split[1--2]) : / ] ;
# }
#
# #
# # Has dir spec ?
# #
# if $(dir) = "."
# {
# dir = "[]" ;
# }
# else if $(dir)
# {
# dir = [ regex.replace $(dir) \\.\\. - ] ;
# dir = [ regex.replace $(dir) / . ] ;
#
# if $(device) = ""
# {
# #
# # Relative directory
# #
# dir = "."$(dir) ;
# }
# dir = "["$(dir)"]" ;
# }
#
# native = [ sequence.join $(device) $(dir) $(file) ] ;
#
# return $(native) ;
# }
#
#
# rule __test__ ( ) {
#
# import assert ;
# import errors : try catch ;
#
# assert.true is-rooted "/" ;
# assert.true is-rooted "/foo" ;
# assert.true is-rooted "/foo/bar" ;
# assert.result : is-rooted "." ;
# assert.result : is-rooted "foo" ;
# assert.result : is-rooted "foo/bar" ;
#
# assert.true has-parent "foo" ;
# assert.true has-parent "foo/bar" ;
# assert.true has-parent "." ;
# assert.result : has-parent "/" ;
#
# assert.result "." : basename "." ;
# assert.result ".." : basename ".." ;
# assert.result "foo" : basename "foo" ;
# assert.result "foo" : basename "bar/foo" ;
# assert.result "foo" : basename "gaz/bar/foo" ;
# assert.result "foo" : basename "/gaz/bar/foo" ;
#
# assert.result "." : parent "foo" ;
# assert.result "/" : parent "/foo" ;
# assert.result "foo/bar" : parent "foo/bar/giz" ;
# assert.result ".." : parent "." ;
# assert.result ".." : parent "../foo" ;
# assert.result "../../foo" : parent "../../foo/bar" ;
#
#
# assert.result "." : reverse "." ;
# assert.result ".." : reverse "foo" ;
# assert.result "../../.." : reverse "foo/bar/giz" ;
#
# assert.result "foo" : join "foo" ;
# assert.result "/foo" : join "/" "foo" ;
# assert.result "foo/bar" : join "foo" "bar" ;
# assert.result "foo/bar" : join "foo/giz" "../bar" ;
# assert.result "foo/giz" : join "foo/bar/baz" "../../giz" ;
# assert.result ".." : join "." ".." ;
# assert.result ".." : join "foo" "../.." ;
# assert.result "../.." : join "../foo" "../.." ;
# assert.result "/foo" : join "/bar" "../foo" ;
# assert.result "foo/giz" : join "foo/giz" "." ;
# assert.result "." : join lib2 ".." ;
# assert.result "/" : join "/a" ".." ;
#
# assert.result /a/b : join /a/b/c .. ;
#
# assert.result "foo/bar/giz" : join "foo" "bar" "giz" ;
# assert.result "giz" : join "foo" ".." "giz" ;
# assert.result "foo/giz" : join "foo" "." "giz" ;
#
# try ;
# {
# join "a" "/b" ;
# }
# catch only first element may be rooted ;
#
# local CWD = "/home/ghost/build" ;
# assert.result : all-parents . : . : $(CWD) ;
# assert.result . .. ../.. ../../.. : all-parents "Jamfile" : "" : $(CWD) ;
# assert.result foo . .. ../.. ../../.. : all-parents "foo/Jamfile" : "" : $(CWD) ;
# assert.result ../Work .. ../.. ../../.. : all-parents "../Work/Jamfile" : "" : $(CWD) ;
#
# local CWD = "/home/ghost" ;
# assert.result . .. : all-parents "Jamfile" : "/home" : $(CWD) ;
# assert.result . : all-parents "Jamfile" : "/home/ghost" : $(CWD) ;
#
# assert.result "c/d" : relative "a/b/c/d" "a/b" ;
# assert.result "foo" : relative "foo" "." ;
#
# local save-os = [ modules.peek path : os ] ;
# modules.poke path : os : NT ;
#
# assert.result "foo/bar/giz" : make "foo/bar/giz" ;
# assert.result "foo/bar/giz" : make "foo\\bar\\giz" ;
# assert.result "foo" : make "foo/." ;
# assert.result "foo" : make "foo/bar/.." ;
# assert.result "/D:/My Documents" : make "D:\\My Documents" ;
# assert.result "/c:/boost/tools/build/new/project.jam" : make "c:\\boost\\tools\\build\\test\\..\\new\\project.jam" ;
#
# assert.result "foo\\bar\\giz" : native "foo/bar/giz" ;
# assert.result "foo" : native "foo" ;
# assert.result "D:\\My Documents\\Work" : native "/D:/My Documents/Work" ;
#
# modules.poke path : os : UNIX ;
#
# assert.result "foo/bar/giz" : make "foo/bar/giz" ;
# assert.result "/sub1" : make "/sub1/." ;
# assert.result "/sub1" : make "/sub1/sub2/.." ;
# assert.result "sub1" : make "sub1/." ;
# assert.result "sub1" : make "sub1/sub2/.." ;
# assert.result "/foo/bar" : native "/foo/bar" ;
#
# modules.poke path : os : VMS ;
#
# #
# # Don't really need to poke os before these
# #
# assert.result "disk:" "[dir]" "file" : split-path-VMS "disk:[dir]file" ;
# assert.result "disk:" "[dir]" "" : split-path-VMS "disk:[dir]" ;
# assert.result "disk:" "" "" : split-path-VMS "disk:" ;
# assert.result "disk:" "" "file" : split-path-VMS "disk:file" ;
# assert.result "" "[dir]" "file" : split-path-VMS "[dir]file" ;
# assert.result "" "[dir]" "" : split-path-VMS "[dir]" ;
# assert.result "" "" "file" : split-path-VMS "file" ;
# assert.result "" "" "" : split-path-VMS "" ;
#
# #
# # Special case: current directory
# #
# assert.result "" "[]" "" : split-path-VMS "[]" ;
# assert.result "disk:" "[]" "" : split-path-VMS "disk:[]" ;
# assert.result "" "[]" "file" : split-path-VMS "[]file" ;
# assert.result "disk:" "[]" "file" : split-path-VMS "disk:[]file" ;
#
# #
# # Make portable paths
# #
# assert.result "/disk:" : make "disk:" ;
# assert.result "foo/bar/giz" : make "[.foo.bar.giz]" ;
# assert.result "foo" : make "[.foo]" ;
# assert.result "foo" : make "[.foo.bar.-]" ;
# assert.result ".." : make "[.-]" ;
# assert.result ".." : make "[-]" ;
# assert.result "." : make "[]" ;
# assert.result "giz.h" : make "giz.h" ;
# assert.result "foo/bar/giz.h" : make "[.foo.bar]giz.h" ;
# assert.result "/disk:/my_docs" : make "disk:[my_docs]" ;
# assert.result "/disk:/boost/tools/build/new/project.jam" : make "disk:[boost.tools.build.test.-.new]project.jam" ;
#
# #
# # Special case (adds '.' to end of file w/o extension to
# # disambiguate from directory in portable path spec).
# #
# assert.result "Jamfile." : make "Jamfile" ;
# assert.result "dir/Jamfile." : make "[.dir]Jamfile" ;
# assert.result "/disk:/dir/Jamfile." : make "disk:[dir]Jamfile" ;
#
# #
# # Make native paths
# #
# assert.result "disk:" : native "/disk:" ;
# assert.result "[.foo.bar.giz]" : native "foo/bar/giz" ;
# assert.result "[.foo]" : native "foo" ;
# assert.result "[.-]" : native ".." ;
# assert.result "[.foo.-]" : native "foo/.." ;
# assert.result "[]" : native "." ;
# assert.result "disk:[my_docs.work]" : native "/disk:/my_docs/work" ;
# assert.result "giz.h" : native "giz.h" ;
# assert.result "disk:Jamfile." : native "/disk:Jamfile." ;
# assert.result "disk:[my_docs.work]Jamfile." : native "/disk:/my_docs/work/Jamfile." ;
#
# modules.poke path : os : $(save-os) ;
#
# }
#
#def glob(dir, patterns):
# result = []
# for pattern in patterns:
# result.extend(builtin_glob(os.path.join(dir, pattern)))
# return result
def glob(dirs, patterns, exclude_patterns=None):
"""Returns the list of files matching the given pattern in the
specified directory. Both directories and patterns are
supplied as portable paths. Each pattern should be non-absolute
path, and can't contain '.' or '..' elements. Each slash separated
element of pattern can contain the following special characters:
- '?', which match any character
- '*', which matches arbitrary number of characters.
A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3
if and only if e1 matches p1, e2 matches p2 and so on.
For example:
[ glob . : *.cpp ]
[ glob . : */build/Jamfile ]
"""
assert(isinstance(patterns, list))
assert(isinstance(dirs, list))
if not exclude_patterns:
exclude_patterns = []
else:
assert(isinstance(exclude_patterns, list))
real_patterns = [os.path.join(d, p) for p in patterns for d in dirs]
real_exclude_patterns = [os.path.join(d, p) for p in exclude_patterns
for d in dirs]
inc = [os.path.normpath(name) for p in real_patterns
for name in builtin_glob(p)]
exc = [os.path.normpath(name) for p in real_exclude_patterns
for name in builtin_glob(p)]
return [x for x in inc if x not in exc]
def glob_tree(roots, patterns, exclude_patterns=None):
"""Recursive version of GLOB. Builds the glob of files while
also searching in the subdirectories of the given roots. An
optional set of exclusion patterns will filter out the
matching entries from the result. The exclusions also apply
to the subdirectory scanning, such that directories that
match the exclusion patterns will not be searched."""
if not exclude_patterns:
exclude_patterns = []
result = glob(roots, patterns, exclude_patterns)
subdirs = [s for s in glob(roots, ["*"], exclude_patterns) if s != "." and s != ".." and os.path.isdir(s)]
if subdirs:
result.extend(glob_tree(subdirs, patterns, exclude_patterns))
return result
def glob_in_parents(dir, patterns, upper_limit=None):
"""Recursive version of GLOB which glob sall parent directories
of dir until the first match is found. Returns an empty result if no match
is found"""
assert(isinstance(dir, str))
assert(isinstance(patterns, list))
result = []
absolute_dir = os.path.join(os.getcwd(), dir)
absolute_dir = os.path.normpath(absolute_dir)
while absolute_dir:
new_dir = os.path.split(absolute_dir)[0]
if new_dir == absolute_dir:
break
result = glob([new_dir], patterns)
if result:
break
absolute_dir = new_dir
return result
# The relpath functionality is written by
# Cimarron Taylor
def split(p, rest=[]):
(h,t) = os.path.split(p)
if len(h) < 1: return [t]+rest
if len(t) < 1: return [h]+rest
return split(h,[t]+rest)
def commonpath(l1, l2, common=[]):
if len(l1) < 1: return (common, l1, l2)
if len(l2) < 1: return (common, l1, l2)
if l1[0] != l2[0]: return (common, l1, l2)
return commonpath(l1[1:], l2[1:], common+[l1[0]])
def relpath(p1, p2):
(common,l1,l2) = commonpath(split(p1), split(p2))
p = []
if len(l1) > 0:
p = [ '../' * len(l1) ]
p = p + l2
if p:
return os.path.join( *p )
else:
return "."

View File

@@ -0,0 +1,508 @@
# Copyright 2003 Douglas Gregor
# Copyright 2002, 2003, 2005 Rene Rivera
# Copyright 2002, 2003, 2004, 2005 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Utilities for generating format independent output. Using these
# will help in generation of documentation in at minimum plain/console
# and html.
import modules ;
import numbers ;
import string ;
import regex ;
import "class" ;
import scanner ;
import path ;
import os ;
# The current output target. Defaults to console.
output-target = console ;
# The current output type. Defaults to plain. Other possible values are "html".
output-type = plain ;
# Whitespace.
.whitespace = [ string.whitespace ] ;
# Redirect
.redirect-out = ">" ;
.redirect-append = ">>" ;
if [ os.name ] = VMS
{
.redirect-out = "| TYPE SYS$INPUT /OUT=" ;
.redirect-append = "| APPEND/NEW SYS$INPUT " ;
}
# Set the target and type of output to generate. This sets both the destination
# output and the type of docs to generate to that output. The target can be
# either a file or "console" for echoing to the console. If the type of output
# is not specified it defaults to plain text.
#
rule output (
target # The target file or device; file or "console".
type ? # The type of output; "plain" or "html".
)
{
type ?= plain ;
if $(output-target) != $(target)
{
output-target = $(target) ;
output-type = $(type) ;
if $(output-type) = html
{
text
"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
"<html>"
"<head>"
"</head>"
"<body link=\"#0000ff\" vlink=\"#800080\">"
: true
: prefix ;
text
"</body>"
"</html>"
:
: suffix ;
}
}
}
# Generate a section with a description. The type of output can be controlled by
# the value of the 'output-type' variable.
#
rule section (
name # The name of the section.
description * # A number of description lines.
)
{
if $(output-type) = plain
{
lines [ split-at-words "$(name):" ] ;
lines ;
}
else if $(output-type) = html
{
name = [ escape-html $(name) ] ;
text <h3>$(name)</h3> <p> ;
}
local pre = ;
while $(description)
{
local paragraph = ;
while $(description) && [ string.is-whitespace $(description[1]) ] { description = $(description[2-]) ; }
if $(pre)
{
while $(description) && (
$(pre) = " $(description[1])" ||
( $(pre) < [ string.chars [ MATCH "^([$(.whitespace)]*)" : " $(description[1])" ] ] )
)
{ paragraph += $(description[1]) ; description = $(description[2-]) ; }
while [ string.is-whitespace $(paragraph[-1]) ] { paragraph = $(paragraph[1--2]) ; }
pre = ;
if $(output-type) = plain
{
lines $(paragraph) "" : " " " " ;
}
else if $(output-type) = html
{
text <blockquote> ;
lines $(paragraph) ;
text </blockquote> ;
}
}
else
{
while $(description) && ! [ string.is-whitespace $(description[1]) ]
{ paragraph += $(description[1]) ; description = $(description[2-]) ; }
if $(paragraph[1]) = "::" && ! $(paragraph[2])
{
pre = " " ;
}
if $(paragraph[1]) = "::"
{
if $(output-type) = plain
{
lines $(paragraph[2-]) "" : " " " " ;
lines ;
}
else if $(output-type) = html
{
text <blockquote> ;
lines $(paragraph[2-]) ;
text </blockquote> ;
}
}
else
{
local p = [ MATCH "(.*)(::)$" : $(paragraph[-1]) ] ;
local pws = [ MATCH "([ ]*)$" : $(p[1]) ] ;
p = [ MATCH "(.*)($(pws))($(p[2]))$" : $(paragraph[-1]) ] ;
if $(p[3]) = "::"
{
pre = [ string.chars [ MATCH "^([$(.whitespace)]*)" : " $(p[1])" ] ] ;
if ! $(p[2]) || $(p[2]) = "" { paragraph = $(paragraph[1--2]) "$(p[1]):" ; }
else { paragraph = $(paragraph[1--2]) $(p[1]) ; }
if $(output-type) = plain
{
lines [ split-at-words " " $(paragraph) ] : " " " " ;
lines ;
}
else if $(output-type) = html
{
text </p> <p> [ escape-html $(paragraph) ] ;
}
}
else
{
if $(output-type) = plain
{
lines [ split-at-words " " $(paragraph) ] : " " " " ;
lines ;
}
else if $(output-type) = html
{
text </p> <p> [ escape-html $(paragraph) ] ;
}
}
}
}
}
if $(output-type) = html
{
text </p> ;
}
}
# Generate the start of a list of items. The type of output can be controlled by
# the value of the 'output-type' variable.
#
rule list-start ( )
{
if $(output-type) = plain
{
}
else if $(output-type) = html
{
text <ul> ;
}
}
# Generate an item in a list. The type of output can be controlled by the value
# of the 'output-type' variable.
#
rule list-item (
item + # The item to list.
)
{
if $(output-type) = plain
{
lines [ split-at-words "*" $(item) ] : " " " " ;
}
else if $(output-type) = html
{
text <li> [ escape-html $(item) ] </li> ;
}
}
# Generate the end of a list of items. The type of output can be controlled by
# the value of the 'output-type' variable.
#
rule list-end ( )
{
if $(output-type) = plain
{
lines ;
}
else if $(output-type) = html
{
text </ul> ;
}
}
# Split the given text into separate lines, word-wrapping to a margin. The
# default margin is 78 characters.
#
rule split-at-words (
text + # The text to split.
: margin ? # An optional margin, default is 78.
)
{
local lines = ;
text = [ string.words $(text:J=" ") ] ;
text = $(text:J=" ") ;
margin ?= 78 ;
local char-match-1 = ".?" ;
local char-match = "" ;
while $(margin) != 0
{
char-match = $(char-match)$(char-match-1) ;
margin = [ numbers.decrement $(margin) ] ;
}
while $(text)
{
local s = "" ;
local t = "" ;
# divide s into the first X characters and the rest
s = [ MATCH "^($(char-match))(.*)" : $(text) ] ;
if $(s[2])
{
# split the first half at a space
t = [ MATCH "^(.*)[\\ ]([^\\ ]*)$" : $(s[1]) ] ;
}
else
{
t = $(s) ;
}
if ! $(t[2])
{
t += "" ;
}
text = $(t[2])$(s[2]) ;
lines += $(t[1]) ;
}
return $(lines) ;
}
# Generate a set of fixed lines. Each single item passed in is output on a
# separate line. For console this just echos each line, but for html this will
# split them with <br>.
#
rule lines (
text * # The lines of text.
: indent ? # Optional indentation prepended to each line after the first.
outdent ? # Optional indentation to prepend to the first line.
)
{
text ?= "" ;
indent ?= "" ;
outdent ?= "" ;
if $(output-type) = plain
{
text $(outdent)$(text[1]) $(indent)$(text[2-]) ;
}
else if $(output-type) = html
{
local indent-chars = [ string.chars $(indent) ] ;
indent = "" ;
for local c in $(indent-chars)
{
if $(c) = " " { c = "&nbsp;" ; }
else if $(c) = " " { c = "&nbsp;&nbsp;&nbsp;&nbsp;" ; }
indent = $(indent)$(c) ;
}
local html-text = [ escape-html $(text) : "&nbsp;" ] ;
text $(html-text[1])<br> $(indent)$(html-text[2-])<br> ;
}
}
# Output text directly to the current target. When doing output to a file, one
# can indicate if the text should be output to "prefix" it, as the "body"
# (default), or "suffix" of the file. This is independent of the actual
# execution order of the text rule. This rule invokes a singular action, one
# action only once, which does the build of the file. Therefore actions on the
# target outside of this rule will happen entirely before and/or after all
# output using this rule.
#
rule text (
strings * # The strings of text to output.
: overwrite ? # True to overwrite the output (if it is a file).
: prefix-body-suffix ? # Indication to output prefix, body, or suffix (for
# a file).
)
{
prefix-body-suffix ?= body ;
if $(output-target) = console
{
if ! $(strings)
{
ECHO ;
}
else
{
for local s in $(strings)
{
ECHO $(s) ;
}
}
}
if ! $($(output-target).did-action)
{
$(output-target).did-action = yes ;
$(output-target).text-prefix = ;
$(output-target).text-body = ;
$(output-target).text-suffix = ;
nl on $(output-target) = "
" ;
text-redirect on $(output-target) = $(.redirect-append) ;
if $(overwrite)
{
text-redirect on $(output-target) = $(.redirect-out) ;
}
text-content on $(output-target) = ;
text-action $(output-target) ;
if $(overwrite) && $(output-target) != console
{
check-for-update $(output-target) ;
}
}
$(output-target).text-$(prefix-body-suffix) += $(strings) ;
text-content on $(output-target) =
$($(output-target).text-prefix)
$($(output-target).text-body)
$($(output-target).text-suffix) ;
}
# Outputs the text to the current targets, after word-wrapping it.
#
rule wrapped-text ( text + )
{
local lines = [ split-at-words $(text) ] ;
text $(lines) ;
}
# Escapes text into html/xml printable equivalents. Does not know about tags and
# therefore tags fed into this will also be escaped. Currently escapes space,
# "<", ">", and "&".
#
rule escape-html (
text + # The text to escape.
: space ? # What to replace spaces with, defaults to " ".
)
{
local html-text = ;
while $(text)
{
local html = $(text[1]) ;
text = $(text[2-]) ;
html = [ regex.replace $(html) "&" "&amp;" ] ;
html = [ regex.replace $(html) "<" "&lt;" ] ;
html = [ regex.replace $(html) ">" "&gt;" ] ;
if $(space)
{
html = [ regex.replace $(html) " " "$(space)" ] ;
}
html-text += $(html) ;
}
return $(html-text) ;
}
# Outputs the text strings collected by the text rule to the output file.
#
actions quietly text-action
{
@($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) "$(<)"
}
if [ os.name ] = VMS
{
actions quietly text-action
{
@($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) $(<:W)
}
}
rule get-scanner ( )
{
if ! $(.scanner)
{
.scanner = [ class.new print-scanner ] ;
}
return $(.scanner) ;
}
# The following code to update print targets when their contents change is a
# horrible hack. It basically creates a target which binds to this file
# (print.jam) and installs a scanner on it which reads the target and compares
# its contents to the new contents that we are writing.
#
rule check-for-update ( target )
{
local scanner = [ get-scanner ] ;
local file = [ path.native [ modules.binding $(__name__) ] ] ;
local g = [ MATCH <(.*)> : $(target:G) ] ;
local dependency-target = $(__file__:G=$(g:E=)-$(target:G=)-$(scanner)) ;
DEPENDS $(target) : $(dependency-target) ;
SEARCH on $(dependency-target) = $(file:D) ;
ISFILE $(dependency-target) ;
NOUPDATE $(dependency-target) ;
base on $(dependency-target) = $(target) ;
scanner.install $(scanner) : $(dependency-target) ;
return $(dependency-target) ;
}
class print-scanner : scanner
{
import path ;
import os ;
rule pattern ( )
{
return "(One match...)" ;
}
rule process ( target : matches * : binding )
{
local base = [ on $(target) return $(base) ] ;
local nl = [ on $(base) return $(nl) ] ;
local text-content = [ on $(base) return $(text-content) ] ;
local dir = [ on $(base) return $(LOCATE) ] ;
if $(dir)
{
dir = [ path.make $(dir) ] ;
}
local file = [ path.native [ path.join $(dir) $(base:G=) ] ] ;
local actual-content ;
if [ os.name ] = NT
{
actual-content = [ SHELL "type \"$(file)\" 2>nul" ] ;
}
else if [ os.name ] = VMS
{
actual-content = [ SHELL "PIPE TYPE $(file:W) 2>NL:" ] ;
}
else
{
actual-content = [ SHELL "cat \"$(file)\" 2>/dev/null" ] ;
}
if $(text-content:J=$(nl)) != $(actual-content)
{
ALWAYS $(base) ;
}
}
}
rule __test__ ( )
{
import assert ;
assert.result one two three : split-at-words one two three : 5 ;
assert.result "one two" three : split-at-words one two three : 8 ;
assert.result "one two" three : split-at-words one two three : 9 ;
assert.result "one two three" : split-at-words one two three ;
# VP, 2004-12-03 The following test fails for some reason, so commenting it
# out.
#assert.result "one&nbsp;two&nbsp;three" "&amp;&lt;&gt;" :
# escape-html "one two three" "&<>" ;
}

View File

@@ -0,0 +1,208 @@
# Copyright 2001, 2002 Dave Abrahams
# Copyright 2003 Douglas Gregor
# Copyright 2003 Rene Rivera
# Copyright 2002, 2003, 2004, 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)
#
# Returns a list of the following substrings:
# 1) from beginning till the first occurrence of 'separator' or till the end,
# 2) between each occurrence of 'separator' and the next occurrence,
# 3) from the last occurrence of 'separator' till the end.
# If no separator is present, the result will contain only one element.
#
rule split ( string separator )
{
local result ;
local s = $(string) ;
# Break pieaces off 's' until it has no separators left.
local match = 1 ;
while $(match)
{
match = [ MATCH ^(.*)($(separator))(.*) : $(s) ] ;
if $(match)
{
match += "" ; # in case 3rd item was empty - works around MATCH bug
result = $(match[3]) $(result) ;
s = $(match[1]) ;
}
}
# Combine the remaining part at the beginning, which does not have
# separators, with the pieces broken off. Note that the rule's signature
# does not allow the initial s to be empty.
return $(s) $(result) ;
}
if [ HAS_NATIVE_RULE regex : split : 1 ]
{
NATIVE_RULE regex : split ;
}
# Returns the concatenated results of Applying regex.split to every element of
# the list using the separator pattern.
#
rule split-list ( list * : separator )
{
local result ;
for s in $(list)
{
result += [ split $(s) $(separator) ] ;
}
return $(result) ;
}
# Match string against pattern, and return the elements indicated by indices.
#
rule match ( pattern : string : indices * )
{
indices ?= 1 2 3 4 5 6 7 8 9 ;
local x = [ MATCH $(pattern) : $(string) ] ;
return $(x[$(indices)]) ;
}
# Matches all elements of 'list' against the 'pattern' and returns a list of
# elements indicated by indices of all successful matches. If 'indices' is
# omitted returns a list of first parenthesised groups of all successful
# matches.
#
rule transform ( list * : pattern : indices * )
{
indices ?= 1 ;
local result ;
for local e in $(list)
{
local m = [ MATCH $(pattern) : $(e) ] ;
if $(m)
{
result += $(m[$(indices)]) ;
}
}
return $(result) ;
}
NATIVE_RULE regex : transform ;
# Escapes all of the characters in symbols using the escape symbol escape-symbol
# for the given string, and returns the escaped string.
#
rule escape ( string : symbols : escape-symbol )
{
local result = "" ;
local m = 1 ;
while $(m)
{
m = [ MATCH "^([^$(symbols)]*)([$(symbols)])(.*)" : $(string) ] ;
if $(m)
{
m += "" ; # Supposedly a bug fix; borrowed from regex.split
result = "$(result)$(m[1])$(escape-symbol)$(m[2])" ;
string = $(m[3]) ;
}
}
string ?= "" ;
result = "$(result)$(string)" ;
return $(result) ;
}
# Replaces occurrences of a match string in a given string and returns the new
# string. The match string can be a regex expression.
#
rule replace (
string # The string to modify.
match # The characters to replace.
replacement # The string to replace with.
)
{
local result = "" ;
local parts = 1 ;
while $(parts)
{
parts = [ MATCH ^(.*)($(match))(.*) : $(string) ] ;
if $(parts)
{
parts += "" ;
result = "$(replacement)$(parts[3])$(result)" ;
string = $(parts[1]) ;
}
}
string ?= "" ;
result = "$(string)$(result)" ;
return $(result) ;
}
if [ HAS_NATIVE_RULE regex : replace : 1 ]
{
NATIVE_RULE regex : replace ;
}
# Replaces occurrences of a match string in a given list of strings and returns
# a list of new strings. The match string can be a regex expression.
#
# list - the list of strings to modify.
# match - the search expression.
# replacement - the string to replace with.
#
rule replace-list ( list * : match : replacement )
{
local result ;
for local e in $(list)
{
result += [ replace $(e) $(match) $(replacement) ] ;
}
return $(result) ;
}
rule __test__ ( )
{
import assert ;
assert.result a b c : split "a/b/c" / ;
assert.result "" a b c : split "/a/b/c" / ;
assert.result "" "" a b c : split "//a/b/c" / ;
assert.result "" a "" b c : split "/a//b/c" / ;
assert.result "" a "" b c "" : split "/a//b/c/" / ;
assert.result "" a "" b c "" "" : split "/a//b/c//" / ;
assert.result "" a b c "" : split "abc" "" ;
assert.result "" "" : split "" "" ;
assert.result a c b d
: match (.)(.)(.)(.) : abcd : 1 3 2 4 ;
assert.result a b c d
: match (.)(.)(.)(.) : abcd ;
assert.result ababab cddc
: match "((ab)*)([cd]+)" : abababcddc : 1 3 ;
assert.result a.h c.h
: transform <a.h> \"b.h\" <c.h> : <(.*)> ;
assert.result a.h b.h c.h
: transform <a.h> \"b.h\" <c.h> : "<([^>]*)>|\"([^\"]*)\"" : 1 2 ;
assert.result "^<?xml version=\"1.0\"^>"
: escape "<?xml version=\"1.0\">" : "&|()<>^" : "^" ;
assert.result "<?xml version=\\\"1.0\\\">"
: escape "<?xml version=\"1.0\">" : "\\\"" : "\\" ;
assert.result "string&nbsp;string&nbsp;" : replace "string string " " " "&nbsp;" ;
assert.result "&nbsp;string&nbsp;string" : replace " string string" " " "&nbsp;" ;
assert.result "string&nbsp;&nbsp;string" : replace "string string" " " "&nbsp;" ;
assert.result "-" : replace "&" "&" "-" ;
assert.result "x" : replace "" "" "x" ;
assert.result "xax" : replace "a" "" "x" ;
assert.result "xaxbx" : replace "ab" "" "x" ;
assert.result "-" "a-b" : replace-list "&" "a&b" : "&" : "-" ;
}

View File

@@ -0,0 +1,63 @@
# (C) Copyright David Abrahams 2001. 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.
import re
from b2.util import bjam_signature
def transform (list, pattern, indices = [1]):
""" Matches all elements of 'list' against the 'pattern'
and returns a list of the elements indicated by indices of
all successful matches. If 'indices' is omitted returns
a list of first paranthethised groups of all successful
matches.
"""
result = []
for e in list:
m = re.match (pattern, e)
if m:
for i in indices:
result.append (m.group (i))
return result
@bjam_signature([['s', 'pattern', 'replacement']])
def replace(s, pattern, replacement):
"""Replaces occurrences of a match string in a given
string and returns the new string. The match string
can be a regex expression.
Args:
s (str): the string to modify
pattern (str): the search expression
replacement (str): the string to replace each match with
"""
# the replacement string may contain invalid backreferences (like \1 or \g)
# which will cause python's regex to blow up. Since this should emulate
# the jam version exactly and the jam version didn't support
# backreferences, this version shouldn't either. re.sub
# allows replacement to be a callable; this is being used
# to simply return the replacement string and avoid the hassle
# of worrying about backreferences within the string.
def _replacement(matchobj):
return replacement
return re.sub(pattern, _replacement, s)
@bjam_signature((['items', '*'], ['match'], ['replacement']))
def replace_list(items, match, replacement):
"""Replaces occurrences of a match string in a given list of strings and returns
a list of new strings. The match string can be a regex expression.
Args:
items (list): the list of strings to modify.
match (str): the search expression.
replacement (str): the string to replace with.
"""
return [replace(item, match, replacement) for item in items]

View File

@@ -0,0 +1,378 @@
# Copyright 2001, 2002, 2003 Dave Abrahams
# Copyright 2006 Rene Rivera
# Copyright 2002, 2003 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 assert ;
import numbers ;
import modules ;
# Note that algorithms in this module execute largely in the caller's module
# namespace, so that local rules can be used as function objects. Also note that
# most predicates can be multi-element lists. In that case, all but the first
# element are prepended to the first argument which is passed to the rule named
# by the first element.
# Return the elements e of $(sequence) for which [ $(predicate) e ] has a
# non-null value.
#
rule filter ( predicate + : sequence * )
{
local caller = [ CALLER_MODULE ] ;
local result ;
for local e in $(sequence)
{
if [ modules.call-in $(caller) : $(predicate) $(e) ]
{
result += $(e) ;
}
}
return $(result) ;
}
# Return a new sequence consisting of [ $(function) $(e) ] for each element e of
# $(sequence).
#
rule transform ( function + : sequence * )
{
local caller = [ CALLER_MODULE ] ;
local result ;
for local e in $(sequence)
{
result += [ modules.call-in $(caller) : $(function) $(e) ] ;
}
return $(result) ;
}
if [ HAS_NATIVE_RULE sequence : transform : 1 ]
{
NATIVE_RULE sequence : transform ;
}
# Returns the elements of 's' in reverse order
rule reverse ( s * )
{
local r ;
for local x in $(s)
{
r = $(x) $(r) ;
}
return $(r) ;
}
rule less ( a b )
{
if $(a) < $(b)
{
return true ;
}
}
# Insertion-sort s using the BinaryPredicate ordered.
#
rule insertion-sort ( s * : ordered * )
{
if ! $(ordered)
{
return [ SORT $(s) ] ;
}
else
{
local caller = [ CALLER_MODULE ] ;
ordered ?= sequence.less ;
local result = $(s[1]) ;
if $(ordered) = sequence.less
{
local head tail ;
for local x in $(s[2-])
{
head = ;
tail = $(result) ;
while $(tail) && ( $(tail[1]) < $(x) )
{
head += $(tail[1]) ;
tail = $(tail[2-]) ;
}
result = $(head) $(x) $(tail) ;
}
}
else
{
for local x in $(s[2-])
{
local head tail ;
tail = $(result) ;
while $(tail) && [ modules.call-in $(caller) : $(ordered) $(tail[1]) $(x) ]
{
head += $(tail[1]) ;
tail = $(tail[2-]) ;
}
result = $(head) $(x) $(tail) ;
}
}
return $(result) ;
}
}
# Merge two ordered sequences using the BinaryPredicate ordered.
#
rule merge ( s1 * : s2 * : ordered * )
{
ordered ?= sequence.less ;
local result__ ;
local caller = [ CALLER_MODULE ] ;
while $(s1) && $(s2)
{
if [ modules.call-in $(caller) : $(ordered) $(s1[1]) $(s2[1]) ]
{
result__ += $(s1[1]) ;
s1 = $(s1[2-]) ;
}
else if [ modules.call-in $(caller) : $(ordered) $(s2[1]) $(s1[1]) ]
{
result__ += $(s2[1]) ;
s2 = $(s2[2-]) ;
}
else
{
s2 = $(s2[2-]) ;
}
}
result__ += $(s1) ;
result__ += $(s2) ;
return $(result__) ;
}
# Compares two sequences lexicagraphically
#
rule compare ( s1 * : s2 * : ordered * )
{
if ! $(ordered)
{
if $(s1) < $(s2)
{
return true ;
}
}
else
{
while true
{
if ! $(s2[1])-is-defined
{
return ;
}
else if ! $(s1[1])-is-defined
{
return true ;
}
else if [ $(ordered) $(s1[1]) $(s2[1]) ]
{
return true ;
}
else if [ $(ordered) $(s2[1]) $(s1[1]) ]
{
return ;
}
s1 = $(s1[2-]) ;
s2 = $(s2[2-]) ;
}
}
}
# Join the elements of s into one long string. If joint is supplied, it is used
# as a separator.
#
rule join ( s * : joint ? )
{
joint ?= "" ;
return $(s:J=$(joint)) ;
}
# Find the length of any sequence.
#
rule length ( s * )
{
local result = 0 ;
for local i in $(s)
{
result = [ CALC $(result) + 1 ] ;
}
return $(result) ;
}
# Removes duplicates from 'list'. If 'stable' is
# passed, then the order of the elements will
# be unchanged.
rule unique ( list * : stable ? )
{
local result ;
local prev ;
if $(stable)
{
for local f in $(list)
{
if ! $(f) in $(result)
{
result += $(f) ;
}
}
}
else
{
for local i in [ SORT $(list) ]
{
if $(i) != $(prev)
{
result += $(i) ;
}
prev = $(i) ;
}
}
return $(result) ;
}
# Returns the maximum number in 'elements'. Uses 'ordered' for comparisons or
# 'numbers.less' if none is provided.
#
rule max-element ( elements + : ordered ? )
{
ordered ?= numbers.less ;
local max = $(elements[1]) ;
for local e in $(elements[2-])
{
if [ $(ordered) $(max) $(e) ]
{
max = $(e) ;
}
}
return $(max) ;
}
# Returns all of 'elements' for which corresponding element in parallel list
# 'rank' is equal to the maximum value in 'rank'.
#
rule select-highest-ranked ( elements * : ranks * )
{
if $(elements)
{
local max-rank = [ max-element $(ranks) ] ;
local result ;
while $(elements)
{
if $(ranks[1]) = $(max-rank)
{
result += $(elements[1]) ;
}
elements = $(elements[2-]) ;
ranks = $(ranks[2-]) ;
}
return $(result) ;
}
}
NATIVE_RULE sequence : select-highest-ranked ;
rule __test__ ( )
{
# Use a unique module so we can test the use of local rules.
module sequence.__test__
{
import assert ;
import sequence ;
local rule is-even ( n )
{
if $(n) in 0 2 4 6 8
{
return true ;
}
}
assert.result 4 6 4 2 8 : sequence.filter is-even : 1 4 6 3 4 7 2 3 8 ;
# Test that argument binding works.
local rule is-equal-test ( x y )
{
if $(x) = $(y)
{
return true ;
}
}
assert.result 3 3 3 : sequence.filter is-equal-test 3 : 1 2 3 4 3 5 3 5 7 ;
local rule append-x ( n )
{
return $(n)x ;
}
assert.result 1x 2x 3x : sequence.transform append-x : 1 2 3 ;
local rule repeat2 ( x )
{
return $(x) $(x) ;
}
assert.result 1 1 2 2 3 3 : sequence.transform repeat2 : 1 2 3 ;
local rule test-greater ( a b )
{
if $(a) > $(b)
{
return true ;
}
}
assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ;
assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ;
assert.result 1 2 3 4 5 6 : sequence.merge 1 3 5 : 2 4 6 ;
assert.result 6 5 4 3 2 1 : sequence.merge 5 3 1 : 6 4 2 : test-greater ;
assert.result 1 2 3 : sequence.merge 1 2 3 : ;
assert.result 1 : sequence.merge 1 : 1 ;
assert.result foo-bar-baz : sequence.join foo bar baz : - ;
assert.result substandard : sequence.join sub stan dard ;
assert.result 3.0.1 : sequence.join 3.0.1 : - ;
assert.result 0 : sequence.length ;
assert.result 3 : sequence.length a b c ;
assert.result 17 : sequence.length 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 ;
assert.result 1 : sequence.length a ;
assert.result 10 : sequence.length a b c d e f g h i j ;
assert.result 11 : sequence.length a b c d e f g h i j k ;
assert.result 12 : sequence.length a b c d e f g h i j k l ;
local p2 = x ;
for local i in 1 2 3 4 5 6 7 8
{
p2 = $(p2) $(p2) ;
}
assert.result 256 : sequence.length $(p2) ;
assert.result 1 2 3 4 5 : sequence.unique 1 2 3 2 4 3 3 5 5 5 ;
assert.result 5 : sequence.max-element 1 3 5 0 4 ;
assert.result e-3 h-3 : sequence.select-highest-ranked e-1 e-3 h-3 m-2 : 1 3 3 2 ;
assert.result 7 6 5 4 3 2 1 : sequence.reverse 1 2 3 4 5 6 7 ;
}
}

View File

@@ -0,0 +1,58 @@
# (C) Copyright David Abrahams 2002. 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.
import operator
from b2.util import is_iterable
def unique (values, stable=False):
assert is_iterable(values)
if stable:
s = set()
r = []
for v in values:
if not v in s:
r.append(v)
s.add(v)
return r
else:
return list(set(values))
def max_element (elements, ordered = None):
""" Returns the maximum number in 'elements'. Uses 'ordered' for comparisons,
or '<' is none is provided.
"""
assert is_iterable(elements)
assert callable(ordered) or ordered is None
if not ordered: ordered = operator.lt
max = elements [0]
for e in elements [1:]:
if ordered (max, e):
max = e
return max
def select_highest_ranked (elements, ranks):
""" Returns all of 'elements' for which corresponding element in parallel
list 'rank' is equal to the maximum value in 'rank'.
"""
assert is_iterable(elements)
assert is_iterable(ranks)
if not elements:
return []
max_rank = max_element (ranks)
result = []
while elements:
if ranks [0] == max_rank:
result.append (elements [0])
elements = elements [1:]
ranks = ranks [1:]
return result

View File

@@ -0,0 +1,93 @@
# Copyright 2001, 2002 Dave Abrahams
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
class set
{
rule __init__ ( )
{
}
rule add ( elements * )
{
for local e in $(elements)
{
if ! $($(e))
{
$(e) = 1 ;
self.result += $(e) ;
}
}
}
rule contains ( element )
{
return $($(element)) ;
}
rule list ( )
{
return $(self.result) ;
}
}
# Returns the elements of set1 that are not in set2.
#
rule difference ( set1 * : set2 * )
{
local result = ;
for local element in $(set1)
{
if ! ( $(element) in $(set2) )
{
result += $(element) ;
}
}
return $(result) ;
}
NATIVE_RULE set : difference ;
# Removes all the items appearing in both set1 & set2.
#
rule intersection ( set1 * : set2 * )
{
local result ;
for local v in $(set1)
{
if $(v) in $(set2)
{
result += $(v) ;
}
}
return $(result) ;
}
# Returns whether set1 & set2 contain the same elements. Note that this ignores
# any element ordering differences as well as any element duplication.
#
rule equal ( set1 * : set2 * )
{
if $(set1) in $(set2) && ( $(set2) in $(set1) )
{
return true ;
}
}
rule __test__ ( )
{
import assert ;
assert.result 0 1 4 6 8 9 : difference 0 1 2 3 4 5 6 7 8 9 : 2 3 5 7 ;
assert.result 2 5 7 : intersection 0 1 2 4 5 6 7 8 9 : 2 3 5 7 ;
assert.true equal : ;
assert.true equal 1 1 2 3 : 3 2 2 1 ;
assert.false equal 2 3 : 3 2 2 1 ;
}

View File

@@ -0,0 +1,48 @@
# (C) Copyright David Abrahams 2001. 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.
from b2.util import is_iterable
from .utility import to_seq
def difference (b, a):
""" Returns the elements of B that are not in A.
"""
a = set(a)
result = []
for item in b:
if item not in a:
result.append(item)
return result
def intersection (set1, set2):
""" Removes from set1 any items which don't appear in set2 and returns the result.
"""
assert is_iterable(set1)
assert is_iterable(set2)
result = []
for v in set1:
if v in set2:
result.append (v)
return result
def contains (small, large):
""" Returns true iff all elements of 'small' exist in 'large'.
"""
small = to_seq (small)
large = to_seq (large)
for s in small:
if not s in large:
return False
return True
def equal (a, b):
""" Returns True iff 'a' contains the same elements as 'b', irrespective of their order.
# TODO: Python 2.4 has a proper set class.
"""
assert is_iterable(a)
assert is_iterable(b)
return contains (a, b) and contains (b, a)

View File

@@ -0,0 +1,189 @@
# Copyright 2002 Dave Abrahams
# Copyright 2002, 2003 Rene Rivera
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import regex ;
# Characters considered whitespace, as a list.
.whitespace-chars = " " " " "
" ;
# Characters considered whitespace, as a single string.
.whitespace = $(.whitespace-chars:J="") ;
# Returns the canonical set of whitespace characters, as a list.
#
rule whitespace-chars ( )
{
return $(.whitespace-chars) ;
}
# Returns the canonical set of whitespace characters, as a single string.
#
rule whitespace ( )
{
return $(.whitespace) ;
}
# Splits the given string into a list of strings composed of each character of
# the string in sequence.
#
rule chars (
string # The string to split.
)
{
local result ;
while $(string)
{
local s = [ MATCH (.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.*) : $(string) ] ;
string = $(s[9]) ;
result += $(s[1-8]) ;
}
# Trim off empty strings.
while $(result[1]) && ! $(result[-1])
{
result = $(result[1--2]) ;
}
return $(result) ;
}
# Apply a set of standard transformations to string to produce an abbreviation
# no more than 5 characters long.
#
rule abbreviate ( string )
{
local r = $(.abbreviated-$(string)) ;
if $(r)
{
return $(r) ;
}
# Anything less than 4 characters gets no abbreviation.
else if ! [ MATCH (....) : $(string) ]
{
.abbreviated-$(string) = $(string) ;
return $(string) ;
}
else
{
# Separate the initial letter in case it's a vowel.
local s1 = [ MATCH ^(.)(.*) : $(string) ] ;
# Drop trailing "ing".
local s2 = [ MATCH ^(.*)ing$ : $(s1[2]) ] ;
s2 ?= $(s1[2]) ;
# Reduce all doubled characters to one.
local last = "" ;
for local c in [ chars $(s2) ]
{
if $(c) != $(last)
{
r += $(c) ;
last = $(c) ;
}
}
s2 = $(r:J="") ;
# Chop all vowels out of the remainder.
s2 = [ regex.replace $(s2) "[AEIOUaeiou]" "" ] ;
# Shorten remaining consonants to 4 characters.
s2 = [ MATCH ^(.?.?.?.?) : $(s2) ] ;
# Glue the initial character back on to the front.
s2 = $(s1[1])$(s2) ;
.abbreviated-$(string) = $(s2) ;
return $(s2) ;
}
}
# Concatenates the given strings, inserting the given separator between each
# string.
#
rule join (
strings * # The strings to join.
: separator ? # The optional separator.
)
{
separator ?= "" ;
return $(strings:J=$(separator)) ;
}
# Split a string into whitespace separated words.
#
rule words (
string # The string to split.
: whitespace * # Optional, characters to consider as whitespace.
)
{
whitespace = $(whitespace:J="") ;
whitespace ?= $(.whitespace) ;
local w = ;
while $(string)
{
string = [ MATCH "^[$(whitespace)]*([^$(whitespace)]*)(.*)" : $(string) ] ;
if $(string[1]) && $(string[1]) != ""
{
w += $(string[1]) ;
}
string = $(string[2]) ;
}
return $(w) ;
}
# Check that the given string is composed entirely of whitespace.
#
rule is-whitespace (
string ? # The string to test.
)
{
if ! $(string) { return true ; }
else if $(string) = "" { return true ; }
else if [ MATCH "^([$(.whitespace)]+)$" : $(string) ] { return true ; }
else { return ; }
}
rule __test__ ( )
{
import assert ;
assert.result a b c : chars abc ;
assert.result rntm : abbreviate runtime ;
assert.result ovrld : abbreviate overload ;
assert.result dbg : abbreviate debugging ;
assert.result async : abbreviate asynchronous ;
assert.result pop : abbreviate pop ;
assert.result aaa : abbreviate aaa ;
assert.result qck : abbreviate quack ;
assert.result sttc : abbreviate static ;
# Check boundary cases.
assert.result a : chars a ;
assert.result : chars "" ;
assert.result a b c d e f g h : chars abcdefgh ;
assert.result a b c d e f g h i : chars abcdefghi ;
assert.result a b c d e f g h i j : chars abcdefghij ;
assert.result a b c d e f g h i j k : chars abcdefghijk ;
assert.result a//b/c/d : join a "" b c d : / ;
assert.result abcd : join a "" b c d ;
assert.result a b c : words "a b c" ;
assert.true is-whitespace " " ;
assert.false is-whitespace " a b c " ;
assert.true is-whitespace "" ;
assert.true is-whitespace ;
}

View File

@@ -0,0 +1,235 @@
# Copyright 2001, 2002 Dave Abrahams
# Copyright 2002, 2003, 2004, 2005 Vladimir Prus
# Copyright 2008 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import "class" : is-instance ;
# For all elements of 'list' which do not already have 'suffix', add 'suffix'.
#
rule apply-default-suffix ( suffix : list * )
{
local result ;
for local i in $(list)
{
if $(i:S) = $(suffix)
{
result += $(i) ;
}
else
{
result += $(i)$(suffix) ;
}
}
return $(result) ;
}
# If 'name' contains a dot, returns the part before the last dot. If 'name'
# contains no dot, returns it unmodified.
#
rule basename ( name )
{
if $(name:S)
{
name = $(name:B) ;
}
return $(name) ;
}
# Return the file of the caller of the rule that called caller-file.
#
rule caller-file ( )
{
local bt = [ BACKTRACE ] ;
return $(bt[9]) ;
}
# Tests if 'a' is equal to 'b'. If 'a' is a class instance, calls its 'equal'
# method. Uses ordinary jam's comparison otherwise.
#
rule equal ( a b )
{
if [ is-instance $(a) ]
{
return [ $(a).equal $(b) ] ;
}
else
{
if $(a) = $(b)
{
return true ;
}
}
}
# Tests if 'a' is less than 'b'. If 'a' is a class instance, calls its 'less'
# method. Uses ordinary jam's comparison otherwise.
#
rule less ( a b )
{
if [ is-instance $(a) ]
{
return [ $(a).less $(b) ] ;
}
else
{
if $(a) < $(b)
{
return true ;
}
}
}
# Returns the textual representation of argument. If it is a class instance,
# class its 'str' method. Otherwise, returns the argument.
#
rule str ( value )
{
if [ is-instance $(value) ]
{
return [ $(value).str ] ;
}
else
{
return $(value) ;
}
}
# Accepts a list of gristed values and returns them ungristed. Reports an error
# in case any of the passed parameters is not gristed, i.e. surrounded in angle
# brackets < and >.
#
rule ungrist ( names * )
{
local result ;
for local name in $(names)
{
local stripped = [ MATCH ^<(.*)>$ : $(name) ] ;
if ! $(stripped)-defined
{
import errors ;
local quoted-names = \"$(names)\" ;
errors.error "in" ungrist "$(quoted-names:J= ):" \"$(name)\" is not
of the form <.*> ;
}
result += $(stripped) ;
}
return $(result) ;
}
# If the passed value is quoted, unquotes it. Otherwise returns the value
# unchanged.
#
rule unquote ( value ? )
{
local match-result = [ MATCH ^(\")(.*)(\")$ : $(value) ] ;
if $(match-result)
{
return $(match-result[2]) ;
}
else
{
return $(value) ;
}
}
rule __test__ ( )
{
import assert ;
import "class" : new ;
import errors : try catch ;
assert.result 123 : str 123 ;
class test-class__
{
rule __init__ ( ) { }
rule str ( ) { return "str-test-class" ; }
rule less ( a ) { return "yes, of course!" ; }
rule equal ( a ) { return "not sure" ; }
}
assert.result "str-test-class" : str [ new test-class__ ] ;
assert.true less 1 2 ;
assert.false less 2 1 ;
assert.result "yes, of course!" : less [ new test-class__ ] 1 ;
assert.true equal 1 1 ;
assert.false equal 1 2 ;
assert.result "not sure" : equal [ new test-class__ ] 1 ;
assert.result foo.lib foo.lib : apply-default-suffix .lib : foo.lib foo.lib
;
assert.result foo : basename foo ;
assert.result foo : basename foo.so ;
assert.result foo.so : basename foo.so.1 ;
assert.result : unquote ;
assert.result "" : unquote "" ;
assert.result "" : unquote \"\" ;
assert.result \" : unquote \"\"\" ;
assert.result \"\" : unquote \"\"\"\" ;
assert.result foo : unquote foo ;
assert.result \"foo : unquote \"foo ;
assert.result foo\" : unquote foo\" ;
assert.result foo : unquote \"foo\" ;
assert.result \"foo\" : unquote \"\"foo\"\" ;
assert.result : ungrist ;
assert.result "" : ungrist <> ;
assert.result foo : ungrist <foo> ;
assert.result <foo> : ungrist <<foo>> ;
assert.result foo bar : ungrist <foo> <bar> ;
try ;
{
ungrist "" ;
}
catch "in" ungrist "\"\":" \"\" is not of the form <.*> ;
try ;
{
ungrist foo ;
}
catch "in" ungrist "\"foo\":" \"foo\" is not of the form <.*> ;
try ;
{
ungrist <foo ;
}
catch "in" ungrist "\"<foo\":" \"<foo\" is not of the form <.*> ;
try ;
{
ungrist foo> ;
}
catch "in" ungrist "\"foo>\":" \"foo>\" is not of the form <.*> ;
try ;
{
ungrist foo bar ;
}
catch "in" ungrist "\"foo\" "\"bar\"":" \"foo\" is not of the form <.*> ;
try ;
{
ungrist foo <bar> ;
}
catch "in" ungrist "\"foo\" "\"<bar>\"":" \"foo\" is not of the form <.*> ;
try ;
{
ungrist <foo> bar ;
}
catch "in" ungrist "\"<foo>\" "\"bar\"":" \"bar\" is not of the form <.*> ;
}

View File

@@ -0,0 +1,176 @@
# (C) Copyright David Abrahams 2001. 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.
""" Utility functions to add/remove/get grists.
Grists are string enclosed in angle brackets (<>) that are used as prefixes. See Jam for more information.
"""
import re
import os
import bjam
from b2.exceptions import *
from b2.util import is_iterable_typed
__re_grist_and_value = re.compile (r'(<[^>]*>)(.*)')
__re_grist_content = re.compile ('^<(.*)>$')
__re_backslash = re.compile (r'\\')
def to_seq (value):
""" If value is a sequence, returns it.
If it is a string, returns a sequence with value as its sole element.
"""
if not value:
return []
if isinstance (value, str):
return [value]
else:
return value
def replace_references_by_objects (manager, refs):
objs = []
for r in refs:
objs.append (manager.get_object (r))
return objs
def add_grist (features):
""" Transform a string by bracketing it with "<>". If already bracketed, does nothing.
features: one string or a sequence of strings
return: the gristed string, if features is a string, or a sequence of gristed strings, if features is a sequence
"""
assert is_iterable_typed(features, basestring) or isinstance(features, basestring)
def grist_one (feature):
if feature [0] != '<' and feature [len (feature) - 1] != '>':
return '<' + feature + '>'
else:
return feature
if isinstance (features, str):
return grist_one (features)
else:
return [ grist_one (feature) for feature in features ]
def replace_grist (features, new_grist):
""" Replaces the grist of a string by a new one.
Returns the string with the new grist.
"""
assert is_iterable_typed(features, basestring) or isinstance(features, basestring)
assert isinstance(new_grist, basestring)
# this function is used a lot in the build phase and the original implementation
# was extremely slow; thus some of the weird-looking optimizations for this function.
single_item = False
if isinstance(features, str):
features = [features]
single_item = True
result = []
for feature in features:
# '<feature>value' -> ('<feature', '>', 'value')
# 'something' -> ('something', '', '')
# '<toolset>msvc/<feature>value' -> ('<toolset', '>', 'msvc/<feature>value')
grist, split, value = feature.partition('>')
# if a partition didn't occur, then grist is just 'something'
# set the value to be the grist
if not value and not split:
value = grist
result.append(new_grist + value)
if single_item:
return result[0]
return result
def get_value (property):
""" Gets the value of a property, that is, the part following the grist, if any.
"""
assert is_iterable_typed(property, basestring) or isinstance(property, basestring)
return replace_grist (property, '')
def get_grist (value):
""" Returns the grist of a string.
If value is a sequence, does it for every value and returns the result as a sequence.
"""
assert is_iterable_typed(value, basestring) or isinstance(value, basestring)
def get_grist_one (name):
split = __re_grist_and_value.match (name)
if not split:
return ''
else:
return split.group (1)
if isinstance (value, str):
return get_grist_one (value)
else:
return [ get_grist_one (v) for v in value ]
def ungrist (value):
""" Returns the value without grist.
If value is a sequence, does it for every value and returns the result as a sequence.
"""
assert is_iterable_typed(value, basestring) or isinstance(value, basestring)
def ungrist_one (value):
stripped = __re_grist_content.match (value)
if not stripped:
raise BaseException ("in ungrist: '%s' is not of the form <.*>" % value)
return stripped.group (1)
if isinstance (value, str):
return ungrist_one (value)
else:
return [ ungrist_one (v) for v in value ]
def replace_suffix (name, new_suffix):
""" Replaces the suffix of name by new_suffix.
If no suffix exists, the new one is added.
"""
assert isinstance(name, basestring)
assert isinstance(new_suffix, basestring)
split = os.path.splitext (name)
return split [0] + new_suffix
def forward_slashes (s):
""" Converts all backslashes to forward slashes.
"""
assert isinstance(s, basestring)
return s.replace('\\', '/')
def split_action_id (id):
""" Splits an id in the toolset and specific rule parts. E.g.
'gcc.compile.c++' returns ('gcc', 'compile.c++')
"""
assert isinstance(id, basestring)
split = id.split ('.', 1)
toolset = split [0]
name = ''
if len (split) > 1:
name = split [1]
return (toolset, name)
def os_name ():
result = bjam.variable("OS")
assert(len(result) == 1)
return result[0]
def platform ():
return bjam.variable("OSPLAT")
def os_version ():
return bjam.variable("OSVER")
def on_windows ():
""" Returns true if running on windows, whether in cygwin or not.
"""
if bjam.variable("NT"):
return True
elif bjam.variable("UNIX"):
uname = bjam.variable("JAMUNAME")
if uname and uname[0].startswith("CYGWIN"):
return True
return False