diff options
author | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
---|---|---|
committer | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
commit | 670a8f984fc6d8342180c59ae9e96b0b76f34d3d (patch) | |
tree | 9f2ce7eec1a77e56b3bb1ad0ad40f212d7a996b0 /jam-files/boost-build/util | |
parent | eb3ee28dc0eb1d3e5ed01ba0df843be329ae450d (diff) | |
parent | 2f64af3e06a518b93f7ca2c30a9d0aeb2c947031 (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'jam-files/boost-build/util')
26 files changed, 6469 insertions, 0 deletions
diff --git a/jam-files/boost-build/util/__init__.py b/jam-files/boost-build/util/__init__.py new file mode 100644 index 00000000..f80fe70e --- /dev/null +++ b/jam-files/boost-build/util/__init__.py @@ -0,0 +1,136 @@ + +import bjam +import re +import types + +# Decorator the specifies bjam-side prototype for a Python function +def bjam_signature(s): + + def wrap(f): + f.bjam_signature = s + return f + + return wrap + +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 diff --git a/jam-files/boost-build/util/assert.jam b/jam-files/boost-build/util/assert.jam new file mode 100644 index 00000000..abedad52 --- /dev/null +++ b/jam-files/boost-build/util/assert.jam @@ -0,0 +1,336 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.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) ] ; + } + + 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) ] "]" : 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) ] ; + } + + 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) ] "]" : 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 8 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) ] ; + } + + 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) ] "]" : 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) ] ; + } + + 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) ] "]" ; + } +} + + +# 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 ; +} diff --git a/jam-files/boost-build/util/container.jam b/jam-files/boost-build/util/container.jam new file mode 100644 index 00000000..dd496393 --- /dev/null +++ b/jam-files/boost-build/util/container.jam @@ -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_1_0.txt or http://www.boost.org/LICENSE_1_0.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) ; +} diff --git a/jam-files/boost-build/util/doc.jam b/jam-files/boost-build/util/doc.jam new file mode 100644 index 00000000..a7515588 --- /dev/null +++ b/jam-files/boost-build/util/doc.jam @@ -0,0 +1,997 @@ +# Copyright 2002, 2005 Dave Abrahams +# Copyright 2002, 2003, 2006 Rene Rivera +# Copyright 2003 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Documentation system, handles --help requests. +# It defines rules that attach documentation to modules, rules, and variables. +# Collects and generates documentation for the various parts of the build +# system. The documentation is collected from comments integrated into the code. + +import modules ; +import print ; +import set ; +import container ; +import "class" ; +import sequence ; +import path ; + + +# The type of output to generate. +# "console" is formated text echoed to the console (the default); +# "text" is formated text appended to the output file; +# "html" is HTML output to the file. +# +help-output = console ; + + +# The file to output documentation to when generating "text" or "html" help. +# This is without extension as the extension is determined by the type of +# output. +# +help-output-file = help ; + +# Whether to include local rules in help output. +# +.option.show-locals ?= ; + +# When showing documentation for a module, whether to also generate +# automatically the detailed docs for each item in the module. +# +.option.detailed ?= ; + +# Generate debug output as the help is generated and modules are parsed. +# +.option.debug ?= ; + +# Enable or disable a documentation option. +# +local rule set-option ( + option # The option name. + : value ? # Enabled (non-empty), or disabled (empty) +) +{ + .option.$(option) = $(value) ; +} + + +# Set the type of output. +# +local rule set-output ( type ) +{ + help-output = $(type) ; +} + + +# Set the output to a file. +# +local rule set-output-file ( file ) +{ + help-output-file = $(file) ; +} + + +# Extracts the brief comment from a complete comment. The brief comment is the +# first sentence. +# +local rule brief-comment ( + docs * # The comment documentation. +) +{ + local d = $(docs:J=" ") ; + local p = [ MATCH ".*([.])$" : $(d) ] ; + if ! $(p) { d = $(d)"." ; } + d = $(d)" " ; + local m = [ MATCH "^([^.]+[.])(.*)" : $(d) ] ; + local brief = $(m[1]) ; + while $(m[2]) && [ MATCH "^([^ ])" : $(m[2]) ] + { + m = [ MATCH "^([^.]+[.])(.*)" : $(m[2]) ] ; + brief += $(m[1]) ; + } + return $(brief:J="") ; +} + + +# Specifies the documentation for the current module. +# +local rule set-module-doc ( + module-name ? # The name of the module to document. + : docs * # The documentation for the module. +) +{ + module-name ?= * ; + + $(module-name).brief = [ brief-comment $(docs) ] ; + $(module-name).docs = $(docs) ; + + if ! $(module-name) in $(documented-modules) + { + documented-modules += $(module-name) ; + } +} + + +# Specifies the documentation for the current module. +# +local rule set-module-copyright ( + module-name ? # The name of the module to document. + : copyright * # The copyright for the module. +) +{ + module-name ?= * ; + + $(module-name).copy-brief = [ brief-comment $(copyright) ] ; + $(module-name).copy-docs = $(docs) ; + + if ! $(module-name) in $(documented-modules) + { + documented-modules += $(module-name) ; + } +} + + +# Specifies the documentation for a rule in the current module. If called in the +# global module, this documents a global rule. +# +local rule set-rule-doc ( + name # The name of the rule. + module-name ? # The name of the module to document. + is-local ? # Whether the rule is local to the module. + : docs * # The documentation for the rule. +) +{ + module-name ?= * ; + + $(module-name).$(name).brief = [ brief-comment $(docs) ] ; + $(module-name).$(name).docs = $(docs) ; + $(module-name).$(name).is-local = $(is-local) ; + + if ! $(name) in $($(module-name).rules) + { + $(module-name).rules += $(name) ; + } +} + + +# Specify a class, will turn a rule into a class. +# +local rule set-class-doc ( + name # The name of the class. + module-name ? # The name of the module to document. + : super-name ? # The super class name. +) +{ + module-name ?= * ; + + $(module-name).$(name).is-class = true ; + $(module-name).$(name).super-name = $(super-name) ; + $(module-name).$(name).class-rules = + [ MATCH "^($(name)[.].*)" : $($(module-name).rules) ] ; + $(module-name).$($(module-name).$(name).class-rules).is-class-rule = true ; + + $(module-name).classes += $(name) ; + $(module-name).class-rules += $($(module-name).$(name).class-rules) ; + $(module-name).rules = + [ set.difference $($(module-name).rules) : + $(name) $($(module-name).$(name).class-rules) ] ; +} + + +# Set the argument call signature of a rule. +# +local rule set-rule-arguments-signature ( + name # The name of the rule. + module-name ? # The name of the module to document. + : signature * # The arguments signature. +) +{ + module-name ?= * ; + + $(module-name).$(name).signature = $(signature) ; +} + + +# Specifies the documentation for an argument of a rule. +# +local rule set-argument-doc ( + name # The name of the argument. + qualifier # Argument syntax qualifier, "*", "+", etc. + rule-name # The name of the rule. + module-name ? # THe optional name of the module. + : docs * # The documentation. +) +{ + module-name ?= * ; + + $(module-name).$(rule-name).args.$(name).qualifier = $(qualifier) ; + $(module-name).$(rule-name).args.$(name).docs = $(docs) ; + + if ! $(name) in $($(module-name).$(rule-name).args) + { + $(module-name).$(rule-name).args += $(name) ; + } +} + + +# Specifies the documentation for a variable in the current module. If called in +# the global module, the global variable is documented. +# +local rule set-variable-doc ( + name # The name of the variable. + default # The default value. + initial # The initial value. + module-name ? # The name of the module to document. + : docs * # The documentation for the variable. +) +{ + module-name ?= * ; + + $(module-name).$(name).brief = [ brief-comment $(docs) ] ; + $(module-name).$(name).default = $(default) ; + $(module-name).$(name).initial = $(initial) ; + $(module-name).$(name).docs = $(docs) ; + + if ! $(name) in $($(module-name).variables) + { + $(module-name).variables += $(name) ; + } +} + + +# Generates a general description of the documentation and help system. +# +local rule print-help-top ( ) +{ + print.section "General command line usage" ; + + print.text " bjam [options] [properties] [targets] + + Options, properties and targets can be specified in any order. + " ; + + print.section "Important Options" ; + + print.list-start ; + print.list-item "--clean Remove targets instead of building" ; + print.list-item "-a Rebuild everything" ; + print.list-item "-n Don't execute the commands, only print them" ; + print.list-item "-d+2 Show commands as they are executed" ; + print.list-item "-d0 Supress all informational messages" ; + print.list-item "-q Stop at first error" ; + print.list-item "--debug-configuration Diagnose configuration" ; + print.list-item "--debug-building Report which targets are built with what properties" ; + print.list-item "--debug-generator Diagnose generator search/execution" ; + print.list-end ; + + print.section "Further Help" + The following options can be used to obtain additional documentation. + ; + + print.list-start ; + print.list-item "--help-options Print more obscure command line options." ; + print.list-item "--help-internal Boost.Build implementation details." ; + print.list-item "--help-doc-options Implementation details doc formatting." ; + print.list-end ; +} + + +# Generate Jam/Boost.Jam command usage information. +# +local rule print-help-usage ( ) +{ + print.section "Boost.Jam Usage" + "bjam [ options... ] targets..." + ; + print.list-start ; + print.list-item -a; + Build all targets, even if they are current. ; + print.list-item -fx; + Read '"x"' as the Jamfile for building instead of searching for the + Boost.Build system. ; + print.list-item -jx; + Run up to '"x"' commands concurrently. ; + print.list-item -n; + Do not execute build commands. Instead print out the commands as they + would be executed if building. ; + print.list-item -ox; + Output the used build commands to file '"x"'. ; + print.list-item -q; + Quit as soon as a build failure is encountered. Without this option + Boost.Jam will continue building as many targets as it can. + print.list-item -sx=y; + Sets a Jam variable '"x"' to the value '"y"', overriding any value that + variable would have from the environment. ; + print.list-item -tx; + Rebuild the target '"x"', even if it is up-to-date. ; + print.list-item -v; + Display the version of bjam. ; + print.list-item --x; + Any option not explicitly handled by Boost.Jam remains available to + build scripts using the '"ARGV"' variable. ; + print.list-item -dn; + Enables output of diagnostic messages. The debug level '"n"' and all + below it are enabled by this option. ; + print.list-item -d+n; + Enables output of diagnostic messages. Only the output for debug level + '"n"' is enabled. ; + print.list-end ; + print.section "Debug Levels" + Each debug level shows a different set of information. Usually with + higher levels producing more verbose information. The following levels + are supported: ; + print.list-start ; + print.list-item 0; + Turn off all diagnostic output. Only errors are reported. ; + print.list-item 1; + Show the actions taken for building targets, as they are executed. ; + print.list-item 2; + Show "quiet" actions and display all action text, as they are executed. ; + print.list-item 3; + Show dependency analysis, and target/source timestamps/paths. ; + print.list-item 4; + Show arguments of shell invocations. ; + print.list-item 5; + Show rule invocations and variable expansions. ; + print.list-item 6; + Show directory/header file/archive scans, and attempts at binding to targets. ; + print.list-item 7; + Show variable settings. ; + print.list-item 8; + Show variable fetches, variable expansions, and evaluation of '"if"' expressions. ; + print.list-item 9; + Show variable manipulation, scanner tokens, and memory usage. ; + print.list-item 10; + Show execution times for rules. ; + print.list-item 11; + Show parsing progress of Jamfiles. ; + print.list-item 12; + Show graph for target dependencies. ; + print.list-item 13; + Show changes in target status (fate). ; + print.list-end ; +} + + +# Generates description of options controlling the help system. This +# automatically reads the options as all variables in the doc module of the form +# ".option.*". +# +local rule print-help-options ( + module-name # The doc module. +) +{ + print.section "Help Options" + These are all the options available for enabling or disabling to control + the help system in various ways. Options can be enabled or disabled with + '"--help-enable-<option>"', and "'--help-disable-<option>'" + respectively. + ; + local options-to-list = [ MATCH ^[.]option[.](.*) : $($(module-name).variables) ] ; + if $(options-to-list) + { + print.list-start ; + for local option in [ sequence.insertion-sort $(options-to-list) ] + { + local def = disabled ; + if $($(module-name)..option.$(option).default) != "(empty)" + { + def = enabled ; + } + print.list-item $(option): $($(module-name)..option.$(option).docs) + Default is $(def). ; + } + print.list-end ; + } +} + + +# Generate brief documentation for all the known items in the section for a +# module. Possible sections are: "rules", and "variables". +# +local rule print-help-module-section ( + module # The module name. + section # rules or variables. + : section-head # The title of the section. + section-description * # The detailed description of the section. +) +{ + if $($(module).$(section)) + { + print.section $(section-head) $(section-description) ; + print.list-start ; + for local item in [ sequence.insertion-sort $($(module).$(section)) ] + { + local show = ; + if ! $($(module).$(item).is-local) + { + show = yes ; + } + if $(.option.show-locals) + { + show = yes ; + } + if $(show) + { + print.list-item $(item): $($(module).$(item).brief) ; + } + } + print.list-end ; + } +} + + +# Generate documentation for all possible modules. We attempt to list all known +# modules together with a brief description of each. +# +local rule print-help-all ( + ignored # Usually the module name, but is ignored here. +) +{ + print.section "Modules" + "These are all the known modules. Use --help <module> to get more" + "detailed information." + ; + if $(documented-modules) + { + print.list-start ; + for local module-name in [ sequence.insertion-sort $(documented-modules) ] + { + # The brief docs for each module. + print.list-item $(module-name): $($(module-name).brief) ; + } + print.list-end ; + } + # The documentation for each module when details are requested. + if $(documented-modules) && $(.option.detailed) + { + for local module-name in [ sequence.insertion-sort $(documented-modules) ] + { + # The brief docs for each module. + print-help-module $(module-name) ; + } + } +} + + +# Generate documentation for a module. Basic information about the module is +# generated. +# +local rule print-help-module ( + module-name # The module to generate docs for. +) +{ + # Print the docs. + print.section "Module '$(module-name)'" $($(module-name).docs) ; + + # Print out the documented classes. + print-help-module-section $(module-name) classes : "Module '$(module-name)' classes" + Use --help $(module-name).<class-name> to get more information. ; + + # Print out the documented rules. + print-help-module-section $(module-name) rules : "Module '$(module-name)' rules" + Use --help $(module-name).<rule-name> to get more information. ; + + # Print out the documented variables. + print-help-module-section $(module-name) variables : "Module '$(module-name)' variables" + Use --help $(module-name).<variable-name> to get more information. ; + + # Print out all the same information but indetailed form. + if $(.option.detailed) + { + print-help-classes $(module-name) ; + print-help-rules $(module-name) ; + print-help-variables $(module-name) ; + } +} + + +# Generate documentation for a set of rules in a module. +# +local rule print-help-rules ( + module-name # Module of the rules. + : name * # Optional list of rules to describe. +) +{ + name ?= $($(module-name).rules) ; + if [ set.intersection $(name) : $($(module-name).rules) $($(module-name).class-rules) ] + { + # Print out the given rules. + for local rule-name in [ sequence.insertion-sort $(name) ] + { + if $(.option.show-locals) || ! $($(module-name).$(rule-name).is-local) + { + local signature = $($(module-name).$(rule-name).signature:J=" ") ; + signature ?= "" ; + print.section "Rule '$(module-name).$(rule-name) ( $(signature) )'" + $($(module-name).$(rule-name).docs) ; + if $($(module-name).$(rule-name).args) + { + print.list-start ; + for local arg-name in $($(module-name).$(rule-name).args) + { + print.list-item $(arg-name): $($(module-name).$(rule-name).args.$(arg-name).docs) ; + } + print.list-end ; + } + } + } + } +} + + +# Generate documentation for a set of classes in a module. +# +local rule print-help-classes ( + module-name # Module of the classes. + : name * # Optional list of classes to describe. +) +{ + name ?= $($(module-name).classes) ; + if [ set.intersection $(name) : $($(module-name).classes) ] + { + # Print out the given classes. + for local class-name in [ sequence.insertion-sort $(name) ] + { + if $(.option.show-locals) || ! $($(module-name).$(class-name).is-local) + { + local signature = $($(module-name).$(class-name).signature:J=" ") ; + signature ?= "" ; + print.section "Class '$(module-name).$(class-name) ( $(signature) )'" + $($(module-name).$(class-name).docs) + "Inherits from '"$($(module-name).$(class-name).super-name)"'." ; + if $($(module-name).$(class-name).args) + { + print.list-start ; + for local arg-name in $($(module-name).$(class-name).args) + { + print.list-item $(arg-name): $($(module-name).$(class-name).args.$(arg-name).docs) ; + } + print.list-end ; + } + } + + # Print out the documented rules of the class. + print-help-module-section $(module-name) $(class-name).class-rules : "Class '$(module-name).$(class-name)' rules" + Use --help $(module-name).<rule-name> to get more information. ; + + # Print out all the rules if details are requested. + if $(.option.detailed) + { + print-help-rules $(module-name) : $($(module-name).$(class-name).class-rules) ; + } + } + } +} + + +# Generate documentation for a set of variables in a module. +# +local rule print-help-variables ( + module-name ? # Module of the variables. + : name * # Optional list of variables to describe. +) +{ + name ?= $($(module-name).variables) ; + if [ set.intersection $(name) : $($(module-name).variables) ] + { + # Print out the given variables. + for local variable-name in [ sequence.insertion-sort $(name) ] + { + print.section "Variable '$(module-name).$(variable-name)'" $($(module-name).$(variable-name).docs) ; + if $($(module-name).$(variable-name).default) || + $($(module-name).$(variable-name).initial) + { + print.list-start ; + if $($(module-name).$(variable-name).default) + { + print.list-item "default value:" '$($(module-name).$(variable-name).default:J=" ")' ; + } + if $($(module-name).$(variable-name).initial) + { + print.list-item "initial value:" '$($(module-name).$(variable-name).initial:J=" ")' ; + } + print.list-end ; + } + } + } +} + + +# Generate documentation for a project. +# +local rule print-help-project ( + unused ? + : jamfile * # The project Jamfile. +) +{ + if $(jamfile<$(jamfile)>.docs) + { + # Print the docs. + print.section "Project-specific help" + Project has jamfile at $(jamfile) ; + + print.lines $(jamfile<$(jamfile)>.docs) "" ; + } +} + + +# Generate documentation for a config file. +# +local rule print-help-config ( + unused ? + : type # The type of configuration file user or site. + config-file # The configuration Jamfile. +) +{ + if $(jamfile<$(config-file)>.docs) + { + # Print the docs. + print.section "Configuration help" + Configuration file at $(config-file) ; + + print.lines $(jamfile<$(config-file)>.docs) "" ; + } +} + + +ws = " " ; + +# Extract the text from a block of comments. +# +local rule extract-comment ( + var # The name of the variable to extract from. +) +{ + local comment = ; + local line = $($(var)[1]) ; + local l = [ MATCH "^[$(ws)]*(#)(.*)$" : $(line) ] ; + while $(l[1]) && $($(var)) + { + if $(l[2]) { comment += [ MATCH "^[$(ws)]?(.*)$" : $(l[2]) ] ; } + else { comment += "" ; } + $(var) = $($(var)[2-]) ; + line = $($(var)[1]) ; + l = [ MATCH "^[$(ws)]*(#)(.*)$" : $(line) ] ; + } + return $(comment) ; +} + + +# Extract s single line of Jam syntax, ignoring any comments. +# +local rule extract-syntax ( + var # The name of the variable to extract from. +) +{ + local syntax = ; + local line = $($(var)[1]) ; + while ! $(syntax) && ! [ MATCH "^[$(ws)]*(#)" : $(line) ] && $($(var)) + { + local m = [ MATCH "^[$(ws)]*(.*)$" : $(line) ] ; + if $(m) && ! $(m) = "" + { + syntax = $(m) ; + } + $(var) = $($(var)[2-]) ; + line = $($(var)[1]) ; + } + return $(syntax) ; +} + + +# Extract the next token, this is either a single Jam construct or a comment as +# a single token. +# +local rule extract-token ( + var # The name of the variable to extract from. +) +{ + local parts = ; + while ! $(parts) + { + parts = [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]*(.*)" : $($(var)[1]) ] ; + if ! $(parts) + { + $(var) = $($(var)[2-]) ; + } + } + local token = ; + if [ MATCH "^(#)" : $(parts[1]) ] + { + token = $(parts:J=" ") ; + $(var) = $($(var)[2-]) ; + } + else + { + token = $(parts[1]) ; + $(var) = $(parts[2-]:J=" ") $($(var)[2-]) ; + } + return $(token) ; +} + + +# Scan for a rule declaration as the next item in the variable. +# +local rule scan-rule ( + syntax ? # The first part of the text which contains the rule declaration. + : var # The name of the variable to extract from. +) +{ + local rule-parts = + [ MATCH "^[$(ws)]*(rule|local[$(ws)]*rule)[$(ws)]+([^$(ws)]+)[$(ws)]*(.*)" : $(syntax:J=" ") ] ; + if $(rule-parts[1]) + { + # Mark as doc for rule. + local rule-name = $(rule-parts[2]) ; + if $(scope-name) + { + rule-name = $(scope-name).$(rule-name) ; + } + local is-local = [ MATCH "^(local).*" : $(rule-parts[1]) ] ; + if $(comment-block) + { + set-rule-doc $(rule-name) $(module-name) $(is-local) : $(comment-block) ; + } + # Parse args of rule. + $(var) = $(rule-parts[3-]) $($(var)) ; + set-rule-arguments-signature $(rule-name) $(module-name) : [ scan-rule-arguments $(var) ] ; + # Scan within this rules scope. + local scope-level = [ extract-token $(var) ] ; + local scope-name = $(rule-name) ; + while $(scope-level) + { + local comment-block = [ extract-comment $(var) ] ; + local syntax-block = [ extract-syntax $(var) ] ; + if [ scan-rule $(syntax-block) : $(var) ] + { + } + else if [ MATCH "^(\\{)" : $(syntax-block) ] + { + scope-level += "{" ; + } + else if [ MATCH "^[^\\}]*([\\}])[$(ws)]*$" : $(syntax-block) ] + { + scope-level = $(scope-level[2-]) ; + } + } + + return true ; + } +} + + +# Scan the arguments of a rule. +# +local rule scan-rule-arguments ( + var # The name of the variable to extract from. +) +{ + local arg-syntax = ; + local token = [ extract-token $(var) ] ; + while $(token) != "(" && $(token) != "{" + { + token = [ extract-token $(var) ] ; + } + if $(token) != "{" + { + token = [ extract-token $(var) ] ; + } + local arg-signature = ; + while $(token) != ")" && $(token) != "{" + { + local arg-name = ; + local arg-qualifier = " " ; + local arg-doc = ; + if $(token) = ":" + { + arg-signature += $(token) ; + token = [ extract-token $(var) ] ; + } + arg-name = $(token) ; + arg-signature += $(token) ; + token = [ extract-token $(var) ] ; + if [ MATCH "^([\\*\\+\\?])" : $(token) ] + { + arg-qualifier = $(token) ; + arg-signature += $(token) ; + token = [ extract-token $(var) ] ; + } + if $(token) = ":" + { + arg-signature += $(token) ; + token = [ extract-token $(var) ] ; + } + if [ MATCH "^(#)" : $(token) ] + { + $(var) = $(token) $($(var)) ; + arg-doc = [ extract-comment $(var) ] ; + token = [ extract-token $(var) ] ; + } + set-argument-doc $(arg-name) $(arg-qualifier) $(rule-name) $(module-name) : $(arg-doc) ; + } + while $(token) != "{" + { + token = [ extract-token $(var) ] ; + } + $(var) = "{" $($(var)) ; + arg-signature ?= "" ; + return $(arg-signature) ; +} + + +# Scan for a variable declaration. +# +local rule scan-variable ( + syntax ? # The first part of the text which contains the variable declaration. + : var # The name of the variable to extract from. +) +{ + # [1] = name, [2] = value(s) + local var-parts = + [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]+([\\?\\=]*)[$(ws)]+([^\\;]*)\\;" : $(syntax) ] ; + if $(var-parts) + { + local value = [ MATCH "^(.*)[ ]$" : $(var-parts[3-]:J=" ") ] ; + local default-value = "" ; + local initial-valie = "" ; + if $(var-parts[2]) = "?=" + { + default-value = $(value) ; + default-value ?= "(empty)" ; + } + else + { + initial-value = $(value) ; + initial-value ?= "(empty)" ; + } + if $(comment-block) + { + set-variable-doc $(var-parts[1]) $(default-value) $(initial-value) $(module-name) : $(comment-block) ; + } + return true ; + } +} + + +# Scan a class declaration. +# +local rule scan-class ( + syntax ? # The syntax text for the class declaration. +) +{ + # [1] = class?, [2] = name, [3] = superclass + local class-parts = + [ MATCH "^[$(ws)]*([^$(ws)]+)[$(ws)]+([^$(ws)]+)[$(ws)]+:*[$(ws)]*([^$(ws);]*)" : $(syntax) ] ; + if $(class-parts[1]) = "class" || $(class-parts[1]) = "class.class" + { + set-class-doc $(class-parts[2]) $(module-name) : $(class-parts[3]) ; + } +} + + +# Scan a module file for documentation comments. This also invokes any actions +# assigned to the module. The actions are the rules that do the actual output of +# the documentation. This rule is invoked as the header scan rule for the module +# file. +# +rule scan-module ( + target # The module file. + : text * # The text in the file, one item per line. + : action * # Rule to call to output docs for the module. +) +{ + if $(.option.debug) { ECHO "HELP:" scanning module target '$(target)' ; } + local module-name = $(target:B) ; + local module-documented = ; + local comment-block = ; + local syntax-block = ; + # This is a hack because we can not get the line of a file if it happens to + # not have a new-line termination. + text += "}" ; + while $(text) + { + comment-block = [ extract-comment text ] ; + syntax-block = [ extract-syntax text ] ; + if $(.option.debug) + { + ECHO "HELP:" comment block; '$(comment-block)' ; + ECHO "HELP:" syntax block; '$(syntax-block)' ; + } + if [ scan-rule $(syntax-block) : text ] { } + else if [ scan-variable $(syntax-block) : text ] { } + else if [ scan-class $(syntax-block) ] { } + else if [ MATCH .*([cC]opyright).* : $(comment-block:J=" ") ] + { + # mark as the copy for the module. + set-module-copyright $(module-name) : $(comment-block) ; + } + else if $(action[1]) in "print-help-project" "print-help-config" + && ! $(jamfile<$(target)>.docs) + { + # special module docs for the project jamfile. + jamfile<$(target)>.docs = $(comment-block) ; + } + else if ! $(module-documented) + { + # document the module. + set-module-doc $(module-name) : $(comment-block) ; + module-documented = true ; + } + } + if $(action) + { + $(action[1]) $(module-name) : $(action[2-]) ; + } +} + + +# Import scan-module to global scope, so that it is available during header +# scanning phase. +# +IMPORT $(__name__) : scan-module : : doc.scan-module ; + + +# Read in a file using the SHELL builtin and return the individual lines as +# would be done for header scanning. +# +local rule read-file ( + file # The file to read in. +) +{ + file = [ path.native [ path.root [ path.make $(file) ] [ path.pwd ] ] ] ; + if ! $(.file<$(file)>.lines) + { + local content ; + switch [ modules.peek : OS ] + { + case NT : + content = [ SHELL "TYPE \"$(file)\"" ] ; + + case * : + content = [ SHELL "cat \"$(file)\"" ] ; + } + local lines ; + local nl = " +" ; + local << = "([^$(nl)]*)[$(nl)](.*)" ; + local line+ = [ MATCH "$(<<)" : "$(content)" ] ; + while $(line+) + { + lines += $(line+[1]) ; + line+ = [ MATCH "$(<<)" : "$(line+[2])" ] ; + } + .file<$(file)>.lines = $(lines) ; + } + return $(.file<$(file)>.lines) ; +} + + +# Add a scan action to perform to generate the help documentation. The action +# rule is passed the name of the module as the first argument. The second +# argument(s) are optional and passed directly as specified here. +# +local rule do-scan ( + modules + # The modules to scan and perform the action on. + : action * # The action rule, plus the secondary arguments to pass to the action rule. +) +{ + if $(help-output) = text + { + print.output $(help-output-file).txt plain ; + ALWAYS $(help-output-file).txt ; + DEPENDS all : $(help-output-file).txt ; + } + if $(help-output) = html + { + print.output $(help-output-file).html html ; + ALWAYS $(help-output-file).html ; + DEPENDS all : $(help-output-file).html ; + } + for local module-file in $(modules[1--2]) + { + scan-module $(module-file) : [ read-file $(module-file) ] ; + } + scan-module $(modules[-1]) : [ read-file $(modules[-1]) ] : $(action) ; +} diff --git a/jam-files/boost-build/util/indirect.jam b/jam-files/boost-build/util/indirect.jam new file mode 100644 index 00000000..ec63f192 --- /dev/null +++ b/jam-files/boost-build/util/indirect.jam @@ -0,0 +1,115 @@ +# Copyright 2003 Dave Abrahams +# Copyright 2003 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +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 ? ) +{ + 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) ] ; +} + + +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 ; +} diff --git a/jam-files/boost-build/util/indirect.py b/jam-files/boost-build/util/indirect.py new file mode 100644 index 00000000..78fa8994 --- /dev/null +++ b/jam-files/boost-build/util/indirect.py @@ -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_1_0.txt or http://www.boost.org/LICENSE_1_0.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:])) diff --git a/jam-files/boost-build/util/logger.py b/jam-files/boost-build/util/logger.py new file mode 100644 index 00000000..de652129 --- /dev/null +++ b/jam-files/boost-build/util/logger.py @@ -0,0 +1,46 @@ +# Copyright Pedro Ferreira 2005. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +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 diff --git a/jam-files/boost-build/util/numbers.jam b/jam-files/boost-build/util/numbers.jam new file mode 100644 index 00000000..665347d3 --- /dev/null +++ b/jam-files/boost-build/util/numbers.jam @@ -0,0 +1,218 @@ +# Copyright 2001, 2002 Dave Abrahams +# Copyright 2002, 2003 Vladimir Prus +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +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 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 ; + + # 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) ; + } + } +} diff --git a/jam-files/boost-build/util/option.jam b/jam-files/boost-build/util/option.jam new file mode 100644 index 00000000..f6dc3752 --- /dev/null +++ b/jam-files/boost-build/util/option.jam @@ -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_1_0.txt or +# http://www.boost.org/LICENSE_1_0.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) ; +} diff --git a/jam-files/boost-build/util/option.py b/jam-files/boost-build/util/option.py new file mode 100644 index 00000000..47d6abdf --- /dev/null +++ b/jam-files/boost-build/util/option.py @@ -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_1_0.txt or +# http://www.boost.org/LICENSE_1_0.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.has_key(name) and options[name] != None: + return options[name] + else: + return default_value diff --git a/jam-files/boost-build/util/order.jam b/jam-files/boost-build/util/order.jam new file mode 100644 index 00000000..a74fc8c8 --- /dev/null +++ b/jam-files/boost-build/util/order.jam @@ -0,0 +1,169 @@ +# Copyright (C) 2003 Vladimir Prus +# Use, modification, and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +# at http://www.boost.org/LICENSE_1_0.txt) + +# 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 contraints 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 ; +} diff --git a/jam-files/boost-build/util/order.py b/jam-files/boost-build/util/order.py new file mode 100644 index 00000000..4e67b3f1 --- /dev/null +++ b/jam-files/boost-build/util/order.py @@ -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_1_0.txt or copy +# at http://www.boost.org/LICENSE_1_0.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 containts 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 diff --git a/jam-files/boost-build/util/os.jam b/jam-files/boost-build/util/os.jam new file mode 100644 index 00000000..daef27f7 --- /dev/null +++ b/jam-files/boost-build/util/os.jam @@ -0,0 +1,171 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.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 + ) +{ + return [ modules.peek .ENVIRON : $(variable-names) ] ; +} + +.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 = ";" ; +.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 ; + +# 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) ; +} + + +if ! [ on-windows ] +{ + .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 ; +} diff --git a/jam-files/boost-build/util/os_j.py b/jam-files/boost-build/util/os_j.py new file mode 100644 index 00000000..f44cca62 --- /dev/null +++ b/jam-files/boost-build/util/os_j.py @@ -0,0 +1,19 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +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 diff --git a/jam-files/boost-build/util/path.jam b/jam-files/boost-build/util/path.jam new file mode 100644 index 00000000..ea26b816 --- /dev/null +++ b/jam-files/boost-build/util/path.jam @@ -0,0 +1,934 @@ +# Copyright Vladimir Prus 2002-2006. +# Copyright Dave Abrahams 2003-2004. +# Copyright Rene Rivera 2003-2006. +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# Performs various path manipulations. Paths are always in a 'normalized' +# 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 errors ; +import modules ; +import regex ; +import sequence ; +import set ; +import version ; + + +os = [ modules.peek : OS ] ; +if [ modules.peek : UNIX ] +{ + local uname = [ modules.peek : JAMUNAME ] ; + switch $(uname) + { + case CYGWIN* : os = CYGWIN ; + case * : os = UNIX ; + } +} + + +# Converts the native path into normalized form. +# +rule make ( native ) +{ + return [ make-$(os) $(native) ] ; +} + + +# Builds native representation of the path. +# +rule native ( path ) +{ + return [ native-$(os) $(path) ] ; +} + + +# 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 + { + errors.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) : "/" ] ; + } +} + + +# Concatenates the passed path elements. Generates an error if any element other +# than the first one is rooted. Skips any empty or undefined path elements. +# +rule join ( elements + ) +{ + if ! $(elements[2-]) + { + return $(elements[1]) ; + } + else + { + for local e in $(elements[2-]) + { + if [ is-rooted $(e) ] + { + errors.error only the first element may be rooted ; + } + } + if [ version.check-jam-version 3 1 17 ] + { + return [ NORMALIZE_PATH "$(elements)" ] ; + } + else + { + # Boost Jam prior to version 3.1.17 had problems with its + # NORMALIZE_PATH rule in case you passed it a leading backslash + # instead of a slash, in some cases when you sent it an empty + # initial path element and possibly some others. At least some of + # those cases were being hit and relied upon when calling this rule + # from the path.make-NT rule. + if ! $(elements[1]) && $(elements[2]) + { + return [ NORMALIZE_PATH "/" "$(elements[2-])" ] ; + } + else + { + return [ NORMALIZE_PATH "$(elements)" ] ; + } + } + } +} + + +# If 'path' is relative, it is rooted at 'root'. Otherwise, it is unchanged. +# +rule root ( path root ) +{ + if [ is-rooted $(path) ] + { + return $(path) ; + } + else + { + return [ join $(root) $(path) ] ; + } +} + + +# Returns the current working directory. +# +rule pwd ( ) +{ + if ! $(.pwd) + { + .pwd = [ make [ PWD ] ] ; + } + return $(.pwd) ; +} + + +# 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 ] +# +rule glob ( dirs * : patterns + : exclude-patterns * ) +{ + local result ; + local real-patterns ; + local real-exclude-patterns ; + for local d in $(dirs) + { + for local p in $(patterns) + { + local pattern = [ path.root $(p) $(d) ] ; + real-patterns += [ path.native $(pattern) ] ; + } + + for local p in $(exclude-patterns) + { + local pattern = [ path.root $(p) $(d) ] ; + real-exclude-patterns += [ path.native $(pattern) ] ; + } + } + + local inc = [ GLOB-RECURSIVELY $(real-patterns) ] ; + inc = [ sequence.transform NORMALIZE_PATH : $(inc) ] ; + local exc = [ GLOB-RECURSIVELY $(real-exclude-patterns) ] ; + exc = [ sequence.transform NORMALIZE_PATH : $(exc) ] ; + + return [ sequence.transform path.make : [ set.difference $(inc) : $(exc) ] ] + ; +} + + +# 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. +# +rule glob-tree ( roots * : patterns + : exclude-patterns * ) +{ + return [ sequence.transform path.make : [ .glob-tree [ sequence.transform + path.native : $(roots) ] : $(patterns) : $(exclude-patterns) ] ] ; +} + + +local rule .glob-tree ( roots * : patterns * : exclude-patterns * ) +{ + local excluded ; + if $(exclude-patterns) + { + excluded = [ GLOB $(roots) : $(exclude-patterns) ] ; + } + local result = [ set.difference [ GLOB $(roots) : $(patterns) ] : + $(excluded) ] ; + local subdirs ; + for local d in [ set.difference [ GLOB $(roots) : * ] : $(excluded) ] + { + if ! ( $(d:D=) in . .. ) && ! [ CHECK_IF_FILE $(d) ] + { + subdirs += $(d) ; + } + } + if $(subdirs) + { + result += [ .glob-tree $(subdirs) : $(patterns) : $(exclude-patterns) ] + ; + } + return $(result) ; +} + + +# Returns true is the specified file exists. +# +rule exists ( file ) +{ + return [ path.glob $(file:D) : $(file:D=) ] ; +} +NATIVE_RULE path : exists ; + + +# 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. +# +rule all-parents ( path : upper_limit ? : cwd ? ) +{ + cwd ?= [ pwd ] ; + local path_ele = [ regex.split [ root $(path) $(cwd) ] "/" ] ; + + if ! $(upper_limit) + { + upper_limit = / ; + } + local upper_ele = [ regex.split [ root $(upper_limit) $(cwd) ] "/" ] ; + + # Leave only elements in 'path_ele' below 'upper_ele'. + while $(path_ele) && ( $(upper_ele[1]) = $(path_ele[1]) ) + { + upper_ele = $(upper_ele[2-]) ; + path_ele = $(path_ele[2-]) ; + } + + # Have all upper elements been removed ? + if $(upper_ele) + { + errors.error "$(upper_limit) is not prefix of $(path)" ; + } + + # Create the relative paths to parents, number of elements in 'path_ele'. + local result ; + for local i in $(path_ele) + { + path = [ parent $(path) ] ; + result += $(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. +# +rule glob-in-parents ( dir : patterns + : upper-limit ? ) +{ + local result ; + local parent-dirs = [ all-parents $(dir) : $(upper-limit) ] ; + + while $(parent-dirs) && ! $(result) + { + result = [ glob $(parent-dirs[1]) : $(patterns) ] ; + parent-dirs = $(parent-dirs[2-]) ; + } + return $(result) ; +} + + +# Assuming 'child' is a subdirectory of 'parent', return the relative path from +# 'parent' to 'child'. +# +rule relative ( child parent : no-error ? ) +{ + local not-a-child ; + 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 + { + not-a-child = true ; + split1 = ; + } + } + if $(split2) + { + if $(not-a-child) + { + if $(no-error) + { + return not-a-child ; + } + else + { + errors.error $(child) is not a subdir of $(parent) ; + } + } + else + { + return [ join $(split2) ] ; + } + } + else + { + return "." ; + } + } +} + + +# 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. +# +rule programs-path ( ) +{ + local result ; + local raw = [ modules.peek : PATH Path path ] ; + for local p in $(raw) + { + if $(p) + { + result += [ path.make $(p) ] ; + } + } + return $(result) ; +} + +rule makedirs ( path ) +{ + local result = true ; + local native = [ native $(path) ] ; + if ! [ exists $(native) ] + { + if [ makedirs [ parent $(path) ] ] + { + if ! [ MAKEDIR $(native) ] + { + errors.error "Could not create directory '$(path)'" ; + result = ; + } + } + } + return $(result) ; +} + +# Converts native Windows paths into our internal canonic path representation. +# Supports 'invalid' paths containing multiple successive path separator +# characters. +# +# TODO: Check and if needed add support for Windows 'X:file' path format where +# the file is located in the current folder on drive X. +# +rule make-NT ( native ) +{ + local result ; + + if [ version.check-jam-version 3 1 17 ] + { + result = [ NORMALIZE_PATH $(native) ] ; + } + else + { + # This old implementation is really fragile due to a not so clear way + # NORMALIZE_PATH rule worked in Boost.Jam versions prior to 3.1.17. E.g. + # path.join would mostly ignore empty path elements but would root the + # joined path in case the initial two path elements were empty or some + # similar accidental wierdness. + result = [ path.join [ regex.split $(native) "[/\\]" ] ] ; + } + + # We need to add an extra '/' in front in case this is a rooted Windows path + # starting with a drive letter and not a path separator character since the + # builtin NORMALIZE_PATH rule has no knowledge of this leading drive letter + # and treats it as a regular folder name. + if [ regex.match "(^.:)" : $(native) ] + { + result = /$(result) ; + } + + return $(result) ; +} + + +rule native-NT ( path ) +{ + local result ; + if [ is-rooted $(path) ] && ! [ regex.match "^/(.:)" : $(path) ] + { + result = $(path) ; + } + else + { + result = [ MATCH "^/?(.*)" : $(path) ] ; + } + result = [ sequence.join [ regex.split $(result) "/" ] : "\\" ] ; + return $(result) ; +} + + +rule make-UNIX ( native ) +{ + # VP: I have no idea now 'native' can be empty here! But it can! + if ! $(native) + { + errors.error "Empty path passed to 'make-UNIX'" ; + } + else + { + return [ NORMALIZE_PATH $(native:T) ] ; + } +} + + +rule native-UNIX ( path ) +{ + return $(path) ; +} + + +rule make-CYGWIN ( path ) +{ + return [ make-NT $(path) ] ; +} + + +rule native-CYGWIN ( path ) +{ + local result = $(path) ; + if [ regex.match "(^/.:)" : $(path) ] # Windows absolute path. + { + result = [ MATCH "^/?(.*)" : $(path) ] ; # Remove leading '/'. + } + return [ native-UNIX $(result) ] ; +} + + +# split-path-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 from 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\\" ; + assert.result "foo" : make "foo/." ; + assert.result "foo" : make "foo/bar/.." ; + assert.result "foo" : make "foo/bar/../" ; + assert.result "foo" : make "foo/bar/..\\" ; + assert.result "foo/bar" : make "foo/././././bar" ; + assert.result "/foo" : make "\\foo" ; + 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" ; + + # Test processing 'invalid' paths containing multiple successive path + # separators. + assert.result "foo" : make "foo//" ; + assert.result "foo" : make "foo///" ; + assert.result "foo" : make "foo\\\\" ; + assert.result "foo" : make "foo\\\\\\" ; + assert.result "/foo" : make "//foo" ; + assert.result "/foo" : make "///foo" ; + assert.result "/foo" : make "\\\\foo" ; + assert.result "/foo" : make "\\\\\\foo" ; + assert.result "/foo" : make "\\/\\/foo" ; + assert.result "foo/bar" : make "foo//\\//\\\\bar//\\//\\\\\\//\\//\\\\" ; + assert.result "foo" : make "foo/bar//.." ; + assert.result "foo/bar" : make "foo/bar/giz//.." ; + assert.result "foo/giz" : make "foo//\\//\\\\bar///\\\\//\\\\////\\/..///giz\\//\\\\\\//\\//\\\\" ; + assert.result "../../../foo" : make "..///.//..///.//..////foo///" ; + + # Test processing 'invalid' rooted paths with too many '..' path elements + # that would place them before the root. + assert.result : make "/.." ; + assert.result : make "/../" ; + assert.result : make "/../." ; + assert.result : make "/.././" ; + assert.result : make "/foo/../bar/giz/.././././../../." ; + assert.result : make "/foo/../bar/giz/.././././../.././" ; + assert.result : make "//foo/../bar/giz/.././././../../." ; + assert.result : make "//foo/../bar/giz/.././././../.././" ; + assert.result : make "\\\\foo/../bar/giz/.././././../../." ; + assert.result : make "\\\\foo/../bar/giz/.././././../.././" ; + assert.result : make "/..///.//..///.//..////foo///" ; + + assert.result "foo\\bar\\giz" : native "foo/bar/giz" ; + assert.result "foo" : native "foo" ; + 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) ; +} diff --git a/jam-files/boost-build/util/path.py b/jam-files/boost-build/util/path.py new file mode 100644 index 00000000..222b96bf --- /dev/null +++ b/jam-files/boost-build/util/path.py @@ -0,0 +1,904 @@ +# 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) + +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) : "/" ] ; +# } +# } +# +# # +# # Auxillary rule: does all the semantic of 'join', except for error cheching. +# # 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): + 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, ["*"]) 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 "." diff --git a/jam-files/boost-build/util/print.jam b/jam-files/boost-build/util/print.jam new file mode 100644 index 00000000..708d21ab --- /dev/null +++ b/jam-files/boost-build/util/print.jam @@ -0,0 +1,488 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.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 ; + +# 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 ] ; + + +# 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 one. + 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 = " " ; } + else if $(c) = " " { c = " " ; } + indent = $(indent)$(c) ; + } + local html-text = [ escape-html $(text) : " " ] ; + 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 independant 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) = ">>" ; + if $(overwrite) + { + text-redirect on $(output-target) = ">" ; + } + 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) "&" "&" ] ; + html = [ regex.replace $(html) "<" "<" ] ; + html = [ regex.replace $(html) ">" ">" ] ; + 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) "$(<)" +} + + +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're 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) none ; + 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 + { + 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 two three" "&<>" : + # escape-html "one two three" "&<>" ; +} diff --git a/jam-files/boost-build/util/regex.jam b/jam-files/boost-build/util/regex.jam new file mode 100644 index 00000000..234c36f6 --- /dev/null +++ b/jam-files/boost-build/util/regex.jam @@ -0,0 +1,193 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.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) ; +} + + +# 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' agains the 'pattern' and returns a list of +# elements indicated by indices of all successful matches. If 'indices' is +# omitted returns a list of first paranthethised 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) ; +} + + +# 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 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 string " : replace "string string " " " " " ; + assert.result " string string" : replace " string string" " " " " ; + assert.result "string string" : replace "string string" " " " " ; + assert.result "-" : replace "&" "&" "-" ; + + assert.result "-" "a-b" : replace-list "&" "a&b" : "&" : "-" ; +} diff --git a/jam-files/boost-build/util/regex.py b/jam-files/boost-build/util/regex.py new file mode 100644 index 00000000..29e26ecf --- /dev/null +++ b/jam-files/boost-build/util/regex.py @@ -0,0 +1,25 @@ +# (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 + +def transform (list, pattern, indices = [1]): + """ Matches all elements of 'list' agains the 'pattern' + and returns a list of the elements indicated by indices of + all successfull matches. If 'indices' is omitted returns + a list of first paranthethised groups of all successfull + matches. + """ + result = [] + + for e in list: + m = re.match (pattern, e) + + if m: + for i in indices: + result.append (m.group (i)) + + return result + diff --git a/jam-files/boost-build/util/sequence.jam b/jam-files/boost-build/util/sequence.jam new file mode 100644 index 00000000..73919a65 --- /dev/null +++ b/jam-files/boost-build/util/sequence.jam @@ -0,0 +1,335 @@ +# 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_1_0.txt or http://www.boost.org/LICENSE_1_0.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) ; +} + + +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__) ; +} + + +# 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) ; +} + + +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 ; + } +} diff --git a/jam-files/boost-build/util/sequence.py b/jam-files/boost-build/util/sequence.py new file mode 100644 index 00000000..1d32efd2 --- /dev/null +++ b/jam-files/boost-build/util/sequence.py @@ -0,0 +1,50 @@ +# (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 + +def unique (values, stable=False): + 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. + """ + 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'. + """ + 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 diff --git a/jam-files/boost-build/util/set.jam b/jam-files/boost-build/util/set.jam new file mode 100644 index 00000000..fc179134 --- /dev/null +++ b/jam-files/boost-build/util/set.jam @@ -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_1_0.txt or http://www.boost.org/LICENSE_1_0.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 ; +} diff --git a/jam-files/boost-build/util/set.py b/jam-files/boost-build/util/set.py new file mode 100644 index 00000000..dc7cf328 --- /dev/null +++ b/jam-files/boost-build/util/set.py @@ -0,0 +1,42 @@ +# (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 utility import to_seq + +def difference (b, a): + """ Returns the elements of B that are not in A. + """ + result = [] + for element in b: + if not element in a: + result.append (element) + + return result + +def intersection (set1, set2): + """ Removes from set1 any items which don't appear in set2 and returns the result. + """ + 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. + """ + return contains (a, b) and contains (b, a) diff --git a/jam-files/boost-build/util/string.jam b/jam-files/boost-build/util/string.jam new file mode 100644 index 00000000..a39ed119 --- /dev/null +++ b/jam-files/boost-build/util/string.jam @@ -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_1_0.txt or http://www.boost.org/LICENSE_1_0.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 ; +} diff --git a/jam-files/boost-build/util/utility.jam b/jam-files/boost-build/util/utility.jam new file mode 100644 index 00000000..c46747f5 --- /dev/null +++ b/jam-files/boost-build/util/utility.jam @@ -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_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +import "class" : is-instance ; +import errors ; + + +# 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) + { + errors.error "in ungrist $(names) : $(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 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 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 <> ; + } + 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 : foo is not of the form <.*>" ; + + try ; + { + ungrist foo <bar> ; + } + catch "in ungrist foo : foo is not of the form <.*>" ; + + try ; + { + ungrist <foo> bar ; + } + catch "in ungrist bar : bar is not of the form <.*>" ; +} diff --git a/jam-files/boost-build/util/utility.py b/jam-files/boost-build/util/utility.py new file mode 100644 index 00000000..afea765b --- /dev/null +++ b/jam-files/boost-build/util/utility.py @@ -0,0 +1,155 @@ +# (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 * + +__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 + """ + + 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. + """ + def replace_grist_one (name, new_grist): + split = __re_grist_and_value.match (name) + if not split: + return new_grist + name + else: + return new_grist + split.group (2) + + if isinstance (features, str): + return replace_grist_one (features, new_grist) + else: + return [ replace_grist_one (feature, new_grist) for feature in features ] + +def get_value (property): + """ Gets the value of a property, that is, the part following the grist, if any. + """ + 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. + """ + 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. + """ + 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. + """ + split = os.path.splitext (name) + return split [0] + new_suffix + +def forward_slashes (s): + """ Converts all backslashes to forward slashes. + """ + return __re_backslash.sub ('/', s) + + +def split_action_id (id): + """ Splits an id in the toolset and specific rule parts. E.g. + 'gcc.compile.c++' returns ('gcc', 'compile.c++') + """ + 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 |