| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| # You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| import os |
| import sys |
| |
| from mozprofile import MozProfileCLI |
| |
| from .application import get_app_context |
| from .runners import runners |
| from .utils import findInPath |
| |
| # Map of debugging programs to information about them |
| # from http://mxr.mozilla.org/mozilla-central/source/build/automationutils.py#59 |
| DEBUGGERS = {'gdb': {'interactive': True, |
| 'args': ['-q', '--args'],}, |
| 'valgrind': {'interactive': False, |
| 'args': ['--leak-check=full']} |
| } |
| |
| def debugger_arguments(debugger, arguments=None, interactive=None): |
| """Finds debugger arguments from debugger given and defaults |
| |
| :param debugger: name or path to debugger |
| :param arguments: arguments for the debugger, or None to use defaults |
| :param interactive: whether the debugger should run in interactive mode |
| |
| """ |
| # find debugger executable if not a file |
| executable = debugger |
| if not os.path.exists(executable): |
| executable = findInPath(debugger) |
| if executable is None: |
| raise Exception("Path to '%s' not found" % debugger) |
| |
| # if debugger not in dictionary of knowns return defaults |
| dirname, debugger = os.path.split(debugger) |
| if debugger not in DEBUGGERS: |
| return ([executable] + (arguments or []), bool(interactive)) |
| |
| # otherwise use the dictionary values for arguments unless specified |
| if arguments is None: |
| arguments = DEBUGGERS[debugger].get('args', []) |
| if interactive is None: |
| interactive = DEBUGGERS[debugger].get('interactive', False) |
| return ([executable] + arguments, interactive) |
| |
| |
| class CLI(MozProfileCLI): |
| """Command line interface""" |
| |
| module = "mozrunner" |
| |
| def __init__(self, args=sys.argv[1:]): |
| MozProfileCLI.__init__(self, args=args) |
| |
| # choose appropriate runner and profile classes |
| app = self.options.app |
| try: |
| self.runner_class = runners[app] |
| self.profile_class = get_app_context(app).profile_class |
| except KeyError: |
| self.parser.error('Application "%s" unknown (should be one of "%s")' % |
| (app, ', '.join(runners.keys()))) |
| |
| def add_options(self, parser): |
| """add options to the parser""" |
| parser.description = ("Reliable start/stop/configuration of Mozilla" |
| " Applications (Firefox, Thunderbird, etc.)") |
| |
| # add profile options |
| MozProfileCLI.add_options(self, parser) |
| |
| # add runner options |
| parser.add_option('-b', "--binary", |
| dest="binary", help="Binary path.", |
| metavar=None, default=None) |
| parser.add_option('--app', dest='app', default='firefox', |
| help="Application to use [DEFAULT: %default]") |
| parser.add_option('--app-arg', dest='appArgs', |
| default=[], action='append', |
| help="provides an argument to the test application") |
| parser.add_option('--debugger', dest='debugger', |
| help="run under a debugger, e.g. gdb or valgrind") |
| parser.add_option('--debugger-args', dest='debugger_args', |
| action='store', |
| help="arguments to the debugger") |
| parser.add_option('--interactive', dest='interactive', |
| action='store_true', |
| help="run the program interactively") |
| |
| ### methods for running |
| |
| def command_args(self): |
| """additional arguments for the mozilla application""" |
| return map(os.path.expanduser, self.options.appArgs) |
| |
| def runner_args(self): |
| """arguments to instantiate the runner class""" |
| return dict(cmdargs=self.command_args(), |
| binary=self.options.binary) |
| |
| def create_runner(self): |
| profile = self.profile_class(**self.profile_args()) |
| return self.runner_class(profile=profile, **self.runner_args()) |
| |
| def run(self): |
| runner = self.create_runner() |
| self.start(runner) |
| runner.cleanup() |
| |
| def debugger_arguments(self): |
| """Get the debugger arguments |
| |
| returns a 2-tuple of debugger arguments: |
| (debugger_arguments, interactive) |
| |
| """ |
| debug_args = self.options.debugger_args |
| if debug_args is not None: |
| debug_args = debug_args.split() |
| interactive = self.options.interactive |
| if self.options.debugger: |
| debug_args, interactive = debugger_arguments(self.options.debugger, debug_args, interactive) |
| return debug_args, interactive |
| |
| def start(self, runner): |
| """Starts the runner and waits for the application to exit |
| |
| It can also happen via a keyboard interrupt. It should be |
| overwritten to provide custom running of the runner instance. |
| |
| """ |
| # attach a debugger if specified |
| debug_args, interactive = self.debugger_arguments() |
| runner.start(debug_args=debug_args, interactive=interactive) |
| print 'Starting: ' + ' '.join(runner.command) |
| try: |
| runner.wait() |
| except KeyboardInterrupt: |
| runner.stop() |
| |
| |
| def cli(args=sys.argv[1:]): |
| CLI(args).run() |
| |
| |
| if __name__ == '__main__': |
| cli() |