Bug 910660 - Make package formatters handle addons. r=gps, a=sledru
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 13 Mar 2015 12:12:03 +0900
changeset 258295 90925932c88c
parent 258294 06cd1b5deb25
child 258296 f4f4979e09b2
push id4638
push userryanvm@gmail.com
push date2015-04-06 20:41 +0000
treeherdermozilla-beta@499e1c563939 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps, sledru
bugs910660
milestone38.0
Bug 910660 - Make package formatters handle addons. r=gps, a=sledru Addons are always formatted with jars for omnijar and jar packaging, and flat for flat packaging.
python/mozbuild/mozpack/packager/formats.py
python/mozbuild/mozpack/test/test_packager_formats.py
--- a/python/mozbuild/mozpack/packager/formats.py
+++ b/python/mozbuild/mozpack/packager/formats.py
@@ -34,20 +34,22 @@ formats:
     - flat: essentially, copies files from the source with the same file system
       layout. Manifests entries are grouped in a single manifest per directory,
       as well as XPT interfaces.
     - jar: chrome content is packaged in jar files.
     - omni: chrome content, modules, non-binary components, and many other
       elements are packaged in an omnijar file for each base directory.
 
 The base interface provides the following methods:
-    - add_base(path)
-        Register a base directory for an application or GRE. Base directories
-        usually contain a root manifest (manifests not included in any other
-        manifest) named chrome.manifest.
+    - add_base(path [, addon])
+        Register a base directory for an application or GRE, or an addon.
+        Base directories usually contain a root manifest (manifests not
+        included in any other manifest) named chrome.manifest.
+        The optional boolean addon argument tells whether the base directory
+        is that of an addon.
     - add(path, content)
         Add the given content (BaseFile instance) at the given virtual path
     - add_interfaces(path, content)
         Add the given content (BaseFile instance) and link it to other
         interfaces in the parent directory of the given virtual path.
     - add_manifest(entry)
         Add a ManifestEntry.
     - contains(path)
@@ -64,23 +66,26 @@ data.
 class FlatFormatter(object):
     '''
     Formatter for the flat package format.
     '''
     def __init__(self, copier):
         assert isinstance(copier, FileRegistry)
         self.copier = copier
         self._bases = ['']
+        self._addons = []
         self._frozen_bases = False
 
-    def add_base(self, base):
+    def add_base(self, base, addon=False):
         # Only allow to add a base directory before calls to _get_base()
         assert not self._frozen_bases
         if not base in self._bases:
             self._bases.append(base)
+        if addon and base not in self._addons:
+            self._addons.append(base)
 
     def _get_base(self, path):
         '''
         Return the deepest base directory containing the given path.
         '''
         self._frozen_bases = True
         return mozpack.path.basedir(path, self._bases)
 
@@ -194,87 +199,89 @@ class JarFormatter(FlatFormatter):
         if not chrome:
             return self.copier.contains(path)
         if not self.copier.contains(chrome + '.jar'):
             return False
         return self.copier[chrome + '.jar']. \
             contains(mozpack.path.relpath(path, chrome))
 
 
-class OmniJarFormatter(FlatFormatter):
+class OmniJarFormatter(JarFormatter):
     '''
     Formatter for the omnijar package format.
     '''
     def __init__(self, copier, omnijar_name, compress=True, optimize=True,
                  non_resources=[]):
-        FlatFormatter.__init__(self, copier)
+        JarFormatter.__init__(self, copier, compress, optimize)
         self.omnijars = {}
         self._omnijar_name = omnijar_name
-        self._compress = compress
-        self._optimize = optimize
         self._non_resources = non_resources
 
-    def _get_omnijar(self, path, create=True):
+    def _get_formatter(self, path, is_resource=None):
         '''
-        Return the omnijar corresponding to the given path, its base directory
-        and the path translated to be under the omnijar..
+        Return the (sub)formatter corresponding to the given path, its base
+        directory and the path relative to that base.
         '''
         base = self._get_base(path)
+        use_omnijar = base not in self._addons
+        if use_omnijar:
+            if is_resource is None:
+                is_resource = self.is_resource(path, base)
+            use_omnijar = is_resource
+        if not use_omnijar:
+            return super(OmniJarFormatter, self), '', path
         if not base in self.omnijars:
-            if not create:
-                return None, '', path
             omnijar = Jarrer(self._compress, self._optimize)
             self.omnijars[base] = FlatFormatter(omnijar)
             self.copier.add(mozpack.path.join(base, self._omnijar_name),
                             omnijar)
         return self.omnijars[base], base, mozpack.path.relpath(path, base)
 
     def add(self, path, content):
