Bug 893976 - Add a mach command to update uuids for specific interfaces and their descendants. r=gps
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 30 Jul 2013 08:57:28 +0900
changeset 140482 9251bba8a9b2d99137551659c214ac17b5dee1cc
parent 140481 03f984a3bd2a4d07ce5751c78fceabaf9008e58c
child 140483 f92a6dcf7361edd3ef7ea7d18c82cf2140bc81cc
push id25030
push userryanvm@gmail.com
push dateTue, 30 Jul 2013 17:07:39 +0000
treeherdermozilla-central@129ce98f4cb2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs893976
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 893976 - Add a mach command to update uuids for specific interfaces and their descendants. r=gps
build/mach_bootstrap.py
tools/mach_commands.py
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -45,16 +45,17 @@ SEARCH_PATHS = [
     'testing/mozbase/mozfile',
     'testing/mozbase/mozhttpd',
     'testing/mozbase/mozlog',
     'testing/mozbase/moznetwork',
     'testing/mozbase/mozprocess',
     'testing/mozbase/mozprofile',
     'testing/mozbase/mozrunner',
     'testing/mozbase/mozinfo',
+    'xpcom/idl-parser',
 ]
 
 # Individual files providing mach commands.
 MACH_MODULES = [
     'addon-sdk/mach_commands.py',
     'layout/tools/reftest/mach_commands.py',
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
--- a/tools/mach_commands.py
+++ b/tools/mach_commands.py
@@ -55,24 +55,140 @@ class SearchProvider(object):
         'MDN, and MXR.')
     @CommandArgument('term', nargs='+', help='Term(s) to search for.')
     def search(self, term):
         self.google(term)
         self.mdn(term)
         self.mxr(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.name = production.name
+        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(interface.name, [])
+        l.append(interface)
+        l = self.by_base.setdefault(interface.base, [])
+        l.append(interface)
+
+
+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):
+            self._add(interface)
+
+    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(interface.name):
+            self._add(derived)
+
+    def update(self):
+        for p, f in self.registry:
+            f.copy(p)
+
+
 @CommandProvider
 class UUIDProvider(object):
     @Command('uuid', category='misc',
         description='Generate a uuid.')
     @CommandArgument('--format', '-f', choices=['idl', 'cpp'], default='idl',
                      help='Output format for the generated uuid.')
     def uuid(self, format):
         import uuid
         u = uuid.uuid4()
         if format == 'idl':
             print(u)
         else:
             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()
+        try:
+            parser = xpidl.IDLParser(outputdir=tmpdir)
+            registry = InterfaceRegistry()
+            for p, f in finder.find('**/*.idl'):
+                p = mozpack.path.join(path, p)
+                try:
+                    content = f.open().read()
+                    idl = parser.parse(content, filename=p)
+                except Exception:
+                    continue
+                for prod in idl.productions:
+                    if isinstance(prod, xpidl.Interface):
+                         registry.add(Interface(p, prod))
+        finally:
+            import shutil
+            shutil.rmtree(tmpdir)
+
+        updates = IDLUpdater(registry)
+
+        for interface in interfaces:
+            updates.add(interface)
+
+        updates.update()