diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 6 | ||||
-rw-r--r-- | src/engine/MANIFEST.in | 7 | ||||
-rw-r--r-- | src/engine/SCons/Options/BoolOption.py | 88 | ||||
-rw-r--r-- | src/engine/SCons/Options/BoolOptionTests.py | 121 | ||||
-rw-r--r-- | src/engine/SCons/Options/EnumOption.py | 101 | ||||
-rw-r--r-- | src/engine/SCons/Options/EnumOptionTests.py | 198 | ||||
-rw-r--r-- | src/engine/SCons/Options/ListOption.py | 131 | ||||
-rw-r--r-- | src/engine/SCons/Options/ListOptionTests.py | 101 | ||||
-rw-r--r-- | src/engine/SCons/Options/OptionsTests.py (renamed from src/engine/SCons/OptionsTests.py) | 25 | ||||
-rw-r--r-- | src/engine/SCons/Options/PackageOption.py | 106 | ||||
-rw-r--r-- | src/engine/SCons/Options/PackageOptionTests.py | 109 | ||||
-rw-r--r-- | src/engine/SCons/Options/PathOption.py | 85 | ||||
-rw-r--r-- | src/engine/SCons/Options/PathOptionTests.py | 70 | ||||
-rw-r--r-- | src/engine/SCons/Options/__init__.py (renamed from src/engine/SCons/Options.py) | 62 | ||||
-rw-r--r-- | src/engine/SCons/Script/SConscript.py | 7 | ||||
-rw-r--r-- | src/setup.py | 1 |
16 files changed, 1202 insertions, 16 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c81e870..2905339 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -10,6 +10,12 @@ RELEASE X.XX - XXX, XX XXX XXXX XX:XX:XX -XXXX + From Hartmut Goebel: + + - Add several new types of canned functions to help create options: + BoolOption(), EnumOption(), ListOption(), PackageOption(), + PathOption(). + From Steven Knight: - Fix use of CPPDEFINES with C++ source files. diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 24e3d76..c5fe7a3 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -16,7 +16,12 @@ SCons/Optik/__init__.py SCons/Optik/errors.py SCons/Optik/option.py SCons/Optik/option_parser.py -SCons/Options.py +SCons/Options/__init__.py +SCons/Options/BoolOption.py +SCons/Options/EnumOption.py +SCons/Options/ListOption.py +SCons/Options/PackageOption.py +SCons/Options/PathOption.py SCons/Platform/__init__.py SCons/Platform/aix.py SCons/Platform/cygwin.py diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py new file mode 100644 index 0000000..f38bf02 --- /dev/null +++ b/src/engine/SCons/Options/BoolOption.py @@ -0,0 +1,88 @@ +"""engine.SCons.Options.BoolOption + +This file defines the option type for SCons implementing true/false values. + +Usage example: + + opts = Options() + opts.Add(BoolOption('embedded', 'build for an embedded system', 0)) + ... + if env['embedded'] == 1: + ... +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__all__ = ('BoolOption', 'True', 'False') + +import string + +import SCons.Errors + +__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) +__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') + +# we need this since SCons should work version indepentant +True, False = 1, 0 + + +def _text2bool(val): + """ + Converts strings to True/False depending on the 'truth' expressed by + the string. If the string can't be converted, the original value + will be returned. + + See '__true_strings' and '__false_strings' for values considered + 'true' or 'false respectivly. + + This is usable as 'converter' for SCons' Options. + """ + lval = string.lower(val) + if lval in __true_strings: return True + if lval in __false_strings: return False + raise ValueError("Invalid value for boolean option: %s" % val) + + +def _validator(key, val, env): + """ + Validates the given value to be either '0' or '1'. + + This is usable as 'validator' for SCons' Options. + """ + if not env[key] in (True, False): + raise SCons.Errors.UserError( + 'Invalid value for boolean option %s: %s' % (key, env[key])) + + +def BoolOption(key, help, default): + """ + The input parameters describe a boolen option, thus they are + returned with the correct converter and validator appended. The + 'help' text will by appended by '(yes|no) to show the valid + valued. The result is usable for input to opts.Add(). + """ + return (key, '%s (yes|no)' % help, default, + _validator, _text2bool) diff --git a/src/engine/SCons/Options/BoolOptionTests.py b/src/engine/SCons/Options/BoolOptionTests.py new file mode 100644 index 0000000..845f251 --- /dev/null +++ b/src/engine/SCons/Options/BoolOptionTests.py @@ -0,0 +1,121 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import unittest + +import SCons.Errors +import SCons.Options + +class BoolOptionTestCase(unittest.TestCase): + def test_BoolOption(self): + """Test BoolOption creation""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.BoolOption('test', 'test option help', 0)) + + o = opts.options[0] + assert o.key == 'test', o.key + assert o.help == 'test option help (yes|no)', o.help + assert o.default == 0, o.default + assert not o.validator is None, o.validator + assert not o.converter is None, o.converter + + def test_converter(self): + """Test the BoolOption converter""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.BoolOption('test', 'test option help', 0)) + + o = opts.options[0] + + true_values = [ + 'y', 'Y', + 'yes', 'YES', + 't', 'T', + 'true', 'TRUE', + 'on', 'ON', + 'all', 'ALL', + '1', + ] + false_values = [ + 'n', 'N', + 'no', 'NO', + 'f', 'F', + 'false', 'FALSE', + 'off', 'OFF', + 'none', 'NONE', + '0', + ] + + for t in true_values: + x = o.converter(t) + assert x, "converter returned false for '%s'" % t + + for f in false_values: + x = o.converter(f) + assert not x, "converter returned true for '%s'" % f + + caught = None + try: + o.converter('x') + except ValueError: + caught = 1 + assert caught, "did not catch expected ValueError" + + def test_validator(self): + """Test the BoolOption validator""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.BoolOption('test', 'test option help', 0)) + + o = opts.options[0] + + env = { + 'T' : SCons.Options.True, + 'F' : SCons.Options.False, + 'N' : 'xyzzy', + } + + o.validator('T', 0, env) + + o.validator('F', 0, env) + + caught = None + try: + o.validator('N', 0, env) + except SCons.Errors.UserError: + caught = 1 + assert caught, "did not catch expected UserError for N" + + caught = None + try: + o.validator('NOSUCHKEY', 0, env) + except KeyError: + caught = 1 + assert caught, "did not catch expected KeyError for NOSUCHKEY" + + +if __name__ == "__main__": + suite = unittest.makeSuite(BoolOptionTestCase, 'test_') + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Options/EnumOption.py b/src/engine/SCons/Options/EnumOption.py new file mode 100644 index 0000000..d4e2ac1 --- /dev/null +++ b/src/engine/SCons/Options/EnumOption.py @@ -0,0 +1,101 @@ +"""engine.SCons.Options.EnumOption + +This file defines the option type for SCons allowing only specified +input-values. + +Usage example: + + opts = Options() + opts.Add(EnumOption('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=2)) + ... + if env['debug'] == 'full': + ... +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__all__ = ('EnumOption',) + +import string + +import SCons.Errors + +def _validator(key, val, env, vals): + if not val in vals: + raise SCons.Errors.UserError( + 'Invalid value for option %s: %s' % (key, val)) + + +def EnumOption(key, help, default, allowed_values, map={}, ignorecase=0): + """ + The input parameters describe a option with only certain values + allowed. They are returned with an appropriate converter and + validator appended. The result is usable for input to + Options.Add(). + + 'key' and 'default' are the values to be passed on to Options.Add(). + + 'help' will be appended by the allowed values automatically + + 'allowed_values' is a list of strings, which are allowed as values + for this option. + + The 'map'-dictionary may be used for converting the input value + into canonical values (eg. for aliases). + + 'ignorecase' defines the behaviour of the validator: + + If ignorecase == 0, the validator/converter are case-sensitive. + If ignorecase == 1, the validator/converter are case-insensitive. + If ignorecase == 2, the validator/converter is case-insensitive and + the converted value will always be lower-case. + + The 'validator' tests whether the value is in the list of allowed + values. The 'converter' converts input values according to the + given 'map'-dictionary (unmapped input values are returned + unchanged). + """ + help = '%s (%s)' % (help, string.join(allowed_values, '|')) + # define validator + if ignorecase >= 1: + validator = lambda key, val, env, vals=allowed_values: \ + _validator(key, string.lower(val), env, vals) + else: + validator = lambda key, val, env, vals=allowed_values: \ + _validator(key, val, env, vals) + # define converter + if ignorecase == 2: + converter = lambda val, map=map: \ + string.lower(map.get(string.lower(val), val)) + elif ignorecase == 1: + converter = lambda val, map=map: \ + map.get(string.lower(val), val) + else: + converter = lambda val, map=map: \ + map.get(val, val) + return (key, help, default, validator, converter) diff --git a/src/engine/SCons/Options/EnumOptionTests.py b/src/engine/SCons/Options/EnumOptionTests.py new file mode 100644 index 0000000..20ae6c3 --- /dev/null +++ b/src/engine/SCons/Options/EnumOptionTests.py @@ -0,0 +1,198 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import unittest + +import SCons.Errors +import SCons.Options + +class EnumOptionTestCase(unittest.TestCase): + def test_EnumOption(self): + """Test EnumOption creation""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.EnumOption('test', 'test option help', 0, + ['one', 'two', 'three'], + {})) + + o = opts.options[0] + assert o.key == 'test', o.key + assert o.help == 'test option help (one|two|three)', o.help + assert o.default == 0, o.default + assert not o.validator is None, o.validator + assert not o.converter is None, o.converter + + def test_converter(self): + """Test the EnumOption converter""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.EnumOption('test', 'test option help', 0, + ['one', 'two', 'three'])) + + o = opts.options[0] + + for a in ['one', 'two', 'three', 'no_match']: + x = o.converter(a) + assert x == a, x + + opts = SCons.Options.Options() + opts.Add(SCons.Options.EnumOption('test', 'test option help', 0, + ['one', 'two', 'three'], + {'1' : 'one', + '2' : 'two', + '3' : 'three'})) + + o = opts.options[0] + + x = o.converter('one') + assert x == 'one', x + x = o.converter('1') + assert x == 'one', x + + x = o.converter('two') + assert x == 'two', x + x = o.converter('2') + assert x == 'two', x + + x = o.converter('three') + assert x == 'three', x + x = o.converter('3') + assert x == 'three', x + + opts = SCons.Options.Options() + opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=0)) + opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=1)) + opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=2)) + + o0 = opts.options[0] + o1 = opts.options[1] + o2 = opts.options[2] + + table = { + 'one' : ['one', 'one', 'one'], + 'One' : ['One', 'One', 'one'], + 'ONE' : ['ONE', 'ONE', 'one'], + 'two' : ['two', 'two', 'two'], + 'twO' : ['twO', 'twO', 'two'], + 'TWO' : ['TWO', 'TWO', 'two'], + 'three' : ['three', 'three', 'three'], + 'thRee' : ['thRee', 'thRee', 'three'], + 'THREE' : ['THREE', 'THREE', 'three'], + 'a' : ['one', 'one', 'one'], + 'A' : ['A', 'one', 'one'], + 'b' : ['two', 'two', 'two'], + 'B' : ['B', 'two', 'two'], + 'c' : ['three', 'three', 'three'], + 'C' : ['C', 'three', 'three'], + } + + for k, l in table.items(): + x = o0.converter(k) + assert x == l[0], "o0 got %s, expected %s" % (x, l[0]) + x = o1.converter(k) + assert x == l[1], "o1 got %s, expected %s" % (x, l[1]) + x = o2.converter(k) + assert x == l[2], "o2 got %s, expected %s" % (x, l[2]) + + def test_validator(self): + """Test the EnumOption validator""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=0)) + opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=1)) + opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0, + ['one', 'two', 'three'], + {'a' : 'one', + 'b' : 'two', + 'c' : 'three'}, + ignorecase=2)) + + o0 = opts.options[0] + o1 = opts.options[1] + o2 = opts.options[2] + + def valid(o, v): + o.validator('X', v, {}) + + def invalid(o, v): + caught = None + try: + o.validator('X', v, {}) + except SCons.Errors.UserError: + caught = 1 + assert caught, "did not catch expected UserError for o = %s, v = %s" % (o.key, v) + + table = { + 'one' : [ valid, valid, valid], + 'One' : [invalid, valid, valid], + 'ONE' : [invalid, valid, valid], + 'two' : [ valid, valid, valid], + 'twO' : [invalid, valid, valid], + 'TWO' : [invalid, valid, valid], + 'three' : [ valid, valid, valid], + 'thRee' : [invalid, valid, valid], + 'THREE' : [invalid, valid, valid], + 'a' : [invalid, invalid, invalid], + 'A' : [invalid, invalid, invalid], + 'b' : [invalid, invalid, invalid], + 'B' : [invalid, invalid, invalid], + 'c' : [invalid, invalid, invalid], + 'C' : [invalid, invalid, invalid], + 'no_v' : [invalid, invalid, invalid], + } + + for v, l in table.items(): + l[0](o0, v) + l[1](o1, v) + l[2](o2, v) + + +if __name__ == "__main__": + suite = unittest.makeSuite(EnumOptionTestCase, 'test_') + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Options/ListOption.py b/src/engine/SCons/Options/ListOption.py new file mode 100644 index 0000000..20b7e99 --- /dev/null +++ b/src/engine/SCons/Options/ListOption.py @@ -0,0 +1,131 @@ +"""engine.SCons.Options.ListOption + +This file defines the option type for SCons implementing 'lists'. + +A 'list' option may either be 'all', 'none' or a list of names +separated by comma. After the option has been processed, the option +value holds either the named list elements, all list elemens or no +list elements at all. + +Usage example: + + list_of_libs = Split('x11 gl qt ical') + + opts = Options() + opts.Add(ListOption('shared', + 'libraries to build as shared libraries', + 'all', + elems = list_of_libs)) + ... + for lib in list_of_libs: + if lib in env['shared']: + env.SharedObject(...) + else: + env.Object(...) +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +# Know Bug: This should behave like a Set-Type, but does not really, +# since elements can occur twice. + +__all__ = ('ListOption',) + +import string +import UserList + + +class _ListOption(UserList.UserList): + def __init__(self, allowedElems, initlist): + UserList.UserList.__init__(self, filter(None, initlist)) + self.allowedElems = allowedElems[:] + self.allowedElems.sort() + + def __cmp__(self, other): + raise NotImplementedError + def __eq__(self, other): + raise NotImplementedError + def __ge__(self, other): + raise NotImplementedError + def __gt__(self, other): + raise NotImplementedError + def __le__(self, other): + raise NotImplementedError + def __lt__(self, other): + raise NotImplementedError + def __str__(self): + if len(self) == 0: + return 'none' + self.data.sort() + if self.data == self.allowedElems: + return 'all' + else: + return string.join(self, ',') + #def __repr__(self): + # todo: implement this + +def _converter(val, allowedElems): + """ + """ + if val == 'none': + val = [] + elif val == 'all': + val = allowedElems + else: + val = filter(None, string.split(val, ',')) + notAllowed = [] + for v in val: + if not v in allowedElems: + notAllowed.append(v) + if notAllowed: + raise ValueError("Invalid value(s) for option: %s" % + string.join(notAllowed, ',')) + return _ListOption(allowedElems, val) + + +## def _validator(key, val, env): +## """ +## """ +## # todo: write validater for pgk list +## return 1 + + +def ListOption(key, help, default, names): + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validater appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (separated by space). + """ + names_str = 'allowed names: %s' % string.join(names, ' ') + help = string.join( + (help, '(all|none|comma-separated list of names)', names_str), + '\n ') + return (key, help, default, + None, #_validator, + lambda val, elems=names: _converter(val, elems)) diff --git a/src/engine/SCons/Options/ListOptionTests.py b/src/engine/SCons/Options/ListOptionTests.py new file mode 100644 index 0000000..2175720 --- /dev/null +++ b/src/engine/SCons/Options/ListOptionTests.py @@ -0,0 +1,101 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import unittest + +import SCons.Errors +import SCons.Options + +class ListOptionTestCase(unittest.TestCase): + def test_ListOption(self): + """Test ListOption creation""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.ListOption('test', 'test option help', 'all', + ['one', 'two', 'three'])) + + o = opts.options[0] + assert o.key == 'test', o.key + assert o.help == 'test option help\n (all|none|comma-separated list of names)\n allowed names: one two three', repr(o.help) + assert o.default == 'all', o.default + assert o.validator is None, o.validator + assert not o.converter is None, o.converter + + def test_converter(self): + """Test the ListOption converter""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.ListOption('test', 'test option help', 'all', + ['one', 'two', 'three'])) + + o = opts.options[0] + + x = o.converter('all') + assert str(x) == 'all', x + + x = o.converter('none') + assert str(x) == 'none', x + + x = o.converter('one') + assert str(x) == 'one', x + + x = o.converter('two') + assert str(x) == 'two', x + + x = o.converter('three') + assert str(x) == 'three', x + + x = o.converter('one,two') + assert str(x) == 'one,two', x + x = o.converter('two,one') + assert str(x) == 'one,two', x + + x = o.converter('one,three') + assert str(x) == 'one,three', x + x = o.converter('three,one') + assert str(x) == 'one,three', x + + x = o.converter('two,three') + assert str(x) == 'three,two', x + x = o.converter('three,two') + assert str(x) == 'three,two', x + + x = o.converter('one,two,three') + assert str(x) == 'all', x + + x = o.converter('three,two,one') + assert str(x) == 'all', x + + caught = None + try: + x = o.converter('no_match') + except ValueError: + caught = 1 + assert caught, "did not catch expected ValueError" + + +if __name__ == "__main__": + suite = unittest.makeSuite(ListOptionTestCase, 'test_') + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/OptionsTests.py b/src/engine/SCons/Options/OptionsTests.py index 87fbc9a..ab931e2 100644 --- a/src/engine/SCons/OptionsTests.py +++ b/src/engine/SCons/Options/OptionsTests.py @@ -60,6 +60,7 @@ def checkSave(file, expected): assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict) class OptionsTestCase(unittest.TestCase): + def test_Add(self): """Test adding to an Options object""" opts = SCons.Options.Options() @@ -118,6 +119,30 @@ class OptionsTestCase(unittest.TestCase): o = opts.options[2] o.validator(o.key, o.converter(o.default), {}) + def test_AddOptions(self): + """Test adding a list of options to an Options object""" + opts = SCons.Options.Options() + + opts.AddOptions(('VAR2',), + ('ANSWER2', + 'THE answer to THE question', + "42", + check, + lambda x: int(x) + 12)) + + o = opts.options[0] + assert o.key == 'VAR2', o.key + assert o.help == '', o.help + assert o.default == None, o.default + assert o.validator == None, o.validator + assert o.converter == None, o.converter + + o = opts.options[1] + assert o.key == 'ANSWER2', o.key + assert o.help == 'THE answer to THE question', o.help + assert o.default == "42", o.default + o.validator(o.key, o.converter(o.default), {}) + def test_Update(self): """Test updating an Environment""" diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py new file mode 100644 index 0000000..5940974 --- /dev/null +++ b/src/engine/SCons/Options/PackageOption.py @@ -0,0 +1,106 @@ +"""engine.SCons.Options + +This file defines the option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Options() + opts.Add(PackageOption('x11', + 'use X11 installed here (yes = search some places', + 'yes')) + ... + if env['x11'] == True: + dir = ... search X11 in some standard places ... + env['x11'] = dir + if env['x11']: + ... build with x11 ... +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__all__ = ('PackageOption', 'True', 'False') + +import string + +from BoolOption import True, False +import SCons.Errors + +__enable_strings = ('yes', 'true', 'on', 'enable', 'search') +__disable_strings = ('no', 'false', 'off', 'disable') + +def _converter(val): + """ + """ + lval = string.lower(val) + if lval in __enable_strings: return True + if lval in __disable_strings: return False + #raise ValueError("Invalid value for boolean option: %s" % val) + return val + + +def _validator(key, val, env, searchfunc): + # NB: searchfunc is currenty undocumented and unsupported + """ + """ + # todo: write validator, check for path + import os + if env[key] == False: + pass + elif env[key] == True: + if searchfunc: + env[key] = searchfunc(key, val) + elif not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PackageOption(key, help, default, searchfunc=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + help = string.join( + (help, '( yes | no | /path/to/%s )' % key), + '\n ') + return (key, help, default, + lambda k, v, e, f=searchfunc: _validator(k,v,e,f), + _converter) diff --git a/src/engine/SCons/Options/PackageOptionTests.py b/src/engine/SCons/Options/PackageOptionTests.py new file mode 100644 index 0000000..d9fbe7b --- /dev/null +++ b/src/engine/SCons/Options/PackageOptionTests.py @@ -0,0 +1,109 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import unittest + +import SCons.Errors +import SCons.Options + +import TestCmd + +class PackageOptionTestCase(unittest.TestCase): + def test_PackageOption(self): + """Test PackageOption creation""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path')) + + o = opts.options[0] + assert o.key == 'test', o.key + assert o.help == 'test option help\n ( yes | no | /path/to/test )', repr(o.help) + assert o.default == '/default/path', o.default + assert not o.validator is None, o.validator + assert not o.converter is None, o.converter + + def test_converter(self): + """Test the PackageOption converter""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path')) + + o = opts.options[0] + + true_values = [ + 'yes', 'YES', + 'true', 'TRUE', + 'on', 'ON', + 'enable', 'ENABLE', + 'search', 'SEARCH', + ] + false_values = [ + 'no', 'NO', + 'false', 'FALSE', + 'off', 'OFF', + 'disable', 'DISABLE', + ] + + for t in true_values: + x = o.converter(t) + assert x, "converter returned false for '%s'" % t + + for f in false_values: + x = o.converter(f) + assert not x, "converter returned true for '%s'" % f + + x = o.converter('/explicit/path') + assert x == '/explicit/path', x + + def test_validator(self): + """Test the PackageOption validator""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path')) + + test = TestCmd.TestCmd(workdir='') + test.write('exists', 'exists\n') + + o = opts.options[0] + + env = {'F':0, 'T':1, 'X':'x'} + + exists = test.workpath('exists') + does_not_exist = test.workpath('does_not_exist') + + o.validator('F', '/path', env) + o.validator('T', '/path', env) + o.validator('X', exists, env) + + caught = None + try: + o.validator('X', does_not_exist, env) + except SCons.Errors.UserError: + caught = 1 + assert caught, "did not catch expected UserError" + + +if __name__ == "__main__": + suite = unittest.makeSuite(PackageOptionTestCase, 'test_') + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Options/PathOption.py b/src/engine/SCons/Options/PathOption.py new file mode 100644 index 0000000..cea4750 --- /dev/null +++ b/src/engine/SCons/Options/PathOption.py @@ -0,0 +1,85 @@ +"""engine.SCons.Options.PathOption + +This file defines an option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Options() + + opts = Options() + opts.Add(PathOption('qtdir', + 'where the root of Qt is installed', + qtdir)) + opts.Add(PathOption('qt_includes', + 'where the Qt includes are installed', + '$qtdir/includes')) + opts.Add(PathOption('qt_libraries', + 'where the Qt library is installed', + '$qtdir/lib')) + +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__all__ = ('PathOption',) + +import os + +import SCons.Errors + +def _validator(key, val, env): + """ + """ + # todo: write validator, check for path + if not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PathOption(key, help, default): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'path list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + return (key, '%s ( /path/to/%s )' % (help, key), default, + _validator, None) + diff --git a/src/engine/SCons/Options/PathOptionTests.py b/src/engine/SCons/Options/PathOptionTests.py new file mode 100644 index 0000000..358f8e7 --- /dev/null +++ b/src/engine/SCons/Options/PathOptionTests.py @@ -0,0 +1,70 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import unittest + +import SCons.Errors +import SCons.Options + +import TestCmd + +class PathOptionTestCase(unittest.TestCase): + def test_PathOption(self): + """Test PathOption creation""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.PathOption('test', 'test option help', '/default/path')) + + o = opts.options[0] + assert o.key == 'test', o.key + assert o.help == 'test option help ( /path/to/test )', repr(o.help) + assert o.default == '/default/path', o.default + assert not o.validator is None, o.validator + assert o.converter is None, o.converter + + def test_validator(self): + """Test the PathOption validator""" + opts = SCons.Options.Options() + opts.Add(SCons.Options.PathOption('test', 'test option help', '/default/path')) + + test = TestCmd.TestCmd(workdir='') + test.write('exists', 'exists\n') + + o = opts.options[0] + + o.validator('X', test.workpath('exists'), {}) + + caught = None + try: + o.validator('X', test.workpath('does_not_exist'), {}) + except SCons.Errors.UserError: + caught = 1 + assert caught, "did not catch expected UserError" + + +if __name__ == "__main__": + suite = unittest.makeSuite(PathOptionTestCase, 'test_') + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Options.py b/src/engine/SCons/Options/__init__.py index f4d2bcc..6236102 100644 --- a/src/engine/SCons/Options.py +++ b/src/engine/SCons/Options/__init__.py @@ -1,7 +1,7 @@ """engine.SCons.Options -This file defines the Options class that is used to add user-friendly customizable -variables to a scons build. +This file defines the Options class that is used to add user-friendly +customizable variables to an SCons build. """ # @@ -35,6 +35,12 @@ import SCons.Errors import SCons.Util import SCons.Warnings +from BoolOption import BoolOption, True, False # okay +from EnumOption import EnumOption # okay +from ListOption import ListOption # naja +from PackageOption import PackageOption # naja +from PathOption import PathOption # okay + class Options: """ @@ -56,12 +62,25 @@ class Options: elif files: self.files = files + def _do_add(self, key, help="", default=None, validator=None, converter=None): + class Option: + pass + + option = Option() + option.key = key + option.help = help + option.default = default + option.validator = validator + option.converter = converter + + self.options.append(option) + def Add(self, key, help="", default=None, validator=None, converter=None, **kw): """ Add an option. - key - the name of the variable + key - the name of the variable, or a list or tuple of arguments help - optional help text for the options default - optional default value validator - optional function that is called to validate the option's value @@ -70,8 +89,13 @@ class Options: putting it in the environment. """ - if not SCons.Util.is_valid_construction_var(key): - raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % key + if SCons.Util.is_List(key) or type(key) == type(()): + apply(self._do_add, key) + return + + if not SCons.Util.is_String(key) or \ + not SCons.Util.is_valid_construction_var(key): + raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % str(key) if kw.has_key('validater'): SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, @@ -80,17 +104,27 @@ class Options: if validator is None: validator = kw['validater'] - class Option: - pass + self._do_add(key, help, default, validator, converter) - option = Option() - option.key = key - option.help = help - option.default = default - option.validator = validator - option.converter = converter - self.options.append(option) + def AddOptions(self, *optlist): + """ + Add a list of options. + + Each list element is a tuple/list of arguments to be passed on + to the underlying method for adding options. + + Example: + opt.AddOptions( + ('debug', '', 0), + ('CC', 'The C compiler'), + ('VALIDATE', 'An option for testing validation', 'notset', + validator, None), + ) + """ + for o in optlist: + apply(self._do_add, o) + def Update(self, env, args=None): """ diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 924ddf3..fff1869 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -627,17 +627,22 @@ def BuildDefaultGlobals(): globals = {} globals['Action'] = SCons.Action.Action globals['ARGUMENTS'] = arguments + globals['BoolOption'] = SCons.Options.BoolOption globals['Builder'] = SCons.Builder.Builder globals['Configure'] = SCons.SConf.SConf + globals['EnumOption'] = SCons.Options.EnumOption globals['Environment'] = SCons.Environment.Environment + globals['ListOption'] = SCons.Options.ListOption globals['Options'] = Options + globals['PackageOption'] = SCons.Options.PackageOption + globals['PathOption'] = SCons.Options.PathOption globals['Platform'] = SCons.Platform.Platform globals['Return'] = Return globals['Scanner'] = SCons.Scanner.Base globals['Tool'] = SCons.Tool.Tool globals['WhereIs'] = SCons.Util.WhereIs - # Functions we're in the process of converting to Environment methods. + # Functions we might still convert to Environment methods. globals['CScan'] = SCons.Defaults.CScan globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment diff --git a/src/setup.py b/src/setup.py index be38a84..be3171d 100644 --- a/src/setup.py +++ b/src/setup.py @@ -198,6 +198,7 @@ arguments = { 'packages' : ["SCons", "SCons.Node", "SCons.Optik", + "SCons.Options", "SCons.Platform", "SCons.Scanner", "SCons.Script", |