-        if self.is_resource(path):
-            formatter, base, path = self._get_omnijar(path)
-        else:
-            formatter = self
-        FlatFormatter.add(formatter, path, content)
+        formatter, base, path = self._get_formatter(path)
+        formatter.add(path, content)
 
     def add_manifest(self, entry):
         if isinstance(entry, ManifestBinaryComponent):
-            formatter, base = self, ''
+            formatter, base = super(OmniJarFormatter, self), ''
         else:
-            formatter, base, path = self._get_omnijar(entry.base)
+            formatter, base, path = self._get_formatter(entry.base,
+                                                        is_resource=True)
         entry = entry.move(mozpack.path.relpath(entry.base, base))
-        FlatFormatter.add_manifest(formatter, entry)
+        formatter.add_manifest(entry)
 
     def add_interfaces(self, path, content):
-        formatter, base, path = self._get_omnijar(path)
-        FlatFormatter.add_interfaces(formatter, path, content)
+        formatter, base, path = self._get_formatter(path)
+        formatter.add_interfaces(path, content)
 
     def contains(self, path):
         assert '*' not in path
         if self.copier.contains(path):
             return True
         for base, copier in self.omnijars.iteritems():
             if copier.contains(mozpack.path.relpath(path, base)):
                 return True
         return False
 
-    def is_resource(self, path):
+    def is_resource(self, path, base=None):
         '''
         Return whether the given path corresponds to a resource to be put in an
         omnijar archive.
         '''
-        base = self._get_base(path)
+        if base is None:
+            base = self._get_base(path)
         path = mozpack.path.relpath(path, base)
         if any(mozpack.path.match(path, p.replace('*', '**'))
                for p in self._non_resources):
             return False
         path = mozpack.path.split(path)
         if path[0] == 'chrome':
             return len(path) == 1 or path[1] != 'icons'
         if path[0] == 'components':
