From 8dbe61168174137944a5bc55ea5a9ff389336376 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Wed, 6 Nov 2002 12:25:25 +0000 Subject: Refactor command-line parsing. (Steve Leblanc) --- rpm/scons.spec | 8 + src/CHANGES.txt | 5 + src/engine/MANIFEST.in | 4 + src/engine/SCons/Optik/.aeignore | 5 + src/engine/SCons/Optik/__init__.py | 32 + src/engine/SCons/Optik/errors.py | 55 ++ src/engine/SCons/Optik/option.py | 388 +++++++++++ src/engine/SCons/Optik/option_parser.py | 661 +++++++++++++++++++ src/engine/SCons/Script/__init__.py | 1083 ++++++++++++------------------- src/engine/setup.py | 1 + src/setup.py | 1 + test/SCONSFLAGS.py | 11 +- test/option--profile.py | 8 +- test/option-unknown.py | 14 +- 14 files changed, 1600 insertions(+), 676 deletions(-) create mode 100644 src/engine/SCons/Optik/.aeignore create mode 100644 src/engine/SCons/Optik/__init__.py create mode 100644 src/engine/SCons/Optik/errors.py create mode 100644 src/engine/SCons/Optik/option.py create mode 100644 src/engine/SCons/Optik/option_parser.py diff --git a/rpm/scons.spec b/rpm/scons.spec index 8c1cf73..76f6d3b 100644 --- a/rpm/scons.spec +++ b/rpm/scons.spec @@ -68,6 +68,14 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/scons/SCons/Node/FS.pyc /usr/lib/scons/SCons/Node/__init__.py /usr/lib/scons/SCons/Node/__init__.pyc +/usr/lib/scons/SCons/Optik/__init__.py +/usr/lib/scons/SCons/Optik/__init__.pyc +/usr/lib/scons/SCons/Optik/errors.py +/usr/lib/scons/SCons/Optik/errors.pyc +/usr/lib/scons/SCons/Optik/option.py +/usr/lib/scons/SCons/Optik/option.pyc +/usr/lib/scons/SCons/Optik/option_parser.py +/usr/lib/scons/SCons/Optik/option_parser.pyc /usr/lib/scons/SCons/Options.py /usr/lib/scons/SCons/Options.pyc /usr/lib/scons/SCons/Platform/cygwin.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c91a59e..83c0d08 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -50,6 +50,11 @@ RELEASE 0.09 - - Add separate $SHOBJPREFIX and $SHOBJSUFFIX construction variables (by default, the same as $OBJPREFIX and $OBJSUFFIX). + From Steve LeBlanc: + + - Refactor option processing to use our own version of Greg Ward's + Optik module, modified to run under Python 1.5.2. + From Jeff Petkau: - Fix interpretation of '#/../foo' on Win32 systems. diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index bca093f..d500733 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -9,6 +9,10 @@ SCons/exitfuncs.py SCons/Node/__init__.py SCons/Node/Alias.py SCons/Node/FS.py +SCons/Optik/__init__.py +SCons/Optik/errors.py +SCons/Optik/option.py +SCons/Optik/option_parser.py SCons/Options.py SCons/Platform/__init__.py SCons/Platform/cygwin.py diff --git a/src/engine/SCons/Optik/.aeignore b/src/engine/SCons/Optik/.aeignore new file mode 100644 index 0000000..22ebd62 --- /dev/null +++ b/src/engine/SCons/Optik/.aeignore @@ -0,0 +1,5 @@ +*,D +*.pyc +.*.swp +.consign +.sconsign diff --git a/src/engine/SCons/Optik/__init__.py b/src/engine/SCons/Optik/__init__.py new file mode 100644 index 0000000..8ea41fb --- /dev/null +++ b/src/engine/SCons/Optik/__init__.py @@ -0,0 +1,32 @@ +"""optik + +A powerful, extensible, and easy-to-use command-line parser for Python. + +By Greg Ward + +See http://optik.sourceforge.net/ +""" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +# Original Optik revision this is based on: +__Optik_revision__ = "__init__.py,v 1.11 2002/04/11 19:17:34 gward Exp" + +__version__ = "1.3" + + +# Re-import these for convenience +from SCons.Optik.option import Option +from SCons.Optik.option_parser import \ + OptionParser, SUPPRESS_HELP, SUPPRESS_USAGE +from SCons.Optik.errors import OptionValueError + + +# Some day, there might be many Option classes. As of Optik 1.3, the +# preferred way to instantiate Options is indirectly, via make_option(), +# which will become a factory function when there are many Option +# classes. +make_option = Option diff --git a/src/engine/SCons/Optik/errors.py b/src/engine/SCons/Optik/errors.py new file mode 100644 index 0000000..dca8a69 --- /dev/null +++ b/src/engine/SCons/Optik/errors.py @@ -0,0 +1,55 @@ +"""optik.errors + +Exception classes used by Optik. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +# Original Optik revision this is based on: +__Optik_revision__ = "errors.py,v 1.5 2002/02/13 23:29:47 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17 GPW (from optik.py) + + +class OptikError (Exception): + def __init__ (self, msg): + self.msg = msg + + def __str__ (self): + return self.msg + + +class OptionError (OptikError): + """ + Raised if an Option instance is created with invalid or + inconsistent arguments. + """ + + def __init__ (self, msg, option): + self.msg = msg + self.option_id = str(option) + + def __str__ (self): + if self.option_id: + return "option %s: %s" % (self.option_id, self.msg) + else: + return self.msg + +class OptionConflictError (OptionError): + """ + Raised if conflicting options are added to an OptionParser. + """ + +class OptionValueError (OptikError): + """ + Raised if an invalid option value is encountered on the command + line. + """ + +class BadOptionError (OptikError): + """ + Raised if an invalid or ambiguous option is seen on the command-line. + """ diff --git a/src/engine/SCons/Optik/option.py b/src/engine/SCons/Optik/option.py new file mode 100644 index 0000000..2e1ec33 --- /dev/null +++ b/src/engine/SCons/Optik/option.py @@ -0,0 +1,388 @@ +"""optik.option + +Defines the Option class and some standard value-checking functions. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +# Original Optik revision this is based on: +__Optik_revision__ = "option.py,v 1.19.2.1 2002/07/23 01:51:14 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17, GPW (from optik.py) + +import sys +import string +from types import TupleType, ListType, DictType +from SCons.Optik.errors import OptionError, OptionValueError + +_builtin_cvt = { "int" : (int, "integer"), + "long" : (long, "long integer"), + "float" : (float, "floating-point"), + "complex" : (complex, "complex") } + +def check_builtin (option, opt, value): + (cvt, what) = _builtin_cvt[option.type] + try: + return cvt(value) + except ValueError: + raise OptionValueError( + #"%s: invalid %s argument %s" % (opt, what, repr(value))) + "option %s: invalid %s value: %s" % (opt, what, repr(value))) + +def check_choice(option, opt, value): + if value in option.choices: + return value + else: + choices = string.join(map(repr, option.choices),", ") + raise OptionValueError( + "option %s: invalid choice: %s (choose from %s)" + % (opt, repr(value), choices)) + +# Not supplying a default is different from a default of None, +# so we need an explicit "not supplied" value. +NO_DEFAULT = "NO"+"DEFAULT" + + +class Option: + """ + Instance attributes: + _short_opts : [string] + _long_opts : [string] + + action : string + type : string + dest : string + default : any + nargs : int + const : any + choices : [string] + callback : function + callback_args : (any*) + callback_kwargs : { string : any } + help : string + metavar : string + """ + + # The list of instance attributes that may be set through + # keyword args to the constructor. + ATTRS = ['action', + 'type', + 'dest', + 'default', + 'nargs', + 'const', + 'choices', + 'callback', + 'callback_args', + 'callback_kwargs', + 'help', + 'metavar'] + + # The set of actions allowed by option parsers. Explicitly listed + # here so the constructor can validate its arguments. + ACTIONS = ("store", + "store_const", + "store_true", + "store_false", + "append", + "count", + "callback", + "help", + "version") + + # The set of actions that involve storing a value somewhere; + # also listed just for constructor argument validation. (If + # the action is one of these, there must be a destination.) + STORE_ACTIONS = ("store", + "store_const", + "store_true", + "store_false", + "append", + "count") + + # The set of actions for which it makes sense to supply a value + # type, ie. where we expect an argument to this option. + TYPED_ACTIONS = ("store", + "append", + "callback") + + # The set of known types for option parsers. Again, listed here for + # constructor argument validation. + TYPES = ("string", "int", "long", "float", "complex", "choice") + + # Dictionary of argument checking functions, which convert and + # validate option arguments according to the option type. + # + # Signature of checking functions is: + # check(option : Option, opt : string, value : string) -> any + # where + # option is the Option instance calling the checker + # opt is the actual option seen on the command-line + # (eg. "-a", "--file") + # value is the option argument seen on the command-line + # + # The return value should be in the appropriate Python type + # for option.type -- eg. an integer if option.type == "int". + # + # If no checker is defined for a type, arguments will be + # unchecked and remain strings. + TYPE_CHECKER = { "int" : check_builtin, + "long" : check_builtin, + "float" : check_builtin, + "complex" : check_builtin, + "choice" : check_choice, + } + + + # CHECK_METHODS is a list of unbound method objects; they are called + # by the constructor, in order, after all attributes are + # initialized. The list is created and filled in later, after all + # the methods are actually defined. (I just put it here because I + # like to define and document all class attributes in the same + # place.) Subclasses that add another _check_*() method should + # define their own CHECK_METHODS list that adds their check method + # to those from this class. + CHECK_METHODS = None + + + # -- Constructor/initialization methods ---------------------------- + + def __init__ (self, *opts, **attrs): + # Set _short_opts, _long_opts attrs from 'opts' tuple + opts = self._check_opt_strings(opts) + self._set_opt_strings(opts) + + # Set all other attrs (action, type, etc.) from 'attrs' dict + self._set_attrs(attrs) + + # Check all the attributes we just set. There are lots of + # complicated interdependencies, but luckily they can be farmed + # out to the _check_*() methods listed in CHECK_METHODS -- which + # could be handy for subclasses! The one thing these all share + # is that they raise OptionError if they discover a problem. + for checker in self.CHECK_METHODS: + checker(self) + + def _check_opt_strings (self, opts): + # Filter out None because early versions of Optik had exactly + # one short option and one long option, either of which + # could be None. + opts = filter(None, opts) + if not opts: + raise OptionError("at least one option string must be supplied", + self) + return opts + + def _set_opt_strings (self, opts): + self._short_opts = [] + self._long_opts = [] + for opt in opts: + if len(opt) < 2: + raise OptionError( + "invalid option string %s: " + "must be at least two characters long" % (`opt`,), self) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise OptionError( + "invalid short option string %s: " + "must be of the form -x, (x any non-dash char)" % (`opt`,), + self) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise OptionError( + "invalid long option string %s: " + "must start with --, followed by non-dash" % (`opt`,), + self) + self._long_opts.append(opt) + + def _set_attrs (self, attrs): + for attr in self.ATTRS: + if attrs.has_key(attr): + setattr(self, attr, attrs[attr]) + del attrs[attr] + else: + if attr == 'default': + setattr(self, attr, NO_DEFAULT) + else: + setattr(self, attr, None) + if attrs: + raise OptionError( + "invalid keyword arguments: %s" % string.join(attrs.keys(),", "), + self) + + + # -- Constructor validation methods -------------------------------- + + def _check_action (self): + if self.action is None: + self.action = "store" + elif self.action not in self.ACTIONS: + raise OptionError("invalid action: %s" % (`self.action`,), self) + + def _check_type (self): + if self.type is None: + # XXX should factor out another class attr here: list of + # actions that *require* a type + if self.action in ("store", "append"): + if self.choices is not None: + # The "choices" attribute implies "choice" type. + self.type = "choice" + else: + # No type given? "string" is the most sensible default. + self.type = "string" + else: + if self.type not in self.TYPES: + raise OptionError("invalid option type: %s" % (`self.type`,), self) + if self.action not in self.TYPED_ACTIONS: + raise OptionError( + "must not supply a type for action %s" % (`self.action`,), self) + + def _check_choice(self): + if self.type == "choice": + if self.choices is None: + raise OptionError( + "must supply a list of choices for type 'choice'", self) + elif type(self.choices) not in (TupleType, ListType): + raise OptionError( + "choices must be a list of strings ('%s' supplied)" + % string.split(str(type(self.choices)),"'")[1], self) + elif self.choices is not None: + raise OptionError( + "must not supply choices for type %s" % (repr(self.type),), self) + + def _check_dest (self): + if self.action in self.STORE_ACTIONS and self.dest is None: + # No destination given, and we need one for this action. + # Glean a destination from the first long option string, + # or from the first short option string if no long options. + if self._long_opts: + # eg. "--foo-bar" -> "foo_bar" + self.dest = string.replace(self._long_opts[0][2:],'-', '_') + else: + self.dest = self._short_opts[0][1] + + def _check_const (self): + if self.action != "store_const" and self.const is not None: + raise OptionError( + "'const' must not be supplied for action %s" % (repr(self.action),), + self) + + def _check_nargs (self): + if self.action in self.TYPED_ACTIONS: + if self.nargs is None: + self.nargs = 1 + elif self.nargs is not None: + raise OptionError( + "'nargs' must not be supplied for action %s" % (repr(self.action),), + self) + + def _check_callback (self): + if self.action == "callback": + if not callable(self.callback): + raise OptionError( + "callback not callable: %s" % (repr(self.callback),), self) + if (self.callback_args is not None and + type(self.callback_args) is not TupleType): + raise OptionError( + "callback_args, if supplied, must be a tuple: not %s" + % (repr(self.callback_args),), self) + if (self.callback_kwargs is not None and + type(self.callback_kwargs) is not DictType): + raise OptionError( + "callback_kwargs, if supplied, must be a dict: not %s" + % (repr(self.callback_kwargs),), self) + else: + if self.callback is not None: + raise OptionError( + "callback supplied (%s) for non-callback option" + % (repr(self.callback),), self) + if self.callback_args is not None: + raise OptionError( + "callback_args supplied for non-callback option", self) + if self.callback_kwargs is not None: + raise OptionError( + "callback_kwargs supplied for non-callback option", self) + + + CHECK_METHODS = [_check_action, + _check_type, + _check_choice, + _check_dest, + _check_const, + _check_nargs, + _check_callback] + + + # -- Miscellaneous methods ----------------------------------------- + + def __str__ (self): + if self._short_opts or self._long_opts: + return string.join(self._short_opts + self._long_opts,"/") + else: + raise RuntimeError, "short_opts and long_opts both empty!" + + def takes_value (self): + return self.type is not None + + + # -- Processing methods -------------------------------------------- + + def check_value (self, opt, value): + checker = self.TYPE_CHECKER.get(self.type) + if checker is None: + return value + else: + return checker(self, opt, value) + + def process (self, opt, value, values, parser): + + # First, convert the value(s) to the right type. Howl if any + # value(s) are bogus. + if value is not None: + if self.nargs == 1: + value = self.check_value(opt, value) + else: + def cv(v,check=self.check_value,o=opt): + return check(o,v) + + value = tuple(map(cv,value)) + + # And then take whatever action is expected of us. + # This is a separate method to make life easier for + # subclasses to add new actions. + return self.take_action( + self.action, self.dest, opt, value, values, parser) + + def take_action (self, action, dest, opt, value, values, parser): + if action == "store": + setattr(values, dest, value) + elif action == "store_const": + setattr(values, dest, self.const) + elif action == "store_true": + setattr(values, dest, 1) + elif action == "store_false": + setattr(values, dest, 0) + elif action == "append": + values.ensure_value(dest, []).append(value) + elif action == "count": + setattr(values, dest, values.ensure_value(dest, 0) + 1) + elif action == "callback": + args = self.callback_args or () + kwargs = self.callback_kwargs or {} + apply( self.callback, (self, opt, value, parser,)+ args, kwargs) + elif action == "help": + parser.print_help() + sys.exit(0) + elif action == "version": + parser.print_version() + sys.exit(0) + else: + raise RuntimeError, "unknown action %s" % (repr(self.action),) + + return 1 + +# class Option diff --git a/src/engine/SCons/Optik/option_parser.py b/src/engine/SCons/Optik/option_parser.py new file mode 100644 index 0000000..ebe8336 --- /dev/null +++ b/src/engine/SCons/Optik/option_parser.py @@ -0,0 +1,661 @@ +"""optik.option_parser + +Provides the OptionParser and Values classes. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +# Original Optik revision this is based on: +__Optik_revision__ = "option_parser.py,v 1.38.2.1 2002/07/23 01:51:14 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17, GPW (from optik.py) + +import sys, os +import string +import types +from SCons.Optik.option import Option, NO_DEFAULT +from SCons.Optik.errors import OptionConflictError, OptionValueError, BadOptionError + +def get_prog_name (): + return os.path.basename(sys.argv[0]) + + +SUPPRESS_HELP = "SUPPRESS"+"HELP" +SUPPRESS_USAGE = "SUPPRESS"+"USAGE" + +class Values: + + def __init__ (self, defaults=None): + if defaults: + for (attr, val) in defaults.items(): + setattr(self, attr, val) + + + def _update_careful (self, dict): + """ + Update the option values from an arbitrary dictionary, but only + use keys from dict that already have a corresponding attribute + in self. Any keys in dict without a corresponding attribute + are silently ignored. + """ + for attr in dir(self): + if dict.has_key(attr): + dval = dict[attr] + if dval is not None: + setattr(self, attr, dval) + + def _update_loose (self, dict): + """ + Update the option values from an arbitrary dictionary, + using all keys from the dictionary regardless of whether + they have a corresponding attribute in self or not. + """ + self.__dict__.update(dict) + + def _update (self, dict, mode): + if mode == "careful": + self._update_careful(dict) + elif mode == "loose": + self._update_loose(dict) + else: + raise ValueError, "invalid update mode: %s" % (repr(mode),) + + def read_module (self, modname, mode="careful"): + __import__(modname) + mod = sys.modules[modname] + self._update(vars(mod), mode) + + def read_file (self, filename, mode="careful"): + vars = {} + execfile(filename, vars) + self._update(vars, mode) + + def ensure_value (self, attr, value): + if not hasattr(self, attr) or getattr(self, attr) is None: + setattr(self, attr, value) + return getattr(self, attr) + + +class OptionParser: + """ + Class attributes: + standard_option_list : [Option] + list of standard options that will be accepted by all instances + of this parser class (intended to be overridden by subclasses). + + Instance attributes: + usage : string + a usage string for your program. Before it is displayed + to the user, "%prog" will be expanded to the name of + your program (os.path.basename(sys.argv[0])). + option_list : [Option] + the list of all options accepted on the command-line of + this program + _short_opt : { string : Option } + dictionary mapping short option strings, eg. "-f" or "-X", + to the Option instances that implement them. If an Option + has multiple short option strings, it will appears in this + dictionary multiple times. + _long_opt : { string : Option } + dictionary mapping long option strings, eg. "--file" or + "--exclude", to the Option instances that implement them. + Again, a given Option can occur multiple times in this + dictionary. + defaults : { string : any } + dictionary mapping option destination names to default + values for each destination. + + allow_interspersed_args : boolean = true + if true, positional arguments may be interspersed with options. + Assuming -a and -b each take a single argument, the command-line + -ablah foo bar -bboo baz + will be interpreted the same as + -ablah -bboo -- foo bar baz + If this flag were false, that command line would be interpreted as + -ablah -- foo bar -bboo baz + -- ie. we stop processing options as soon as we see the first + non-option argument. (This is the tradition followed by + Python's getopt module, Perl's Getopt::Std, and other argument- + parsing libraries, but it is generally annoying to users.) + + rargs : [string] + the argument list currently being parsed. Only set when + parse_args() is active, and continually trimmed down as + we consume arguments. Mainly there for the benefit of + callback options. + largs : [string] + the list of leftover arguments that we have skipped while + parsing options. If allow_interspersed_args is false, this + list is always empty. + values : Values + the set of option values currently being accumulated. Only + set when parse_args() is active. Also mainly for callbacks. + + Because of the 'rargs', 'largs', and 'values' attributes, + OptionParser is not thread-safe. If, for some perverse reason, you + need to parse command-line arguments simultaneously in different + threads, use different OptionParser instances. + + """ + + standard_option_list = [] + + + def __init__ (self, + usage=None, + option_list=None, + option_class=Option, + version=None, + conflict_handler="error"): + self.set_usage(usage) + self.option_class = option_class + self.version = version + self.set_conflict_handler(conflict_handler) + self.allow_interspersed_args = 1 + + # Create the various lists and dicts that constitute the + # "option list". See class docstring for details about + # each attribute. + self._create_option_list() + + # Populate the option list; initial sources are the + # standard_option_list class attribute, the 'option_list' + # argument, and the STD_VERSION_OPTION global (if 'version' + # supplied). + self._populate_option_list(option_list) + + self._init_parsing_state() + + # -- Private methods ----------------------------------------------- + # (used by the constructor) + + def _create_option_list (self): + self.option_list = [] + self._short_opt = {} # single letter -> Option instance + self._long_opt = {} # long option -> Option instance + self.defaults = {} # maps option dest -> default value + + def _populate_option_list (self, option_list): + if self.standard_option_list: + self.add_options(self.standard_option_list) + if option_list: + self.add_options(option_list) + + def _init_parsing_state (self): + # These are set in parse_args() for the convenience of callbacks. + self.rargs = None + self.largs = None + self.values = None + + + # -- Simple modifier methods --------------------------------------- + + def set_usage (self, usage): + if usage is None: + self.usage = "usage: %prog [options]" + elif usage is SUPPRESS_USAGE: + self.usage = None + else: + self.usage = usage + + def enable_interspersed_args (self): + self.allow_interspersed_args = 1 + + def disable_interspersed_args (self): + self.allow_interspersed_args = 0 + + def set_conflict_handler (self, handler): + if handler not in ("ignore", "error", "resolve"): + raise ValueError, "invalid conflict_resolution value %s" % (repr(handler),) + self.conflict_handler = handler + + def set_default (self, dest, value): + self.defaults[dest] = value + + def set_defaults (self, **kwargs): + self.defaults.update(kwargs) + + def get_default_values(self): + return Values(self.defaults) + + + # -- Option-adding methods ----------------------------------------- + + def _check_conflict (self, option): + conflict_opts = [] + for opt in option._short_opts: + if self._short_opt.has_key(opt): + conflict_opts.append((opt, self._short_opt[opt])) + for opt in option._long_opts: + if self._long_opt.has_key(opt): + conflict_opts.append((opt, self._long_opt[opt])) + + if conflict_opts: + handler = self.conflict_handler + if handler == "ignore": # behaviour for Optik 1.0, 1.1 + pass + elif handler == "error": # new in 1.2 + raise OptionConflictError( + "conflicting option string(s): %s" + % string.join( map( lambda x: x[0], conflict_opts),", "), + option) + elif handler == "resolve": # new in 1.2 + for (opt, c_option) in conflict_opts: + if len(opt)>2 and opt[:2]=="--": + c_option._long_opts.remove(opt) + del self._long_opt[opt] + else: + c_option._short_opts.remove(opt) + del self._short_opt[opt] + if not (c_option._short_opts or c_option._long_opts): + self.option_list.remove(c_option) + + + def add_option (self, *args, **kwargs): + """add_option(Option) + add_option(opt_str, ..., kwarg=val, ...) + """ + if type(args[0]) is types.StringType: + option = apply(self.option_class,args, kwargs) + elif len(args) == 1 and not kwargs: + option = args[0] + if not isinstance(option, Option): + raise TypeError, "not an Option instance: %s" % (repr(option),) + else: + raise TypeError, "invalid arguments" + + self._check_conflict(option) + + self.option_list.append(option) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + if option.dest is not None: # option has a dest, we need a default + if option.default is not NO_DEFAULT: + self.defaults[option.dest] = option.default + elif not self.defaults.has_key(option.dest): + self.defaults[option.dest] = None + + def add_options (self, option_list): + for option in option_list: + self.add_option(option) + + + # -- Option query/removal methods ---------------------------------- + + def get_option (self, opt_str): + return (self._short_opt.get(opt_str) or + self._long_opt.get(opt_str)) + + def has_option (self, opt_str): + return (self._short_opt.has_key(opt_str) or + self._long_opt.has_key(opt_str)) + + + def remove_option (self, opt_str): + option = self._short_opt.get(opt_str) + if option is None: + option = self._long_opt.get(opt_str) + if option is None: + raise ValueError("no such option %s" % (repr(opt_str),)) + + for opt in option._short_opts: + del self._short_opt[opt] + for opt in option._long_opts: + del self._long_opt[opt] + self.option_list.remove(option) + + + # -- Option-parsing methods ---------------------------------------- + + def _get_args (self, args): + if args is None: + return sys.argv[1:] + else: + return args[:] # don't modify caller's list + + def parse_args (self, args=None, values=None): + """ + parse_args(args : [string] = sys.argv[1:], + values : Values = None) + -> (values : Values, args : [string]) + + Parse the command-line options found in 'args' (default: + sys.argv[1:]). Any errors result in a call to 'error()', which + by default prints the usage message to stderr and calls + sys.exit() with an error message. On success returns a pair + (values, args) where 'values' is an Values instance (with all + your option values) and 'args' is the list of arguments left + over after parsing options. + """ + rargs = self._get_args(args) + if values is None: + values = self.get_default_values() + + # Store the halves of the argument list as attributes for the + # convenience of callbacks: + # rargs + # the rest of the command-line (the "r" stands for + # "remaining" or "right-hand") + # largs + # the leftover arguments -- ie. what's left after removing + # options and their arguments (the "l" stands for "leftover" + # or "left-hand") + self.rargs = rargs + self.largs = largs = [] + self.values = values + + try: + stop = self._process_args(largs, rargs, values) + except (BadOptionError, OptionValueError), err: + self.error(err.msg) + + args = largs + rargs + return self.check_values(values, args) + + def check_values (self, values, args): + """ + check_values(values : Values, args : [string]) + -> (values : Values, args : [string]) + + Check that the supplied option values and leftover arguments are + valid. Returns the option values and leftover arguments + (possibly adjusted, possibly completely new -- whatever you + like). Default implementation just returns the passed-in + values; subclasses may override as desired. + """ + return (values, args) + + def _process_args (self, largs, rargs, values): + """_process_args(largs : [string], + rargs : [string], + values : Values) + + Process command-line arguments and populate 'values', consuming + options and arguments from 'rargs'. If 'allow_interspersed_args' is + false, stop at the first non-option argument. If true, accumulate any + interspersed non-option arguments in 'largs'. + """ + while rargs: + arg = rargs[0] + # We handle bare "--" explicitly, and bare "-" is handled by the + # standard arg handler since the short arg case ensures that the + # len of the opt string is greater than 1. + if arg == "--": + del rargs[0] + return + elif arg[0:2] == "--": + # process a single long option (possibly with value(s)) + self._process_long_opt(rargs, values) + elif arg[:1] == "-" and len(arg) > 1: + # process a cluster of short options (possibly with + # value(s) for the last one only) + self._process_short_opts(rargs, values) + elif self.allow_interspersed_args: + largs.append(arg) + del rargs[0] + else: + return # stop now, leave this arg in rargs + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt (self, opt): + """_match_long_opt(opt : string) -> string + + Determine which long option string 'opt' matches, ie. which one + it is an unambiguous abbrevation for. Raises BadOptionError if + 'opt' doesn't unambiguously match any long option string. + """ + return _match_abbrev(opt, self._long_opt) + + def _process_long_opt (self, rargs, values): + arg = rargs.pop(0) + + # Value explicitly attached to arg? Pretend it's the next + # argument. + if "=" in arg: + (opt, next_arg) = string.split(arg,"=", 1) + rargs.insert(0, next_arg) + had_explicit_value = 1 + else: + opt = arg + had_explicit_value = 0 + + opt = self._match_long_opt(opt) + option = self._long_opt[opt] + if option.takes_value(): + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error("%s option requires a value" % opt) + else: + self.error("%s option requires %d values" + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + elif had_explicit_value: + self.error("%s option does not take a value" % opt) + + else: + value = None + + option.process(opt, value, values, self) + + def _process_short_opts (self, rargs, values): + arg = rargs.pop(0) + stop = 0 + i = 1 + for ch in arg[1:]: + opt = "-" + ch + option = self._short_opt.get(opt) + i = i+1 # we have consumed a character + + if not option: + self.error("no such option: %s" % opt) + if option.takes_value(): + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + rargs.insert(0, arg[i:]) + stop = 1 + + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error("%s option requires a value" % opt) + else: + self.error("%s option requires %s values" + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + else: # option doesn't take a value + value = None + + option.process(opt, value, values, self) + + if stop: + break + + + # -- Output/error methods ------------------------------------------ + + def error (self, msg): + """error(msg : string) + + Print a usage message incorporating 'msg' to stderr and exit. + If you override this in a subclass, it should not return -- it + should either exit or raise an exception. + """ + self.print_usage(sys.stderr) + sys.stderr.write("\nSCons error: %s\n" % msg) + sys.exit(2) + + def print_usage (self, file=None): + """print_usage(file : file = stdout) + + Print the usage message for the current program (self.usage) to + 'file' (default stdout). Any occurence of the string "%prog" in + self.usage is replaced with the name of the current program + (basename of sys.argv[0]). Does nothing if self.usage is empty + or not defined. + """ + if file is None: + file = sys.stdout + if self.usage: + usage = string.replace(self.usage,"%prog", get_prog_name()) + file.write(usage + "\n") + + def print_version (self, file=None): + """print_version(file : file = stdout) + + Print the version message for this program (self.version) to + 'file' (default stdout). As with print_usage(), any occurence + of "%prog" in self.version is replaced by the current program's + name. Does nothing if self.version is empty or undefined. + """ + if file is None: + file = sys.stdout + if self.version: + version = string.replace(self.version,"%prog", get_prog_name()) + file.write(version+"\n") + + def print_help (self, file=None): + """print_help(file : file = stdout) + + Print an extended help message, listing all options and any + help text provided with them, to 'file' (default stdout). + """ + from distutils.fancy_getopt import wrap_text + + if file is None: + file = sys.stdout + + self.print_usage(file) + + # The help for each option consists of two parts: + # * the opt strings and metavars + # eg. ("-x", or "-fFILENAME, --file=FILENAME") + # * the user-supplied help string + # eg. ("turn on expert mode", "read data from FILENAME") + # + # If possible, we write both of these on the same line: + # -x turn on expert mode + # + # But if the opt string list is too long, we put the help + # string on a second line, indented to the same column it would + # start in if it fit on the first line. + # -fFILENAME, --file=FILENAME + # read data from FILENAME + + file.write("Options:\n") + width = 78 # assume 80 cols for now + + option_help = [] # list of (string, string) tuples + lengths = [] + + for option in self.option_list: + takes_value = option.takes_value() + if takes_value: + metavar = option.metavar or string.upper(option.dest) + + opts = [] # list of "-a" or "--foo=FILE" strings + if option.help is SUPPRESS_HELP: + continue + + if takes_value: + for sopt in option._short_opts: + opts.append(sopt + ' ' + metavar) + for lopt in option._long_opts: + opts.append(lopt + "=" + metavar) + else: + for opt in option._short_opts + option._long_opts: + opts.append(opt) + + opts = string.join(opts,", ") + option_help.append((opts, option.help)) + lengths.append(len(opts)) + + max_opts = min(max(lengths), 26) + + for (opts, help) in option_help: + # how much to indent lines 2 .. N of help text + indent_rest = 2 + max_opts + 2 + help_width = width - indent_rest + + if len(opts) > max_opts: + opts = " " + opts + "\n" + indent_first = indent_rest + else: # start help on same line as opts + opts = " %-*s " % (max_opts, opts) + indent_first = 0 + + file.write(opts) + + if help: + help_lines = wrap_text(help, help_width) + file.write( "%*s%s\n" % (indent_first, "", help_lines[0])) + for line in help_lines[1:]: + file.write(" %*s%s\n" % (indent_rest, "", line)) + elif opts[-1] != "\n": + file.write("\n") + +# class OptionParser + + +def _match_abbrev (s, wordmap): + """_match_abbrev(s : string, wordmap : {string : Option}) -> string + + Return the string key in 'wordmap' for which 's' is an unambiguous + abbreviation. If 's' is found to be ambiguous or doesn't match any of + 'words', raise BadOptionError. + """ + # Is there an exact match? + if wordmap.has_key(s): + return s + else: + # Isolate all words with s as a prefix. + possibilities = [] + ls = len(s) + for word in wordmap.keys(): + if len(word)>=ls and word[:ls]==s: + possibilities.append(word) + # No exact match, so there had better be just one possibility. + if len(possibilities) == 1: + return possibilities[0] + elif not possibilities: + raise BadOptionError("no such option: %s" % s) + else: + # More than one possible completion: ambiguous prefix. + raise BadOptionError("ambiguous option: %s (%s?)" + % (s, string.join(possibilities,", "))) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 1403724..bc6451c 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -39,7 +39,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import time start_time = time.time() -import getopt import os import os.path import string @@ -65,6 +64,7 @@ from SCons.Taskmaster import Taskmaster import SCons.Builder import SCons.Script.SConscript import SCons.Warnings +from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError # @@ -99,9 +99,9 @@ class BuildTask(SCons.Taskmaster.Task): # don't try to walk the stack, just print the error. sys.stderr.write("\nSCons error: %s\n" % e) raise - except: - sys.stderr.write("scons: *** %s\n" % sys.exc_value) - raise + except: + sys.stderr.write("scons: *** %s\n" % sys.exc_value) + raise def executed(self): SCons.Taskmaster.Task.executed(self) @@ -110,9 +110,9 @@ class BuildTask(SCons.Taskmaster.Task): if print_tree and self.top: print print SCons.Util.render_tree(self.targets[0], get_all_children) - if print_dtree and self.top: - print - print SCons.Util.render_tree(self.targets[0], get_derived_children) + if print_dtree and self.top: + print + print SCons.Util.render_tree(self.targets[0], get_derived_children) def failed(self): global exit_status @@ -157,29 +157,18 @@ class QuestionTask(SCons.Taskmaster.Task): # Global variables -include_dirs = [] -num_jobs = 1 -scripts = [] -task_class = BuildTask # default action is to build targets -current_func = None -calc = None -ignore_errors = 0 keep_going_on_error = 0 -help_option = None print_tree = 0 print_dtree = 0 print_time = 0 +ignore_errors = 0 sconscript_time = 0 command_time = 0 -climb_up = 0 -target_top = None exit_status = 0 # exit status, assume success by default profiling = 0 -max_drift = None repositories = [] sig_module = None -# def print_it(text): print text @@ -264,583 +253,55 @@ def _scons_other_errors(): traceback.print_exc() sys.exit(2) - -# -# After options are initialized, the following variables are -# filled in: -# -option_list = [] # list of Option objects -short_opts = "" # string of short (single-character) options -long_opts = [] # array of long (--) options -opt_func = {} # mapping of option strings to functions - -def options_init(): - """Initialize command-line options processing. - - This is in a subroutine mainly so we can easily single-step over - it in the debugger. - """ - - class Option: - """Class for command-line option information. - - This exists to provide a central location for everything - describing a command-line option, so that we can change - options without having to update the code to handle the - option in one place, the -H help message in another place, - etc. There are no methods here, only attributes. - - You can initialize an Option with the following: - - func The function that will be called when this - option is processed on the command line. - Calling sequence is: - - func(opt, arg) - - If there is no func, then this Option probably - stores an optstring to be printed. - - helpline - The string to be printed in -H output. If no - helpline is specified but a help string is - specified (the usual case), a helpline will be - constructed automatically from the short, long, - arg, and help attributes. (In practice, then, - setting helpline without setting func allows you - to print arbitrary lines of text in the -H - output.) - - short The string for short, single-hyphen - command-line options. - Do not include the hyphen: - - 'a' for -a, 'xy' for -x and -y, etc. - - long An array of strings for long, double-hyphen - command-line options. Do not include - the initial hyphens: - - ['my-option', 'verbose'] - - arg If this option takes an argument, this string - specifies how you want it to appear in the - -H output ('DIRECTORY', 'FILE', etc.). - - help The help string that will be printed for - this option in the -H output. Must be - 49 characters or fewer. - - May be an array of strings, which will be - printed on successive lines. The first string - must be 49 characters or fewer. The remaining - strings will be indented two spaces and must - be 47 characters or fewer. - - future If non-zero, this indicates that this feature - will be supported in a future release, not - the currently planned one. SCons will - recognize the option, but it won't show up - in the -H output. - - The following attribute is derived from the supplied attributes: - - optstring - A string, with hyphens, describing the flags - for this option, as constructed from the - specified short, long and arg attributes. - - All Option objects are stored in the global option_list list, - in the order in which they're created. This is the list - that's used to generate -H output, so the order in which the - objects are created is the order in which they're printed. - - The upshot is that specifying a command-line option and having - everything work correctly is a matter of defining a function to - process its command-line argument (set the right flag, update - the right value), and then creating an appropriate Option object - at the correct point in the code below. - """ - - def __init__(self, func = None, helpline = None, - short = None, long = None, arg = None, - help = None, future = None): - self.func = func - self.short = short - self.long = long - self.arg = arg - if not SCons.Util.is_List(help): - help = [help] - self.help = help - opts = [] - if self.short: - for c in self.short: - if arg: - c = c + " " + arg - opts = opts + ['-' + c] - if self.long: - l = self.long - if arg: - l = map(lambda x,a=arg: x + "=" + a, self.long) - opts = opts + map(lambda x: '--' + x, l) - self.optstring = string.join(opts, ', ') - if helpline: - self.helpline = helpline - elif help and not future: - if len(self.optstring) <= 26: - sep = " " * (28 - len(self.optstring)) - else: - sep = self.helpstring = "\n" + " " * 30 - self.helpline = " " + self.optstring + sep + \ - string.join(self.help, "\n" + " " * 32) - else: - self.helpline = None - global option_list - option_list.append(self) - - # Generic routine for to-be-written options, used by multiple - # options below. - - def opt_not_yet(opt, arg): - sys.stderr.write("Warning: the %s option is not yet implemented\n" - % opt) - - # In the following instantiations, the help string should be no - # longer than 49 characters. Use the following as a guide: - # help = "1234567890123456789012345678901234567890123456789" - - def opt_ignore(opt, arg): - sys.stderr.write("Warning: ignoring %s option\n" % opt) - - Option(func = opt_ignore, - short = 'bmSt', long = ['no-keep-going', 'stop', 'touch'], - help = "Ignored for compatibility.") - - def opt_c(opt, arg): - global task_class, calc - task_class = CleanTask - class CleanCalculator: - def bsig(self, node): - return None - def csig(self, node): - return None - def current(self, node, sig): - return 0 - def write(self): - pass - calc = CleanCalculator() - - Option(func = opt_c, - short = 'c', long = ['clean', 'remove'], - help = "Remove specified targets and dependencies.") - - Option(func = opt_not_yet, future = 1, - long = ['cache-disable', 'no-cache'], - help = "Do not retrieve built targets from Cache.") - - Option(func = opt_not_yet, future = 1, - long = ['cache-force', 'cache-populate'], - help = "Copy already-built targets into the Cache.") - - Option(func = opt_not_yet, future = 1, - long = ['cache-show'], - help = "Print what would have built Cached targets.") - - def opt_C(opt, arg): - try: - os.chdir(arg) - except: - sys.stderr.write("Could not change directory to 'arg'\n") - - Option(func = opt_C, - short = 'C', long = ['directory'], arg = 'DIRECTORY', - help = "Change to DIRECTORY before doing anything.") - - Option(func = opt_not_yet, - short = 'd', - help = "Print file dependency information.") - - def opt_D(opt, arg): - global climb_up - climb_up = 2 - - Option(func = opt_D, - short = 'D', - help = ["Search up directory tree for SConstruct,", - "build all Default() targets."]) - - def opt_debug(opt, arg): - global print_tree - global print_dtree - global print_time - if arg == "pdb": - args = [ sys.executable, "pdb.py" ] + \ - filter(lambda x: x != "--debug=pdb", sys.argv) - if sys.platform == 'win32': - args[1] = os.path.join(sys.prefix, "lib", "pdb.py") - sys.exit(os.spawnve(os.P_WAIT, args[0], args, os.environ)) - else: - args[1] = os.path.join(sys.prefix, - "lib", - "python" + sys.version[0:3], - "pdb.py") - os.execvpe(args[0], args, os.environ) - elif arg == "tree": - print_tree = 1 - elif arg == "dtree": - print_dtree = 1 - elif arg == "time": - print_time = 1 - else: - sys.stderr.write("Warning: %s is not a valid debug type\n" - % arg) - - Option(func = opt_debug, - long = ['debug'], arg='TYPE', - help = "Print various types of debugging information.") - - Option(func = opt_not_yet, future = 1, - short = 'e', long = ['environment-overrides'], - help = "Environment variables override makefiles.") - - def opt_f(opt, arg): - global scripts - scripts.append(arg) - - Option(func = opt_f, - short = 'f', long = ['file', 'makefile', 'sconstruct'], arg = 'FILE', - help = "Read FILE as the top-level SConstruct file.") - - def opt_help(opt, arg): - global help_option - help_option = 'h' - def raisePrintHelp(text): - raise PrintHelp, text - SCons.Script.SConscript.HelpFunction = raisePrintHelp - - Option(func = opt_help, - short = 'h', long = ['help'], - help = "Print defined help message, or this one.") - - def opt_help_options(opt, arg): - global help_option - help_option = 'H' - - Option(func = opt_help_options, - short = 'H', long = ['help-options'], - help = "Print this message and exit.") - - def opt_i(opt, arg): - global ignore_errors - ignore_errors = 1 - - Option(func = opt_i, - short = 'i', long = ['ignore-errors'], - help = "Ignore errors from build actions.") - - def opt_I(opt, arg): - global include_dirs - include_dirs = include_dirs + [arg] - - Option(func = opt_I, - short = 'I', long = ['include-dir'], arg = 'DIRECTORY', - help = "Search DIRECTORY for imported Python modules.") - - def opt_implicit_cache(opt, arg): - import SCons.Node - SCons.Node.implicit_cache = 1 - - Option(func = opt_implicit_cache, - long = ['implicit-cache'], - help = "Cache implicit (scanned) dependencies.") - - def opt_implicit_deps_changed(opt, arg): - import SCons.Node - SCons.Node.implicit_cache = 1 - SCons.Node.implicit_deps_changed = 1 - - Option(func = opt_implicit_deps_changed, - long = ['implicit-deps-changed'], - help = "Ignore the cached implicit dependencies.") - - def opt_implicit_deps_unchanged(opt, arg): - import SCons.Node - SCons.Node.implicit_cache = 1 - SCons.Node.implicit_deps_unchanged = 1 - - Option(func = opt_implicit_deps_unchanged, - long = ['implicit-deps-unchanged'], - help = "Ignore changes in implicit dependencies.") - - def opt_j(opt, arg): - global num_jobs - try: - num_jobs = int(arg) - except: - print UsageString() - sys.exit(1) - - if num_jobs <= 0: - print UsageString() - sys.exit(1) - - Option(func = opt_j, - short = 'j', long = ['jobs'], arg = 'N', - help = "Allow N jobs at once.") - - def opt_k(opt, arg): - global keep_going_on_error - keep_going_on_error = 1 - - Option(func = opt_k, - short = 'k', long = ['keep-going'], - help = "Keep going when a target can't be made.") - - Option(func = opt_not_yet, future = 1, - short = 'l', long = ['load-average', 'max-load'], arg = 'N', - help = "Don't start multiple jobs unless load is below N.") - - Option(func = opt_not_yet, future = 1, - long = ['list-derived'], - help = "Don't build; list files that would be built.") - - Option(func = opt_not_yet, future = 1, - long = ['list-actions'], - help = "Don't build; list files and build actions.") - - Option(func = opt_not_yet, future = 1, - long = ['list-where'], - help = "Don't build; list files and where defined.") - - def opt_max_drift(opt, arg): - global max_drift - try: - max_drift = int(arg) - except ValueError: - raise UserError, "The argument for --max-drift must be an integer." - - Option(func = opt_max_drift, - long = ['max-drift'], - arg = 'SECONDS', - help = "Set the maximum system clock drift to be SECONDS.") - - def opt_n(opt, arg): - SCons.Action.execute_actions = None - CleanTask.execute = CleanTask.show - - Option(func = opt_n, - short = 'n', long = ['no-exec', 'just-print', 'dry-run', 'recon'], - help = "Don't build; just print commands.") - - Option(func = opt_not_yet, future = 1, - short = 'o', long = ['old-file', 'assume-old'], arg = 'FILE', - help = "Consider FILE to be old; don't rebuild it.") - - Option(func = opt_not_yet, future = 1, - long = ['override'], arg = 'FILE', - help = "Override variables as specified in FILE.") - - Option(func = opt_not_yet, future = 1, - short = 'p', - help = "Print internal environments/objects.") - - def opt_profile(opt, arg): - global profiling - if not profiling: - profiling = 1 - import profile - profile.run('SCons.Script.main()', arg) - sys.exit(exit_status) - - Option(func = opt_profile, - long = ['profile'], arg = 'FILE', - help = "Profile SCons and put results in FILE.") - - def opt_Q(opt, arg): - global display - def dont_print_it(text): - pass - display = dont_print_it - - Option(func = opt_Q, - short = 'Q', - help = "Don't print SCons progress messages.") - - def opt_q(opt, arg): - global task_class - task_class = QuestionTask - - Option(func = opt_q, - short = 'q', long = ['question'], - help = "Don't build; exit status says if up to date.") - - Option(func = opt_not_yet, future = 1, - short = 'rR', long = ['no-builtin-rules', 'no-builtin-variables'], - help = "Clear default environments and variables.") - - Option(func = opt_not_yet, future = 1, - long = ['random'], - help = "Build dependencies in random order.") - - def opt_s(opt, arg): - global display - def dont_print_it(text): - pass - display = dont_print_it - SCons.Action.print_actions = None - - Option(func = opt_s, - short = 's', long = ['silent', 'quiet'], - help = "Don't print commands.") - - def opt_u(opt, arg): - global climb_up - climb_up = 1 - - Option(func = opt_u, - short = 'u', long = ['up', 'search-up'], - help = ["Search up directory tree for SConstruct,", - "build targets at or below current directory."]) - - def opt_U(opt, arg): - global climb_up - climb_up = 3 - - Option(func = opt_U, - short = 'U', - help = ["Search up directory tree for SConstruct,", - "build Default() targets from local SConscript."]) - - def option_v(opt, arg): - import __main__ - import SCons - print "SCons by Steven Knight et al.:" - try: - print "\tscript: v%s.%s, %s, by %s on %s" % (__main__.__version__, - __main__.__build__, - __main__.__date__, - __main__.__developer__, - __main__.__buildsys__) - except: - # On win32 there is no scons.py, so there is no __main__.__version__, - # hence there is no script version. - pass - print "\tengine: v%s.%s, %s, by %s on %s" % (SCons.__version__, - SCons.__build__, - SCons.__date__, - SCons.__developer__, - SCons.__buildsys__) - print "Copyright 2001, 2002 Steven Knight" - sys.exit(0) - - Option(func = option_v, - short = 'v', long = ['version'], - help = "Print the SCons version number and exit.") - - Option(func = opt_not_yet, future = 1, - short = 'w', long = ['print-directory'], - help = "Print the current directory.") - - Option(func = opt_not_yet, future = 1, - long = ['no-print-directory'], - help = "Turn off -w, even if it was turned on implicitly.") - - Option(func = opt_not_yet, future = 1, - long = ['write-filenames'], arg = 'FILE', - help = "Write all filenames examined into FILE.") - - Option(func = opt_not_yet, future = 1, - short = 'W', long = ['what-if', 'new-file', 'assume-new'], arg = 'FILE', - help = "Consider FILE to be changed.") - - def opt_warn(opt, arg): - """The --warn option. An argument to this option - should be of the form or no-. - The warning class is munged in order to get an actual class - name from the SCons.Warnings module to enable or disable. - The supplied is split on hyphens, each element - is captialized, then smushed back together. Then the string - "SCons.Warnings." is added to the front and "Warning" is added - to the back to get the fully qualified class name. - - For example, --warn=deprecated will enable the - SCons.Warnings.DeprecatedWarning class. - - --warn=no-dependency will disable the - SCons.Warnings.DependencyWarning class. - - As a special case, --warn=all and --warn=no-all - will enable or disable (respectively) the base - class of all warnings, which is SCons.Warning.Warning.""" - - elems = string.split(string.lower(arg), '-') - enable = 1 - if elems[0] == 'no': - enable = 0 - del elems[0] - - if len(elems) == 1 and elems[0] == 'all': - class_name = "Warning" - else: - class_name = string.join(map(string.capitalize, elems), '') + \ - "Warning" - try: - clazz = getattr(SCons.Warnings, class_name) - except AttributeError: - sys.stderr.write("No warning type: '%s'\n" % arg) +def _varargs(option, parser): + value = None + if parser.rargs: + arg = parser.rargs[0] + if arg[0] != "-": + value = arg + del parser.rargs[0] + return value + +def _setup_warn(arg): + """The --warn option. An argument to this option + should be of the form or no-. + The warning class is munged in order to get an actual class + name from the SCons.Warnings module to enable or disable. + The supplied is split on hyphens, each element + is captialized, then smushed back together. Then the string + "SCons.Warnings." is added to the front and "Warning" is added + to the back to get the fully qualified class name. + + For example, --warn=deprecated will enable the + SCons.Warnings.DeprecatedWarning class. + + --warn=no-dependency will disable the + SCons.Warnings.DependencyWarning class. + + As a special case, --warn=all and --warn=no-all + will enable or disable (respectively) the base + class of all warnings, which is SCons.Warning.Warning.""" + + elems = string.split(string.lower(arg), '-') + enable = 1 + if elems[0] == 'no': + enable = 0 + del elems[0] + + if len(elems) == 1 and elems[0] == 'all': + class_name = "Warning" + else: + class_name = string.join(map(string.capitalize, elems), '') + \ + "Warning" + try: + clazz = getattr(SCons.Warnings, class_name) + except AttributeError: + sys.stderr.write("No warning type: '%s'\n" % arg) + else: + if enable: + SCons.Warnings.enableWarningClass(clazz) else: - if enable: - SCons.Warnings.enableWarningClass(clazz) - else: - SCons.Warnings.suppressWarningClass(clazz) - - Option(func = opt_warn, - long = [ 'warn', 'warning' ], arg='WARNING-SPEC', - help = "Enable or disable warnings.") - - - # We want to preserve the --warn-undefined-variables option for - # compatibility with GNU Make. Unfortunately, this conflicts with - # the --warn=type option that we're using for our own warning - # control. The getopt module reports "--warn not a unique prefix" - # when both are defined. We may be able to support both in the - # future with a more robust getopt solution. - # - #Option(func = opt_not_yet, future = 1, - # long = ['warn-undefined-variables'], - # help = "Warn when an undefined variable is referenced.") - - def opt_Y(opt, arg): - global repositories - repositories.append(arg) - - Option(func = opt_Y, - short = 'Y', long = ['repository'], arg = 'REPOSITORY', - help = "Search REPOSITORY for source and target files.") - - global short_opts - global long_opts - global opt_func - for o in option_list: - if o.short: - if o.func: - for c in o.short: - opt_func['-' + c] = o.func - short_opts = short_opts + o.short - if o.arg: - short_opts = short_opts + ":" - if o.long: - if o.func: - for l in o.long: - opt_func['--' + l] = o.func - if o.arg: - long_opts = long_opts + map(lambda a: a + "=", o.long) - else: - long_opts = long_opts + o.long - -options_init() - - + SCons.Warnings.suppressWarningClass(clazz) def _SConstruct_exists(dirname=''): """This function checks that an SConstruct file exists in a directory. @@ -858,60 +319,343 @@ def _SConstruct_exists(dirname=''): return sfile return None +def _set_globals(options): + global repositories, keep_going_on_error, print_tree, print_dtree + global print_time, ignore_errors + + if options.repository: + repositories.extend(options.repository) + keep_going_on_error = options.keep_going + try: + if options.debug: + if options.debug == "tree": + print_tree = 1 + elif options.debug == "dtree": + print_dtree = 1 + elif options.debug == "time": + print_time = 1 + except AttributeError: + pass + ignore_errors = options.ignore_errors + +def _create_path(plist): + path = '.' + for d in plist: + if os.path.isabs(d): + path = d + else: + path = path + '/' + d + return path -def UsageString(): - help_opts = filter(lambda x: x.helpline, option_list) - s = "Usage: scons [OPTION] [TARGET] ...\n" + "Options:\n" + \ - string.join(map(lambda x: x.helpline, help_opts), "\n") + "\n" - return s +class OptParser(OptionParser): + def __init__(self): + import __main__ + import SCons + parts = ["SCons by Steven Knight et al.:\n"] + try: + parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__, + __main__.__build__, + __main__.__date__, + __main__.__developer__, + __main__.__buildsys__)) + except: + # On win32 there is no scons.py, so there is no __main__.__version__, + # hence there is no script version. + pass + parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__, + SCons.__build__, + SCons.__date__, + SCons.__developer__, + SCons.__buildsys__)) + parts.append("Copyright 2001, 2002 Steven Knight") + OptionParser.__init__(self, version=string.join(parts, ''), + usage="usage: scons [OPTION] [TARGET] ...") + + # options ignored for compatibility + def opt_ignore(option, opt, value, parser): + sys.stderr.write("Warning: ignoring %s option\n" % opt) + self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop", + "--touch", action="callback", callback=opt_ignore, + help="Ignored for compatibility.") + + def opt_c(option, opt, value, parser): + setattr(parser.values, 'clean', 1) + self.add_option('-c', '--clean', '--remove', action="callback", + callback=opt_c, + help="Remove specified targets and dependencies.") + + self.add_option('-C', '--directory', type="string", action = "append", + help="Change to DIRECTORY before doing anything.") + + def opt_not_yet(option, opt, value, parser): + sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt) + sys.exit(0) + self.add_option('-d', action="callback", + callback=opt_not_yet, + help = "Print file dependency information.") + + self.add_option('-D', action="store_const", const=2, dest="climb_up", + help="Search up directory tree for SConstruct, " + "build all Default() targets.") + + def opt_debug(option, opt, value, parser): + if value == "pdb": + args = [ sys.executable, "pdb.py" ] + \ + filter(lambda x: x != "--debug=pdb", sys.argv) + if sys.platform == 'win32': + args[1] = os.path.join(sys.prefix, "lib", "pdb.py") + sys.exit(os.spawnve(os.P_WAIT, args[0], args, os.environ)) + else: + args[1] = os.path.join(sys.prefix, + "lib", + "python" + sys.version[0:3], + "pdb.py") + os.execvpe(args[0], args, os.environ) + elif value in ["tree", "dtree", "time"]: + setattr(parser.values, 'debug', value) + else: + raise OptionValueError("Warning: %s is not a valid debug type" % value) + self.add_option('--debug', action="callback", type="string", + callback=opt_debug, nargs=1, dest="debug", + help="Print various types of debugging information.") + + self.add_option('-f', '--file', '--makefile', '--sconstruct', + action="append", nargs=1, + help="Read FILE as the top-level SConstruct file.") + + self.add_option('-h', '--help', action="store_true", default=0, + dest="help_msg", + help="Print defined help message, or this one.") + + self.add_option("-H", "--help-options", + action="help", + help="Print this message and exit.") + + self.add_option('-i', '--ignore-errors', action="store_true", + default=0, dest='ignore_errors', + help="Ignore errors from build actions.") + + self.add_option('-I', '--include-dir', action="append", + dest='include_dir', metavar="DIRECTORY", + help="Search DIRECTORY for imported Python modules.") + + self.add_option('--implicit-cache', action="store_true", default=0, + dest='implicit_cache', + help="Cache implicit dependencies") + + self.add_option('--implicit-deps-changed', action="store_true", + default=0, dest='implicit_deps_changed', + help="Ignore the cached implicit deps.") + self.add_option('--implicit-deps-unchanged', action="store_true", + default=0, dest='implicit_deps_unchanged', + help="Ignore changes in implicit deps.") + + def opt_j(option, opt, value, parser): + value = int(value) + setattr(parser.values, 'num_jobs', value) + self.add_option('-j', '--jobs', action="callback", type="int", + callback=opt_j, metavar="N", + help="Allow N jobs at once.") + + self.add_option('-k', '--keep-going', action="store_true", default=0, + dest='keep_going', + help="Keep going when a target can't be made.") + + self.add_option('--max-drift', type="int", action="store", + dest='max_drift', + help="Set the maximum system clock drift to be" + " MAX_DRIFT seconds.") + + self.add_option('-n', '--no-exec', '--just-print', '--dry-run', + '--recon', action="store_true", dest='noexec', + default=0, help="Don't build; just print commands.") + + def opt_profile(option, opt, value, parser): + global profiling + if not profiling: + profiling = 1 + import profile + profile.run('SCons.Script.main()', value) + sys.exit(exit_status) + self.add_option('--profile', nargs=1, action="callback", + callback=opt_profile, type="string", dest="profile", + help="Profile SCons and put results in PROFILE.") + + self.add_option('-q', '--question', action="store_true", default=0, + help="Don't build; exit status says if up to date.") + + self.add_option('-Q', dest='no_progress', action="store_true", + default=0, + help="Don't print SCons progress messages.") + + self.add_option('-s', '--silent', '--quiet', action="store_true", + default=0, help="Don't print commands.") + + self.add_option('-u', '--up', '--search-up', action="store_const", + dest="climb_up", default=0, const=1, + help="Search up directory tree for SConstruct, " + "build targets at or below current directory.") + self.add_option('-U', action="store_const", dest="climb_up", + default=0, const=3, + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript.") + + self.add_option("-v", "--version", + action="version", + help="Print the SCons version number and exit.") + + self.add_option('--warn', '--warning', nargs=1, action="store", + metavar="WARNING-SPEC", + help="Enable or disable warnings.") + + self.add_option('-Y', '--repository', nargs=1, action="append", + help="Search REPOSITORY for source and target files.") + + self.add_option('--cache-disable', '--no-cache', action="callback", + callback=opt_not_yet, + # help = "Do not retrieve built targets from Cache." + help=SUPPRESS_HELP) + self.add_option('--cache-force', '--cache-populate', action="callback", + callback=opt_not_yet, + # help = "Copy already-built targets into the Cache." + help=SUPPRESS_HELP) + self.add_option('--cache-show', action="callback", + callback=opt_not_yet, + # help = "Print what would have built Cached targets.", + help=SUPPRESS_HELP) + self.add_option('-e', '--environment-overrides', action="callback", + callback=opt_not_yet, + # help="Environment variables override makefiles." + help=SUPPRESS_HELP) + self.add_option('-l', '--load-average', '--max-load', action="callback", + callback=opt_not_yet, type="int", dest="load_average", + # action="store", + # help="Don't start multiple jobs unless load is below " + # "LOAD-AVERAGE." + # type="int", + help=SUPPRESS_HELP) + self.add_option('--list-derived', action="callback", + callback=opt_not_yet, + # help="Don't build; list files that would be built." + help=SUPPRESS_HELP) + self.add_option('--list-actions', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and build actions." + help=SUPPRESS_HELP) + self.add_option('--list-where', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and where defined." + help=SUPPRESS_HELP) + self.add_option('-o', '--old-file', '--assume-old', action="callback", + callback=opt_not_yet, type="string", dest="old_file", + # help = "Consider FILE to be old; don't rebuild it." + help=SUPPRESS_HELP) + self.add_option('--override', action="callback", dest="override", + callback=opt_not_yet, type="string", + # help="Override variables as specified in FILE." + help=SUPPRESS_HELP) + self.add_option('-p', action="callback", + callback=opt_not_yet, + # help="Print internal environments/objects." + help=SUPPRESS_HELP) + self.add_option('-r', '-R', '--no-builtin-rules', + '--no-builtin-variables', action="callback", + callback=opt_not_yet, + # help="Clear default environments and variables." + help=SUPPRESS_HELP) + self.add_option('--random', action="callback", + callback=opt_not_yet, + # help="Build dependencies in random order." + help=SUPPRESS_HELP) + self.add_option('-w', '--print-directory', action="callback", + callback=opt_not_yet, + # help="Print the current directory." + help=SUPPRESS_HELP) + self.add_option('--no-print-directory', action="callback", + callback=opt_not_yet, + # help="Turn off -w, even if it was turned on implicitly." + help=SUPPRESS_HELP) + self.add_option('--write-filenames', action="callback", + callback=opt_not_yet, type="string", dest="write_filenames", + # help="Write all filenames examined into FILE." + help=SUPPRESS_HELP) + self.add_option('-W', '--what-if', '--new-file', '--assume-new', + dest="new_file", + action="callback", callback=opt_not_yet, type="string", + # help="Consider FILE to be changed." + help=SUPPRESS_HELP) + self.add_option('--warn-undefined-variables', action="callback", + callback=opt_not_yet, + # help="Warn when an undefined variable is referenced." + help=SUPPRESS_HELP) + + def parse_args(self, args=None, values=None): + opt, arglist = OptionParser.parse_args(self, args, values) + if opt.implicit_deps_changed or opt.implicit_deps_unchanged: + opt.implicit_cache = 1 + if not hasattr(opt, "num_jobs"): + setattr(opt, "num_jobs", 1) + return opt, arglist def _main(): - global scripts, num_jobs, task_class, calc, target_top + import SCons.Node targets = [] - # It looks like 2.0 changed the name of the exception class - # raised by getopt. - try: - getopt_err = getopt.GetoptError - except: - getopt_err = getopt.error - # Enable deprecated warnings by default. SCons.Warnings._warningOut = _scons_internal_warning SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning) SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning) + all_args = sys.argv[1:] try: - cmd_opts, t = getopt.getopt(string.split(os.environ['SCONSFLAGS']), - short_opts, long_opts) + all_args = string.split(os.environ['SCONSFLAGS']) + all_args except KeyError: - # It's all right if there's no SCONSFLAGS environment variable. - pass - except getopt_err, x: - _scons_user_warning("SCONSFLAGS " + str(x)) - else: - for opt, arg in cmd_opts: - opt_func[opt](opt, arg) + # it's OK if there's no SCONSFLAGS + pass + parser = OptParser() + options, args = parser.parse_args(all_args) - try: - cmd_opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts) - except getopt_err, x: - _scons_user_error(x) - else: - for opt, arg in cmd_opts: - opt_func[opt](opt, arg) - xmit_args = [] - for a in args: - if '=' in a: - xmit_args.append(a) - else: - targets.append(a) - SCons.Script.SConscript._scons_add_args(xmit_args) + if options.help_msg: + def raisePrintHelp(text): + raise PrintHelp, text + SCons.Script.SConscript.HelpFunction = raisePrintHelp - if climb_up: + _set_globals(options) + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.implicit_deps_changed = options.implicit_deps_changed + SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + if options.warn: + _setup_warn(options.warn) + if options.noexec: + SCons.Action.execute_actions = None + CleanTask.execute = CleanTask.show + if options.no_progress or options.silent: + global display + def dont_print_it(text): + pass + display = dont_print_it + if options.silent: + SCons.Action.print_actions = None + if options.directory: + cdir = _create_path(options.directory) + try: + os.chdir(cdir) + except: + sys.stderr.write("Could not change directory to %s\n" % cdir) + + xmit_args = [] + for a in args: + if '=' in a: + xmit_args.append(a) + else: + targets.append(a) + SCons.Script.SConscript._scons_add_args(xmit_args) + + target_top = None + if options.climb_up: target_top = '.' # directory to prepend to targets script_dir = os.getcwd() # location of script while script_dir and not _SConstruct_exists(script_dir): @@ -928,24 +672,25 @@ def _main(): SCons.Node.FS.default_fs.set_toplevel_dir(os.getcwd()) + scripts = [] + if options.file: + scripts.extend(options.file) if not scripts: sfile = _SConstruct_exists() if sfile: scripts.append(sfile) - if help_option == 'H': - print UsageString() - sys.exit(0) - - if not scripts: - if help_option == 'h': + if options.help_msg: + if not scripts: # There's no SConstruct, but they specified -h. # Give them the options usage now, before we fail # trying to read a non-existent SConstruct file. - print UsageString() - sys.exit(0) - else: - raise UserError, "No SConstruct file found." + parser.print_help() + sys.exit(0) + SCons.Script.SConscript.print_help = 1 + + if not scripts: + raise UserError, "No SConstruct file found." class Unbuffered: def __init__(self, file): @@ -958,7 +703,8 @@ def _main(): sys.stdout = Unbuffered(sys.stdout) - sys.path = include_dirs + sys.path + if options.include_dir: + sys.path = options.include_dir + sys.path global repositories for rep in repositories: @@ -976,27 +722,26 @@ def _main(): print text print "Use scons -H for help about command-line options." sys.exit(0) - display("scons: done reading SConscript files.") SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top) - if help_option == 'h': - # They specified -h, but there was no Help() inside the - # SConscript files. Give them the options usage. - print UsageString() - sys.exit(0) + if options.help_msg: + # They specified -h, but there was no Help() inside the + # SConscript files. Give them the options usage. + parser.print_help(sys.stdout) + sys.exit(0) if target_top: target_top = SCons.Node.FS.default_fs.Dir(target_top) - if climb_up == 2 and not targets: + if options.climb_up == 2 and not targets: # -D with default targets target_top = None - elif climb_up == 3 and not targets: + elif options.climb_up == 3 and not targets: # -U with default targets default_targets = SCons.Script.SConscript.default_targets - def check_dir(x): + def check_dir(x, target_top=target_top): cwd = x.cwd.srcnode() return cwd == target_top default_targets = filter(check_dir, default_targets) @@ -1038,22 +783,42 @@ def _main(): nodes = filter(lambda x: x is not None, map(Entry, targets)) + calc = None + task_class = BuildTask # default action is to build targets + if options.question: + task_class = QuestionTask + try: + if options.clean: + task_class = CleanTask + class CleanCalculator: + def bsig(self, node): + return None + def csig(self, node): + return None + def current(self, node, sig): + return 0 + def write(self): + pass + calc = CleanCalculator() + except AttributeError: + pass + if not calc: - if max_drift is not None: - if sig_module is not None: + if options.max_drift is not None: + if sig_module is not None: SCons.Sig.default_calc = SCons.Sig.Calculator(module=sig_module, - max_drift=max_drift) - else: - SCons.Sig.default_calc = SCons.Sig.Calculator(max_drift=max_drift) + max_drift=options.max_drift) + else: + SCons.Sig.default_calc = SCons.Sig.Calculator(max_drift=options.max_drift) elif sig_module is not None: SCons.Sig.default_calc = SCons.Sig.Calculator(module=sig_module) - + calc = SCons.Sig.default_calc display("scons: Building targets ...") taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, calc) - jobs = SCons.Job.Jobs(num_jobs, taskmaster) + jobs = SCons.Job.Jobs(options.num_jobs, taskmaster) try: jobs.run() diff --git a/src/engine/setup.py b/src/engine/setup.py index d1ef81b..115bced 100644 --- a/src/engine/setup.py +++ b/src/engine/setup.py @@ -63,6 +63,7 @@ software.""", keywords = "scons, cons, make, build tool, make tool", packages = ["SCons", "SCons.Node", + "SCons.Optik", "SCons.Scanner", "SCons.Sig", "SCons.Script"]) diff --git a/src/setup.py b/src/setup.py index ceb4440..50c1491 100644 --- a/src/setup.py +++ b/src/setup.py @@ -76,6 +76,7 @@ arguments = { 'version' : "__VERSION__", 'packages' : ["SCons", "SCons.Node", + "SCons.Optik", "SCons.Platform", "SCons.Scanner", "SCons.Script", diff --git a/test/SCONSFLAGS.py b/test/SCONSFLAGS.py index f8d757a..2d0ac8d 100644 --- a/test/SCONSFLAGS.py +++ b/test/SCONSFLAGS.py @@ -29,7 +29,7 @@ import TestSCons import os import string -test = TestSCons.TestSCons(match = TestCmd.match_re) +test = TestSCons.TestSCons() wpath = test.workpath() @@ -59,11 +59,10 @@ test.fail_test(string.find(test.stdout(), '-H, --help-options') == -1) os.environ['SCONSFLAGS'] = '-Z' -test.run(arguments = "-H", stderr = r""" -SCons warning: SCONSFLAGS option -Z not recognized -File "[^"]*", line \d+, in \S+ -""") +test.run(arguments = "-H", status = 2, + stderr = r"""usage: scons [OPTION] [TARGET] ... -test.fail_test(string.find(test.stdout(), '-H, --help-options') == -1) +SCons error: no such option: -Z +""") test.pass_test() diff --git a/test/option--profile.py b/test/option--profile.py index 6d7efe4..5ba9c46 100644 --- a/test/option--profile.py +++ b/test/option--profile.py @@ -49,9 +49,9 @@ stats.strip_dirs().print_stats() s = sys.stdout.getvalue() test.fail_test(string.find(s, '__init__.py') == -1) -test.fail_test(string.find(s, 'option_v') == -1) +test.fail_test(string.find(s, 'print_version') == -1) test.fail_test(string.find(s, 'SCons.Script.main()') == -1) -test.fail_test(string.find(s, 'getopt.py') == -1) +test.fail_test(string.find(s, 'option_parser.py') == -1) scons_prof = test.workpath('scons2.prof') @@ -70,9 +70,9 @@ stats.strip_dirs().print_stats() s = sys.stdout.getvalue() test.fail_test(string.find(s, '__init__.py') == -1) -test.fail_test(string.find(s, 'option_v') == -1) +test.fail_test(string.find(s, 'print_version') == -1) test.fail_test(string.find(s, 'SCons.Script.main()') == -1) -test.fail_test(string.find(s, 'getopt.py') == -1) +test.fail_test(string.find(s, 'option_parser.py') == -1) test.pass_test() diff --git a/test/option-unknown.py b/test/option-unknown.py index 496a7e0..7ddad94 100644 --- a/test/option-unknown.py +++ b/test/option-unknown.py @@ -29,21 +29,21 @@ import TestSCons import string import sys -test = TestSCons.TestSCons(match = TestCmd.match_re) +test = TestSCons.TestSCons() test.write('SConstruct', "") test.run(arguments = '-Z', - stderr = """ -SCons error: option -Z not recognized -File "\S+", line \d+, in short_has_arg + stderr = """usage: scons [OPTION] [TARGET] ... + +SCons error: no such option: -Z """, status = 2) test.run(arguments = '--ZizzerZazzerZuzz', - stderr = """ -SCons error: option --ZizzerZazzerZuzz not recognized -File "\S+", line \d+, in long_has_args + stderr = """usage: scons [OPTION] [TARGET] ... + +SCons error: no such option: --ZizzerZazzerZuzz """, status = 2) -- cgit v0.12