author Ehsan Akhgari <>
Fri, 02 Jan 2015 21:13:39 -0500
changeset 247993 bc7e683ef708f64d1cefdc4a01762ea79ec48534
parent 241268 e00f07b0609e76f919f20b04303171f59e8dd893
child 258313 d59b572e546f1514aa59f31f134de87f2310901c
permissions -rw-r--r--
Bug 1117264 - Mark virtual overridden functions as MOZ_OVERRIDE in dom/base code; r=baku

# 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

from __future__ import unicode_literals

import sys
import os
import stat
import platform
import urllib2
import errno

from mach.decorators import (

from mozbuild.base import MachCommandBase

class SearchProvider(object):
    @Command('mxr', category='misc',
        description='Search for something in MXR.')
    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
    def mxr(self, term):
        import webbrowser
        term = ' '.join(term)
        uri = '' % term

    @Command('dxr', category='misc',
        description='Search for something in DXR.')
    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
    def dxr(self, term):
        import webbrowser
        term = ' '.join(term)
        uri = '' % term

    @Command('mdn', category='misc',
        description='Search for something on MDN.')
    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
    def mdn(self, term):
        import webbrowser
        term = ' '.join(term)
        uri = '' % term

    @Command('google', category='misc',
        description='Search for something on Google.')
    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
    def google(self, term):
        import webbrowser
        term = ' '.join(term)
        uri = '' % term

    @Command('search', category='misc',
        description='Search for something on the Internets. '
        'This will open 3 new browser tabs and search for the term on Google, '
        'MDN, and MXR.')
    @CommandArgument('term', nargs='+', help='Term(s) to search for.')
    def search(self, term):

class Interface(object):
    Represents an XPIDL interface, in what file it is defined, what it derives
    from, what its uuid is, and where in the source file the uuid is.
    def __init__(self, filename, production):
        import xpidl
        assert isinstance(production, xpidl.Interface) =
        self.base = production.base
        self.filename = filename
        self.uuid = production.attributes.uuid
        location = production.location
        data = location._lexdata
        attr_pos = data.rfind(b'[', 0, location._lexpos)
        # uuid is always lowercase, but actual file content may not be.
        self.uuid_pos = data[attr_pos:location._lexpos].lower() \
                        .rfind(self.uuid) + attr_pos

class InterfaceRegistry(object):
    Tracks XPIDL interfaces, and allow to search them by name and by the
    interface they derive from.
    def __init__(self):
        self.by_name = {}
        self.by_base = {}

    def get_by_name(self, name):
        return self.by_name.get(name, [])

    def get_by_base(self, base):
        return self.by_base.get(base, [])

    def add(self, interface):
        l = self.by_name.setdefault(, [])
        l = self.by_base.setdefault(interface.base, [])

class IDLUpdater(object):
    Updates interfaces uuids in IDL files.
    def __init__(self, interfaces):
        from mozpack.copier import FileRegistry
        self.interfaces = interfaces;
        self.registry = FileRegistry()

    def add(self, name):
        for interface in self.interfaces.get_by_name(name):

    def _add(self, interface):
        from mozpack.files import GeneratedFile
        from uuid import uuid4
        path = interface.filename
        if not self.registry.contains(path):
            self.registry.add(path, GeneratedFile(open(path).read()))
        content = self.registry[path].content
        content = content[:interface.uuid_pos] + str(uuid4()) + \
                  content[interface.uuid_pos + len(interface.uuid):]
        self.registry[path].content = content

        # Recurse through all the interfaces deriving from this one
        for derived in self.interfaces.get_by_base(

    def update(self):
        for p, f in self.registry:

class UUIDProvider(object):
    @Command('uuid', category='misc',
        description='Generate a uuid.')
    @CommandArgument('--format', '-f', choices=['idl', 'cpp', 'c++'],
                     help='Output format for the generated uuid.')
    def uuid(self, format=None):
        import uuid
        u = uuid.uuid4()
        if format in [None, 'idl']:
            if format is None:
        if format in [None, 'cpp', 'c++']:
            u = u.hex
            print('{ 0x%s, 0x%s, 0x%s, \\' % (u[0:8], u[8:12], u[12:16]))
            pairs = tuple(map(lambda n: u[n:n+2], range(16, 32, 2)))
            print(('  { ' + '0x%s, ' * 7 + '0x%s } }') % pairs)

    @Command('update-uuids', category='misc',
        description='Update IDL files with new UUIDs.')
    @CommandArgument('--path', default='.',
                     help='Base path under which uuids will be searched.')
    @CommandArgument('interfaces', nargs='+',
                     help='Changed interfaces whose UUIDs need to be updated. ' +
                          'Their descendants are updated as well.')
    def update_uuids(self, path, interfaces):
        import os
        import xpidl
        from mozpack.files import FileFinder
        import mozpack.path
        from tempfile import mkdtemp

        finder = FileFinder(path, find_executables=False)
        # Avoid creating xpidllex and xpidlyacc in the current directory.
        tmpdir = mkdtemp()
            parser = xpidl.IDLParser(outputdir=tmpdir)
            registry = InterfaceRegistry()
            for p, f in finder.find('**/*.idl'):
                p = mozpack.path.join(path, p)
                    content =
                    idl = parser.parse(content, filename=p)
                except Exception:
                for prod in
                    if isinstance(prod, xpidl.Interface):
                         registry.add(Interface(p, prod))
            import shutil

        updates = IDLUpdater(registry)

        for interface in interfaces:


class PastebinProvider(object):
    @Command('pastebin', category='misc',
        description='Command line interface to')
    @CommandArgument('--language', default=None,
                     help='Language to use for syntax highlighting')
    @CommandArgument('--poster', default=None,
                     help='Specify your name for use with')
    @CommandArgument('--duration', default='day',
                     choices=['d', 'day', 'm', 'month', 'f', 'forever'],
                     help='Keep for specified duration (default: %(default)s)')
    @CommandArgument('file', nargs='?', default=None,
                     help='Specify the file to upload to')

    def pastebin(self, language, poster, duration, file):
        import sys
        import urllib

        URL = ''

        FILE_TYPES = [{'value': 'text', 'name': 'None', 'extension': 'txt'},
        {'value': 'bash', 'name': 'Bash', 'extension': 'sh'},
        {'value': 'c', 'name': 'C', 'extension': 'c'},
        {'value': 'cpp', 'name': 'C++', 'extension': 'cpp'},
        {'value': 'html4strict', 'name': 'HTML', 'extension': 'html'},
        {'value': 'javascript', 'name': 'Javascript', 'extension': 'js'},
        {'value': 'javascript', 'name': 'Javascript', 'extension': 'jsm'},
        {'value': 'lua', 'name': 'Lua', 'extension': 'lua'},
        {'value': 'perl', 'name': 'Perl', 'extension': 'pl'},
        {'value': 'php', 'name': 'PHP', 'extension': 'php'},
        {'value': 'python', 'name': 'Python', 'extension': 'py'},
        {'value': 'ruby', 'name': 'Ruby', 'extension': 'rb'},
        {'value': 'css', 'name': 'CSS', 'extension': 'css'},
        {'value': 'diff', 'name': 'Diff', 'extension': 'diff'},
        {'value': 'ini', 'name': 'INI file', 'extension': 'ini'},
        {'value': 'java', 'name': 'Java', 'extension': 'java'},
        {'value': 'xml', 'name': 'XML', 'extension': 'xml'},
        {'value': 'xml', 'name': 'XML', 'extension': 'xul'}]

        lang = ''

        if file:
                with open(file, 'r') as f:
                    content =
                # TODO: Use mime-types instead of extensions; suprocess('file <f_name>')
                # Guess File-type based on file extension
                extension = file.split('.')[-1]
                for l in FILE_TYPES:
                    if extension == l['extension']:
                        print('Identified file as %s' % l['name'])
                        lang = l['value']
            except IOError:
                print('ERROR. No such file')
                return 1
            content =
        duration = duration[0]

        if language:
            lang = language

        params = [
            ('parent_pid', ''),
            ('format', lang),
            ('code2', content),
            ('poster', poster),
            ('expiry', duration),
            ('paste', 'Send')]

        data = urllib.urlencode(params)
        print('Uploading ...')
            req = urllib2.Request(URL, data)
            response = urllib2.urlopen(req)
            http_response_code = response.getcode()
            if http_response_code == 200:
                print('Could not upload the file, '
                      'HTTP Response Code %s' %(http_response_code))
        except urllib2.URLError:
            print('ERROR. Could not connect to')
            return 1
        return 0

class FormatProvider(MachCommandBase):
    @Command('clang-format', category='misc',
        description='Run clang-format on current changes')
    @CommandArgument('--show', '-s', action = 'store_true',
        help = 'Show diff output on instead of applying changes')
    def clang_format(self, show=False):
        plat = platform.system()
        fmt = plat.lower() + "/clang-format-3.5"
        fmt_diff = "clang-format-diff-3.5"

        # We are currently using a modified version of clang-format hosted on
        # This is a temporary work around until we upstream the necessary changes and we can use
        # a system version of clang-format. See bug 961541.
        if plat == "Windows":
            fmt += ".exe"
            arch = os.uname()[4]
            if (plat != "Linux" and plat != "Darwin") or arch != 'x86_64':
                print("Unsupported platform " + plat + "/" + arch +
                      ". Supported platforms are Windows/*, Linux/x86_64 and Darwin/x86_64")
                return 1

        self.prompt = True

            if not self.locate_or_fetch(fmt):
                return 1
            clang_format_diff = self.locate_or_fetch(fmt_diff)
            if not clang_format_diff:
                return 1

        except urllib2.HTTPError as e:
            print("HTTP error {0}: {1}".format(e.code, e.reason))
            return 1

        from subprocess import Popen, PIPE

        if os.path.exists(".hg"):
            diff_process = Popen(["hg", "diff", "-U0", "-r", "tip^",
                                  "--include", "glob:**.c", "--include", "glob:**.cpp", "--include", "glob:**.h",
                                  "--exclude", "listfile:.clang-format-ignore"], stdout=PIPE)
            git_process = Popen(["git", "diff", "-U0", "HEAD^"], stdout=PIPE)
                diff_process = Popen(["filterdiff", "--include=*.h", "--include=*.cpp",
                                     stdin=git_process.stdout, stdout=PIPE)
            except OSError as e:
                if e.errno == errno.ENOENT:
                    print("Can't find filterdiff. Please install patchutils.")
                    print("OSError {0}: {1}".format(e.code, e.reason))
                return 1

        args = [sys.executable, clang_format_diff, "-p1"]
        if not show:
        cf_process = Popen(args, stdin=diff_process.stdout)
        return cf_process.communicate()[0]

    def locate_or_fetch(self, root):
        target = os.path.join(self._mach_context.state_dir, os.path.basename(root))
        if not os.path.exists(target):
            site = ""
            if self.prompt and raw_input("Download clang-format executables from {0} (yN)? ".format(site)).lower() != 'y':
                print("Download aborted.")
                return 1
            self.prompt = False

            u = site + root
            print("Downloading {0} to {1}".format(u, target))
            data = urllib2.urlopen(url=u).read()
            temp = target + ".tmp"
            with open(temp, "wb") as fh:
            os.chmod(temp, os.stat(temp).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
            os.rename(temp, target)
        return target