-            return path[-1].endswith('.js')
+            return path[-1].endswith(('.js', '.xpt'))
         if path[0] == 'res':
             return len(path) == 1 or \
                 (path[1] != 'cursors' and path[1] != 'MainMenu.nib')
         if path[0] == 'defaults':
             return len(path) != 3 or \
                 not (path[2] == 'channel-prefs.js' and
                      path[1] in ['pref', 'preferences'])
         return path[0] in [
--- a/python/mozbuild/mozpack/test/test_packager_formats.py
+++ b/python/mozbuild/mozpack/test/test_packager_formats.py
@@ -18,53 +18,60 @@ from mozpack.chrome.manifest import (
     ManifestContent,
     ManifestComponent,
     ManifestResource,
     ManifestBinaryComponent,
 )
 from mozpack.test.test_files import (
     MockDest,
     foo_xpt,
+    foo2_xpt,
     bar_xpt,
     read_interfaces,
 )
 
 
 CONTENTS = {
-    'bases': [
-        'app',
-    ],
+    'bases': {
+        # base_path: is_addon?
+        'app': False,
+        'addon0': True,
+    },
     'manifests': [
         ManifestContent('chrome/f', 'oo', 'oo/'),
         ManifestContent('chrome/f', 'bar', 'oo/bar/'),
         ManifestResource('chrome/f', 'foo', 'resource://bar/'),
         ManifestBinaryComponent('components', 'foo.so'),
         ManifestContent('app/chrome', 'content', 'foo/'),
         ManifestComponent('app/components', '{foo-id}', 'foo.js'),
+        ManifestContent('addon0/chrome', 'content', 'foo/bar/'),
     ],
     'chrome/f/oo/bar/baz': GeneratedFile('foobarbaz'),
     'chrome/f/oo/baz': GeneratedFile('foobaz'),
     'chrome/f/oo/qux': GeneratedFile('fooqux'),
     'components/foo.so': GeneratedFile('foo.so'),
     'components/foo.xpt': foo_xpt,
     'components/bar.xpt': bar_xpt,
     'foo': GeneratedFile('foo'),
     'app/chrome/foo/foo': GeneratedFile('appfoo'),
     'app/components/foo.js': GeneratedFile('foo.js'),
+    'addon0/chrome/foo/bar/baz': GeneratedFile('foobarbaz'),
+    'addon0/components/foo.xpt': foo2_xpt,
+    'addon0/components/bar.xpt': bar_xpt,
 }
 
 
 class MockDest(MockDest):
     def exists(self):
         return False
 
 
 def fill_formatter(formatter, contents):
-    for base in contents['bases']:
-        formatter.add_base(base)
+    for base, is_addon in contents['bases'].items():
+        formatter.add_base(base, is_addon)
 
     for manifest in contents['manifests']:
         formatter.add_manifest(manifest)
 
     for k, v in contents.iteritems():
         if k in ('bases', 'manifests'):
             continue
         if k.endswith('.xpt'):
@@ -92,21 +99,24 @@ def get_contents(registry, read_all=Fals
 class TestFormatters(unittest.TestCase):
     maxDiff = None
 
     def test_bases(self):
         formatter = FlatFormatter(FileRegistry())
         formatter.add_base('')
         formatter.add_base('browser')
         formatter.add_base('webapprt')
+        formatter.add_base('addon0', addon=True)
         self.assertEqual(formatter._get_base('platform.ini'), '')
         self.assertEqual(formatter._get_base('browser/application.ini'),
                          'browser')
         self.assertEqual(formatter._get_base('webapprt/webapprt.ini'),
                          'webapprt')
+        self.assertEqual(formatter._get_base('addon0/install.rdf'),
+                         'addon0')
 
     def test_flat_formatter(self):
         registry = FileRegistry()
         formatter = FlatFormatter(registry)
 
         fill_formatter(formatter, CONTENTS)
 
         RESULT = {
@@ -142,16 +152,31 @@ class TestFormatters(unittest.TestCase):
             'app/chrome/chrome.manifest': [
                 'content content foo/',
             ],
             'app/chrome/foo/foo': CONTENTS['app/chrome/foo/foo'],
             'app/components/components.manifest': [
                 'component {foo-id} foo.js',
             ],
             'app/components/foo.js': CONTENTS['app/components/foo.js'],
+            'addon0/chrome.manifest': [
+                'manifest chrome/chrome.manifest',
+                'manifest components/components.manifest',
+            ],
+            'addon0/chrome/chrome.manifest': [
+                'content content foo/bar/',
+            ],
+            'addon0/chrome/foo/bar/baz': CONTENTS['addon0/chrome/foo/bar/baz'],
+            'addon0/components/components.manifest': [
+                'interfaces interfaces.xpt',
+            ],
+            'addon0/components/interfaces.xpt': {
+                'foo': read_interfaces(foo2_xpt.open())['foo'],
+                'bar': read_interfaces(bar_xpt.open())['bar'],
+            },
         }
 
         self.assertEqual(get_contents(registry), RESULT)
 
     def test_jar_formatter(self):
         registry = FileRegistry()
         formatter = JarFormatter(registry)
 
@@ -194,16 +219,33 @@ class TestFormatters(unittest.TestCase):
             ],
             'app/chrome/foo.jar': {
                 'foo': CONTENTS['app/chrome/foo/foo'],
             },
             'app/components/components.manifest': [
                 'component {foo-id} foo.js',
             ],
             'app/components/foo.js': CONTENTS['app/components/foo.js'],
+            'addon0/chrome.manifest': [
+                'manifest chrome/chrome.manifest',
+                'manifest components/components.manifest',
+            ],
+            'addon0/chrome/chrome.manifest': [
+                'content content jar:foo.jar!/bar/',
+            ],
+            'addon0/chrome/foo.jar': {
+                'bar/baz': CONTENTS['addon0/chrome/foo/bar/baz'],
+            },
+            'addon0/components/components.manifest': [
+                'interfaces interfaces.xpt',
+            ],
+            'addon0/components/interfaces.xpt': {
+                'foo': read_interfaces(foo2_xpt.open())['foo'],
+                'bar': read_interfaces(bar_xpt.open())['bar'],
+            },
         }
 
         self.assertEqual(get_contents(registry), RESULT)
 
     def test_omnijar_formatter(self):
         registry = FileRegistry()
         formatter = OmniJarFormatter(registry, 'omni.foo')
 
@@ -251,16 +293,33 @@ class TestFormatters(unittest.TestCase):
                     'content content foo/',
                 ],
                 'chrome/foo/foo': CONTENTS['app/chrome/foo/foo'],
                 'components/components.manifest': [
                     'component {foo-id} foo.js',
                 ],
                 'components/foo.js': CONTENTS['app/components/foo.js'],
             },
+            'addon0/chrome.manifest': [
+                'manifest chrome/chrome.manifest',
+                'manifest components/components.manifest',
+            ],
+            'addon0/chrome/chrome.manifest': [
+                'content content jar:foo.jar!/bar/',
+            ],
+            'addon0/chrome/foo.jar': {
+                'bar/baz': CONTENTS['addon0/chrome/foo/bar/baz'],
+            },
+            'addon0/components/components.manifest': [
+                'interfaces interfaces.xpt',
+            ],
+            'addon0/components/interfaces.xpt': {
+                'foo': read_interfaces(foo2_xpt.open())['foo'],
+                'bar': read_interfaces(bar_xpt.open())['bar'],
+            },
         }
         self.assertEqual(get_contents(registry), RESULT)
 
     def test_omnijar_is_resource(self):
         registry = FileRegistry()
         f = OmniJarFormatter(registry, 'omni.foo', non_resources=[
             'defaults/messenger/mailViews.dat',
             'defaults/foo/*',