Bug 1260241 - Pre: Add compress option to Jarrer.add. r?glandium draft
authorNick Alexander <nalexander@mozilla.com>
Mon, 28 Mar 2016 19:02:05 -0700
changeset 345374 5be2b60d44e92e4ee38da4ff7b6c624bf635e3ff
parent 345328 4af12748f68724568a088f2cb5d26dba072becd9
child 345375 06cd1288bb2a820790f3082e48f56f39aa435b0f
child 345378 0fc111f68a184f99607cb74660cbfa22403ef913
push id14071
push usernalexander@mozilla.com
push dateTue, 29 Mar 2016 02:33:51 +0000
reviewersglandium
bugs1260241
milestone48.0a1
Bug 1260241 - Pre: Add compress option to Jarrer.add. r?glandium This required handling Deflater instances with different compression options in JarWriter, which in turn required exposing the uncompressed data in Deflater. MozReview-Commit-ID: FWXeBOi9FTY
python/mozbuild/mozpack/copier.py
python/mozbuild/mozpack/mozjar.py
python/mozbuild/mozpack/test/test_copier.py
--- a/python/mozbuild/mozpack/copier.py
+++ b/python/mozbuild/mozpack/copier.py
@@ -479,18 +479,24 @@ class Jarrer(FileRegistry, BaseFile):
     def __init__(self, compress=True, optimize=True):
         '''
         Create a Jarrer instance. See mozpack.mozjar.JarWriter documentation
         for details on the compress and optimize arguments.
         '''
         self.compress = compress
         self.optimize = optimize
         self._preload = []
+        self._compress_options = {} # Map path to compress boolean option.
         FileRegistry.__init__(self)
 
+    def add(self, path, content, compress=None):
+        FileRegistry.add(self, path, content)
+        if compress is not None:
+            self._compress_options[path] = compress
+
     def copy(self, dest, skip_if_older=True):
         '''
         Pack all registered files in the given destination jar. The given
         destination jar may be a path to jar file, or a Dest instance for
         a jar file.
         If the destination jar file exists, its (compressed) contents are used
         instead of the registered BaseFile instances when appropriate.
         '''
@@ -535,22 +541,26 @@ class Jarrer(FileRegistry, BaseFile):
         except Exception:
             old_jar = []
 
         old_contents = dict([(f.filename, f) for f in old_jar])
 
         with JarWriter(fileobj=dest, compress=self.compress,
                        optimize=self.optimize) as jar:
             for path, file in self:
+                compress = self._compress_options.get(path, None)
+                if compress is None:
+                    compress = self.compress
+
                 if path in old_contents:
-                    deflater = DeflaterDest(old_contents[path], self.compress)
+                    deflater = DeflaterDest(old_contents[path], compress)
                 else:
-                    deflater = DeflaterDest(compress=self.compress)
+                    deflater = DeflaterDest(compress=compress)
                 file.copy(deflater, skip_if_older)
-                jar.add(path, deflater.deflater, mode=file.mode)
+                jar.add(path, deflater.deflater, mode=file.mode, compress=compress)
             if self._preload:
                 jar.preload(self._preload)
 
     def open(self):
         raise RuntimeError('unsupported')
 
     def preload(self, paths):
         '''
--- a/python/mozbuild/mozpack/mozjar.py
+++ b/python/mozbuild/mozpack/mozjar.py
@@ -594,16 +594,19 @@ class JarWriter(object):
             compress = self._compress
         if (isinstance(data, JarFileReader) and data.compressed == compress) \
                 or (isinstance(data, Deflater) and data.compress == compress):
             deflater = data
         else:
             deflater = Deflater(compress, compress_level=self._compress_level)
             if isinstance(data, basestring):
                 deflater.write(data)
+            elif isinstance(data, Deflater):
+                # A Deflator, but with a different compression option.
+                deflater.write(data.uncompressed_data)
             elif hasattr(data, 'read'):
                 if hasattr(data, 'seek'):
                     data.seek(0)
                 deflater.write(data.read())
             else:
                 raise JarWriterError("Don't know how to handle %s" %
                                      type(data))
         # Fill a central directory entry for this new member.
@@ -751,16 +754,23 @@ class Deflater(object):
         Return the compressed data, if the data should be compressed (real
         compressed size smaller than the uncompressed size), or the
         uncompressed data otherwise.
         '''
         if self.compressed:
             return self._deflated.getvalue()
         return self._data.getvalue()
 
+    @property
+    def uncompressed_data(self):
+        '''
+        Return the uncompressed data.
+        '''
+        return self._data.getvalue()
+
 
 class JarLog(dict):
     '''
     Helper to read the file Gecko generates when setting MOZ_JAR_LOG_FILE.
     The jar log is then available as a dict with the jar path as key (see
     canonicalize for more details on the key value), and the corresponding
     access log as a list value. Only the first access to a given member of
     a jar is stored.
--- a/python/mozbuild/mozpack/test/test_copier.py
+++ b/python/mozbuild/mozpack/test/test_copier.py
@@ -504,10 +504,26 @@ class TestJarrer(unittest.TestCase):
         copier.copy(dest)
 
         dest.seek(0)
         jar = JarReader(fileobj=dest)
         self.assertEqual([f.filename for f in jar], preloaded +
                          [p for p in copier.paths() if not p in preloaded])
         self.assertEqual(jar.last_preloaded, preloaded[-1])
 
+
+    def test_jarrer_compress(self):
+        copier = Jarrer()
+        copier.add('foo/bar', GeneratedFile('ffffff'))
+        copier.add('foo/qux', GeneratedFile('ffffff'), compress=False)
+
+        dest = MockDest()
+        copier.copy(dest)
+        self.check_jar(dest, copier)
+
+        dest.seek(0)
+        jar = JarReader(fileobj=dest)
+        self.assertTrue(jar['foo/bar'].compressed)
+        self.assertFalse(jar['foo/qux'].compressed)
+        
+
 if __name__ == '__main__':
     mozunit.main()