DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (4a108e94d3e2)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
# 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 optparse
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:]):
        self.metadata = getattr(sys.modules[self.module],
                                'package_metadata',
                                {})
        version = self.metadata.get('Version')
        parser_args = {'description': self.metadata.get('Summary')}
        if version:
            parser_args['version'] = "%prog " + version
        self.parser = optparse.OptionParser(**parser_args)
        self.add_options(self.parser)
        (self.options, self.args) = self.parser.parse_args(args)

        if getattr(self.options, 'info', None):
            self.print_metadata()
            sys.exit(0)

        # 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"""

        # 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")
        if self.metadata:
            parser.add_option("--info", dest="info", default=False,
                              action="store_true",
                              help="Print module information")

    ### methods for introspecting data

    def get_metadata_from_egg(self):
        import pkg_resources
        ret = {}
        dist = pkg_resources.get_distribution(self.module)
        if dist.has_metadata("PKG-INFO"):
            for line in dist.get_metadata_lines("PKG-INFO"):
                key, value = line.split(':', 1)
                ret[key] = value
        if dist.has_metadata("requires.txt"):
            ret["Dependencies"] = "\n" + dist.get_metadata("requires.txt")
        return ret

    def print_metadata(self, data=("Name", "Version", "Summary", "Home-page",
                                   "Author", "Author-email", "License", "Platform", "Dependencies")):
        for key in data:
            if key in self.metadata:
                print key + ": " + self.metadata[key]

    ### 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()