Bug 1542963 - Insert unicode_literals into python/mozbuild/mozpack and called mozbuild/* files. r=#build
☠☠ backed out by ee4b88439111 ☠ ☠
authorJustin Wood <Callek@gmail.com>
Mon, 08 Apr 2019 15:20:45 -0400
changeset 532997 5f3c10562df335827c80fd866a1eba8f256540d2
parent 532996 70fbe1a158ae2cc7a6730ba47a04b426ab803f0a
child 532998 b4e024474194a21d9ade565abfd8340e93aed07c
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1542963
milestone68.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 1542963 - Insert unicode_literals into python/mozbuild/mozpack and called mozbuild/* files. r=#build Lint python/mozbuild/{mozbuild,mozpack}. This makes sure we use byte strings (type(b'')) in many places. This also has a few places where we know we want unicode, by enforcing it. This code also has a few places where we call into windows API's which expect either all args to be unicode, or all args to be bytestrings (not-unicode) so make sure those calls align. This is the most risky of the stack, since there are some mach commands and codepaths that are neither excercised in automation nor excercised in tests. Differential Revision: https://phabricator.services.mozilla.com/D26645
python/mozbuild/mozbuild/jar.py
python/mozbuild/mozbuild/repackaging/mar.py
python/mozbuild/mozbuild/util.py
python/mozbuild/mozpack/archive.py
python/mozbuild/mozpack/chrome/flags.py
python/mozbuild/mozpack/chrome/manifest.py
python/mozbuild/mozpack/copier.py
python/mozbuild/mozpack/dmg.py
python/mozbuild/mozpack/errors.py
python/mozbuild/mozpack/executables.py
python/mozbuild/mozpack/files.py
python/mozbuild/mozpack/hg.py
python/mozbuild/mozpack/mozjar.py
python/mozbuild/mozpack/packager/__init__.py
python/mozbuild/mozpack/packager/formats.py
python/mozbuild/mozpack/packager/l10n.py
python/mozbuild/mozpack/packager/unpack.py
python/mozbuild/mozpack/path.py
python/mozbuild/mozpack/test/support/minify_js_verify.py
python/mozbuild/mozpack/test/test_archive.py
python/mozbuild/mozpack/test/test_chrome_flags.py
python/mozbuild/mozpack/test/test_chrome_manifest.py
python/mozbuild/mozpack/test/test_copier.py
python/mozbuild/mozpack/test/test_errors.py
python/mozbuild/mozpack/test/test_files.py
python/mozbuild/mozpack/test/test_mozjar.py
python/mozbuild/mozpack/test/test_packager.py
python/mozbuild/mozpack/test/test_packager_formats.py
python/mozbuild/mozpack/test/test_packager_l10n.py
python/mozbuild/mozpack/test/test_packager_unpack.py
python/mozbuild/mozpack/test/test_path.py
--- a/python/mozbuild/mozbuild/jar.py
+++ b/python/mozbuild/mozbuild/jar.py
@@ -16,16 +16,17 @@ import errno
 import re
 import logging
 from time import localtime
 from MozZipFile import ZipFile
 from cStringIO import StringIO
 
 from mozbuild.preprocessor import Preprocessor
 from mozbuild.action.buildlist import addEntriesToListFile
+from mozbuild.util import ensure_bytes
 from mozpack.files import FileFinder
 import mozpack.path as mozpath
 if sys.platform == 'win32':
     from ctypes import windll, WinError
     CreateHardLink = windll.kernel32.CreateHardLinkA
 
 __all__ = ['JarMaker']
 
@@ -555,17 +556,17 @@ class JarMaker(object):
                 os.remove(out)
             except OSError as e:
                 if e.errno != errno.ENOENT:
                     raise
             if sys.platform != 'win32':
                 os.symlink(src, out)
             else:
                 # On Win32, use ctypes to create a hardlink
-                rv = CreateHardLink(out, src, None)
+                rv = CreateHardLink(ensure_bytes(out), ensure_bytes(src), None)
                 if rv == 0:
                     raise WinError()
 
 
 def main(args=None):
     args = args or sys.argv
     jm = JarMaker()
     p = jm.getCommandLineParser()
--- a/python/mozbuild/mozbuild/repackaging/mar.py
+++ b/python/mozbuild/mozbuild/repackaging/mar.py
@@ -8,17 +8,20 @@ import os
 import sys
 import tempfile
 import shutil
 import zipfile
 import tarfile
 import subprocess
 import mozpack.path as mozpath
 from mozbuild.repackaging.application_ini import get_application_ini_value
-from mozbuild.util import ensureParentDir
+from mozbuild.util import (
+    ensureParentDir,
+    ensure_bytes,
+)
 
 
 _BCJ_OPTIONS = {
     'x86': ['--x86'],
     'x86_64': ['--x86'],
     'aarch64': [],
 }
 
@@ -70,12 +73,14 @@ def repackage_mar(topsrcdir, package, ma
         if os.path.exists(xz_path):
             env['XZ'] = mozpath.normpath(xz_path)
 
         cmd = [make_full_update, output, ffxdir]
         if sys.platform == 'win32':
             # make_full_update.sh is a bash script, and Windows needs to
             # explicitly call out the shell to execute the script from Python.
             cmd.insert(0, env['MOZILLABUILD'] + '/msys/bin/bash.exe')
+        # in py2 env needs str not unicode.
+        env = {ensure_bytes(k): ensure_bytes(v) for k, v in env.iteritems()}
         subprocess.check_call(cmd, env=env)
 
     finally:
         shutil.rmtree(tmpdir)
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -1388,8 +1388,20 @@ def patch_main():
             fork_string = ("main_file_name = '%s'\n" % main_file_name +
                            "main_module_name = '%s'\n" % main_module_name +
                            ''.join(x[12:] for x in fork_code[1:]))
             cmdline = orig_command_line()
             cmdline[2] = fork_string
             return cmdline
         orig_command_line = forking.get_command_line
         forking.get_command_line = my_get_command_line
+
+
+def ensure_bytes(value):
+    if isinstance(value, basestring):
+        return value.encode('utf8')
+    return value
+
+
+def ensure_unicode(value):
+    if isinstance(value, type(b'')):
+        return value.decode('utf8')
+    return value
--- a/python/mozbuild/mozpack/archive.py
+++ b/python/mozbuild/mozpack/archive.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import bz2
 import gzip
 import stat
 import tarfile
 
 from .files import (
     BaseFile, File,
--- a/python/mozbuild/mozpack/chrome/flags.py
+++ b/python/mozbuild/mozpack/chrome/flags.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import re
 from distutils.version import LooseVersion
 from mozpack.errors import errors
 from collections import OrderedDict
 
 
 class Flag(object):
--- a/python/mozbuild/mozpack/chrome/manifest.py
+++ b/python/mozbuild/mozpack/chrome/manifest.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import re
 import os
 
 try:
     from urlparse import urlparse
 except ImportError:
     from urllib.parse import urlparse
--- a/python/mozbuild/mozpack/copier.py
+++ b/python/mozbuild/mozpack/copier.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import stat
 import sys
 
 from mozpack.errors import errors
 from mozpack.files import (
     BaseFile,
--- a/python/mozbuild/mozpack/dmg.py
+++ b/python/mozbuild/mozpack/dmg.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import buildconfig
 import errno
 import mozfile
 import os
 import platform
 import shutil
 import subprocess
--- a/python/mozbuild/mozpack/errors.py
+++ b/python/mozbuild/mozpack/errors.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import sys
 from contextlib import contextmanager
 
 
 class ErrorMessage(Exception):
     '''Exception type raised from errors.error() and errors.fatal()'''
 
--- a/python/mozbuild/mozpack/executables.py
+++ b/python/mozbuild/mozpack/executables.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import struct
 import subprocess
 from mozpack.errors import errors
 
 MACHO_SIGNATURES = [
     0xfeedface,  # mach-o 32-bits big endian
--- a/python/mozbuild/mozpack/files.py
+++ b/python/mozbuild/mozpack/files.py
@@ -1,26 +1,30 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import errno
 import inspect
 import os
 import platform
 import shutil
 import stat
 import subprocess
 import uuid
 import mozbuild.makeutil as makeutil
 from itertools import chain
 from mozbuild.preprocessor import Preprocessor
-from mozbuild.util import FileAvoidWrite
+from mozbuild.util import (
+    FileAvoidWrite,
+    ensure_bytes,
+    ensure_unicode,
+)
 from mozpack.executables import (
     is_executable,
     may_strip,
     strip,
     may_elfhack,
     elfhack,
     xz_compress,
 )
@@ -78,17 +82,17 @@ class Dest(object):
       underlying file.
     - a call to read() after a write() will re-open the underlying file and
       read from it.
     - a call to write() after a read() will re-open the underlying file,
       emptying it, and write to it.
     '''
 
     def __init__(self, path):
-        self.path = path
+        self.path = ensure_unicode(path)
         self.mode = None
 
     @property
     def name(self):
         return self.path
 
     def read(self, length=-1):
         if self.mode != 'r':
@@ -188,22 +192,22 @@ class BaseFile(object):
 
         if can_skip_content_check:
             if getattr(self, 'path', None) and getattr(dest, 'path', None):
                 _copyfile(self.path, dest.path)
                 shutil.copystat(self.path, dest.path)
             else:
                 # Ensure the file is always created
                 if not dest.exists():
-                    dest.write('')
+                    dest.write(b'')
                 shutil.copyfileobj(self.open(), dest)
             return True
 
         src = self.open()
-        copy_content = ''
+        copy_content = b''
         while True:
             dest_content = dest.read(32768)
             src_content = src.read(32768)
             copy_content += src_content
             if len(dest_content) == len(src_content) == 0:
                 break
             # If the read content differs between origin and destination,
             # write what was read up to now, and copy the remainder.
@@ -250,17 +254,17 @@ class BaseFile(object):
 
 
 class File(BaseFile):
     '''
     File class for plain files.
     '''
 
     def __init__(self, path):
-        self.path = path
+        self.path = ensure_unicode(path)
 
     @property
     def mode(self):
         '''
         Return the file's unix mode, as returned by os.stat().st_mode.
         '''
         if platform.system() == 'Windows':
             return None
@@ -507,18 +511,18 @@ class ExistingFile(BaseFile):
 class PreprocessedFile(BaseFile):
     '''
     File class for a file that is preprocessed. PreprocessedFile.copy() runs
     the preprocessor on the file to create the output.
     '''
 
     def __init__(self, path, depfile_path, marker, defines, extra_depends=None,
                  silence_missing_directive_warnings=False):
-        self.path = path
-        self.depfile = depfile_path
+        self.path = ensure_unicode(path)
+        self.depfile = ensure_unicode(depfile_path)
         self.marker = marker
         self.defines = defines
         self.extra_depends = list(extra_depends or [])
         self.silence_missing_directive_warnings = \
             silence_missing_directive_warnings
 
     def inputs(self):
         pp = Preprocessor(defines=self.defines, marker=self.marker)
@@ -701,19 +705,22 @@ class ManifestFile(BaseFile):
         else:
             self._entries.remove(entry)
 
     def open(self):
         '''
         Return a file-like object allowing to read() the serialized content of
         the manifest.
         '''
-        return BytesIO(''.join('%s\n' % e.rebase(self._base)
-                               for e in chain(self._entries,
-                                              self._interfaces)))
+        return BytesIO(
+            ensure_bytes(
+                ''.join(
+                    '%s\n' % e.rebase(self._base)
+                    for e in chain(self._entries, self._interfaces)
+                )))
 
     def __iter__(self):
         '''
         Iterate over entries in the manifest file.
         '''
         return chain(self._entries, self._interfaces)
 
     def isempty(self):
@@ -733,18 +740,18 @@ class MinifiedProperties(BaseFile):
         assert isinstance(file, BaseFile)
         self._file = file
 
     def open(self):
         '''
         Return a file-like object allowing to read() the minified content of
         the properties file.
         '''
-        return BytesIO(''.join(l for l in self._file.open().readlines()
-                               if not l.startswith('#')))
+        return BytesIO(b''.join(l for l in self._file.open().readlines()
+                                if not l.startswith(b'#')))
 
 
 class MinifiedJavaScript(BaseFile):
     '''
     File class for minifying JavaScript files.
     '''
 
     def __init__(self, file, verify_command=None):
--- a/python/mozbuild/mozpack/hg.py
+++ b/python/mozbuild/mozpack/hg.py
@@ -22,17 +22,17 @@
 # modification of the file, and distribution when not combined with
 # mozbuild.)
 #
 # If you modify this code, you may extend this exception to your
 # version of the code, but you are not obliged to do so. If you
 # do not wish to do so, delete this exception statement from your
 # version.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import mercurial.error as error
 import mercurial.hg as hg
 import mercurial.ui as hgui
 
 from .files import (
     BaseFinder,
     MercurialFile,
--- a/python/mozbuild/mozpack/mozjar.py
+++ b/python/mozbuild/mozpack/mozjar.py
@@ -1,26 +1,29 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from io import BytesIO
 import struct
 import subprocess
 import zlib
 import os
 from zipfile import (
     ZIP_STORED,
     ZIP_DEFLATED,
 )
 from collections import OrderedDict
 import mozpack.path as mozpath
-from mozbuild.util import memoize
+from mozbuild.util import (
+    memoize,
+    ensure_bytes,
+)
 
 
 JAR_STORED = ZIP_STORED
 JAR_DEFLATED = ZIP_DEFLATED
 JAR_BROTLI = 0x81
 MAX_WBITS = 15
 
 
@@ -124,36 +127,36 @@ class JarStruct(object):
         JarStruct.TYPE_MAPPING) at the given offset in the given data.
         '''
         assert type in JarStruct.TYPE_MAPPING
         assert data is not None
         format, size = JarStruct.TYPE_MAPPING[type]
         data = data[:size]
         if isinstance(data, memoryview):
             data = data.tobytes()
-        return struct.unpack('<' + format, data)[0], size
+        return struct.unpack(b'<' + format, data)[0], size
 
     def serialize(self):
         '''
         Serialize the data structure according to the data structure definition
         from self.STRUCT.
         '''
-        serialized = struct.pack('<I', self.signature)
+        serialized = struct.pack(b'<I', self.signature)
         sizes = dict((t, name) for name, t in self.STRUCT.iteritems()
                      if t not in JarStruct.TYPE_MAPPING)
         for name, t in self.STRUCT.iteritems():
             if t in JarStruct.TYPE_MAPPING:
                 format, size = JarStruct.TYPE_MAPPING[t]
                 if name in sizes:
                     value = len(self[sizes[name]])
                 else:
                     value = self[name]
-                serialized += struct.pack('<' + format, value)
+                serialized += struct.pack(b'<' + format, value)
             else:
-                serialized += self[name]
+                serialized += ensure_bytes(self[name])
         return serialized
 
     @property
     def size(self):
         '''
         Return the size of the data structure, given the current values of all
         variable length fields.
         '''
--- a/python/mozbuild/mozpack/packager/__init__.py
+++ b/python/mozbuild/mozpack/packager/__init__.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozbuild.preprocessor import Preprocessor
 import re
 import os
 from mozpack.errors import errors
 from mozpack.chrome.manifest import (
     Manifest,
     ManifestBinaryComponent,
--- a/python/mozbuild/mozpack/packager/formats.py
+++ b/python/mozbuild/mozpack/packager/formats.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozpack.chrome.manifest import (
     Manifest,
     ManifestInterfaces,
     ManifestChrome,
     ManifestBinaryComponent,
     ManifestResource,
     ManifestMultiContent,
--- a/python/mozbuild/mozpack/packager/l10n.py
+++ b/python/mozbuild/mozpack/packager/l10n.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 '''
 Replace localized parts of a packaged directory with data from a langpack
 directory.
 '''
 
 import json
 import os
--- a/python/mozbuild/mozpack/packager/unpack.py
+++ b/python/mozbuild/mozpack/packager/unpack.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import mozpack.path as mozpath
 from mozpack.files import (
     BaseFinder,
     FileFinder,
     DeflatedFile,
     ManifestFile,
 )
--- a/python/mozbuild/mozpack/path.py
+++ b/python/mozbuild/mozpack/path.py
@@ -3,17 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 '''
 Like :py:mod:`os.path`, with a reduced set of functions, and with normalized path
 separators (always use forward slashes).
 Also contains a few additional utilities not found in :py:mod:`os.path`.
 '''
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import ctypes
 import posixpath
 import os
 import re
 import sys
 
 
--- a/python/mozbuild/mozpack/test/support/minify_js_verify.py
+++ b/python/mozbuild/mozpack/test/support/minify_js_verify.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 import sys
 
 
 if len(sys.argv) != 4:
     raise Exception('Usage: minify_js_verify <exitcode> <orig> <minified>')
 
 retcode = int(sys.argv[1])
 
--- a/python/mozbuild/mozpack/test/test_archive.py
+++ b/python/mozbuild/mozpack/test/test_archive.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import hashlib
 import os
 import shutil
 import stat
 import tarfile
 import tempfile
 import unittest
--- a/python/mozbuild/mozpack/test/test_chrome_flags.py
+++ b/python/mozbuild/mozpack/test/test_chrome_flags.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 import mozunit
 from mozpack.chrome.flags import (
     Flag,
     StringFlag,
     VersionFlag,
     Flags,
--- a/python/mozbuild/mozpack/test/test_chrome_manifest.py
+++ b/python/mozbuild/mozpack/test/test_chrome_manifest.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 import mozunit
 import os
 from mozpack.chrome.manifest import (
     ManifestContent,
     ManifestLocale,
     ManifestSkin,
--- a/python/mozbuild/mozpack/test/test_copier.py
+++ b/python/mozbuild/mozpack/test/test_copier.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozpack.copier import (
     FileCopier,
     FileRegistry,
     FileRegistrySubtree,
     Jarrer,
 )
 from mozpack.files import (
@@ -37,31 +37,31 @@ class BaseTestFileRegistry(MatchTestTemp
         if result:
             self.assertTrue(self.registry.contains(pattern))
         else:
             self.assertFalse(self.registry.contains(pattern))
         self.assertEqual(self.registry.match(pattern), result)
 
     def do_test_file_registry(self, registry):
         self.registry = registry
-        self.registry.add('foo', GeneratedFile('foo'))
-        bar = GeneratedFile('bar')
+        self.registry.add('foo', GeneratedFile(b'foo'))
+        bar = GeneratedFile(b'bar')
         self.registry.add('bar', bar)
         self.assertEqual(self.registry.paths(), ['foo', 'bar'])
         self.assertEqual(self.registry['bar'], bar)
 
         self.assertRaises(ErrorMessage, self.registry.add, 'foo',
-                          GeneratedFile('foo2'))
+                          GeneratedFile(b'foo2'))
 
         self.assertRaises(ErrorMessage, self.registry.remove, 'qux')
 
         self.assertRaises(ErrorMessage, self.registry.add, 'foo/bar',
-                          GeneratedFile('foobar'))
+                          GeneratedFile(b'foobar'))
         self.assertRaises(ErrorMessage, self.registry.add, 'foo/bar/baz',
-                          GeneratedFile('foobar'))
+                          GeneratedFile(b'foobar'))
 
         self.assertEqual(self.registry.paths(), ['foo', 'bar'])
 
         self.registry.remove('foo')
         self.assertEqual(self.registry.paths(), ['bar'])
         self.registry.remove('bar')
         self.assertEqual(self.registry.paths(), [])
 
@@ -76,55 +76,55 @@ class BaseTestFileRegistry(MatchTestTemp
             'foo/qux/bar',
             'foo/qux/2/test',
             'foo/qux/2/test2',
         ])
 
         self.registry.remove('foo/qux')
         self.assertEqual(self.registry.paths(), ['bar', 'foo/bar', 'foo/baz'])
 
-        self.registry.add('foo/qux', GeneratedFile('fooqux'))
+        self.registry.add('foo/qux', GeneratedFile(b'fooqux'))
         self.assertEqual(self.registry.paths(), ['bar', 'foo/bar', 'foo/baz',
                                                  'foo/qux'])
         self.registry.remove('foo/b*')
         self.assertEqual(self.registry.paths(), ['bar', 'foo/qux'])
 
         self.assertEqual([f for f, c in self.registry], ['bar', 'foo/qux'])
         self.assertEqual(len(self.registry), 2)
 
         self.add('foo/.foo')
         self.assertTrue(self.registry.contains('foo/.foo'))
 
     def do_test_registry_paths(self, registry):
         self.registry = registry
 
         # Can't add a file if it requires a directory in place of a
         # file we also require.
-        self.registry.add('foo', GeneratedFile('foo'))
+        self.registry.add('foo', GeneratedFile(b'foo'))
         self.assertRaises(ErrorMessage, self.registry.add, 'foo/bar',
-                          GeneratedFile('foobar'))
+                          GeneratedFile(b'foobar'))
 
         # Can't add a file if we already have a directory there.
-        self.registry.add('bar/baz', GeneratedFile('barbaz'))
+        self.registry.add('bar/baz', GeneratedFile(b'barbaz'))
         self.assertRaises(ErrorMessage, self.registry.add, 'bar',
-                          GeneratedFile('bar'))
+                          GeneratedFile(b'bar'))
 
         # Bump the count of things that require bar/ to 2.
-        self.registry.add('bar/zot', GeneratedFile('barzot'))
+        self.registry.add('bar/zot', GeneratedFile(b'barzot'))
         self.assertRaises(ErrorMessage, self.registry.add, 'bar',
-                          GeneratedFile('bar'))
+                          GeneratedFile(b'bar'))
 
         # Drop the count of things that require bar/ to 1.
         self.registry.remove('bar/baz')
         self.assertRaises(ErrorMessage, self.registry.add, 'bar',
-                          GeneratedFile('bar'))
+                          GeneratedFile(b'bar'))
 
         # Drop the count of things that require bar/ to 0.
         self.registry.remove('bar/zot')
-        self.registry.add('bar/zot', GeneratedFile('barzot'))
+        self.registry.add('bar/zot', GeneratedFile(b'barzot'))
 
 
 class TestFileRegistry(BaseTestFileRegistry, unittest.TestCase):
     def test_partial_paths(self):
         cases = {
             'foo/bar/baz/zot': ['foo/bar/baz', 'foo/bar', 'foo'],
             'foo/bar': ['foo'],
             'bar': [],
@@ -137,51 +137,51 @@ class TestFileRegistry(BaseTestFileRegis
         self.do_test_file_registry(FileRegistry())
 
     def test_registry_paths(self):
         self.do_test_registry_paths(FileRegistry())
 
     def test_required_directories(self):
         self.registry = FileRegistry()
 
-        self.registry.add('foo', GeneratedFile('foo'))
+        self.registry.add('foo', GeneratedFile(b'foo'))
         self.assertEqual(self.registry.required_directories(), set())
 
-        self.registry.add('bar/baz', GeneratedFile('barbaz'))
+        self.registry.add('bar/baz', GeneratedFile(b'barbaz'))
         self.assertEqual(self.registry.required_directories(), {'bar'})
 
-        self.registry.add('bar/zot', GeneratedFile('barzot'))
+        self.registry.add('bar/zot', GeneratedFile(b'barzot'))
         self.assertEqual(self.registry.required_directories(), {'bar'})
 
-        self.registry.add('bar/zap/zot', GeneratedFile('barzapzot'))
+        self.registry.add('bar/zap/zot', GeneratedFile(b'barzapzot'))
         self.assertEqual(self.registry.required_directories(), {'bar', 'bar/zap'})
 
         self.registry.remove('bar/zap/zot')
         self.assertEqual(self.registry.required_directories(), {'bar'})
 
         self.registry.remove('bar/baz')
         self.assertEqual(self.registry.required_directories(), {'bar'})
 
         self.registry.remove('bar/zot')
         self.assertEqual(self.registry.required_directories(), set())
 
-        self.registry.add('x/y/z', GeneratedFile('xyz'))
+        self.registry.add('x/y/z', GeneratedFile(b'xyz'))
         self.assertEqual(self.registry.required_directories(), {'x', 'x/y'})
 
 
 class TestFileRegistrySubtree(BaseTestFileRegistry, unittest.TestCase):
     def test_file_registry_subtree_base(self):
         registry = FileRegistry()
         self.assertEqual(registry, FileRegistrySubtree('', registry))
         self.assertNotEqual(registry, FileRegistrySubtree('base', registry))
 
     def create_registry(self):
         registry = FileRegistry()
-        registry.add('foo/bar', GeneratedFile('foo/bar'))
-        registry.add('baz/qux', GeneratedFile('baz/qux'))
+        registry.add('foo/bar', GeneratedFile(b'foo/bar'))
+        registry.add('baz/qux', GeneratedFile(b'baz/qux'))
         return FileRegistrySubtree('base/root', registry)
 
     def test_file_registry_subtree(self):
         self.do_test_file_registry(self.create_registry())
 
     def test_registry_paths_subtree(self):
         FileRegistry()
         self.do_test_registry_paths(self.create_registry())
@@ -200,53 +200,53 @@ class TestFileCopier(TestWithTmpDir):
         for root, dirs, files in os.walk(base):
             for f in files:
                 all_files.add(
                     mozpath.join(mozpath.relpath(root, base), f))
         return all_files
 
     def test_file_copier(self):
         copier = FileCopier()
-        copier.add('foo/bar', GeneratedFile('foobar'))
-        copier.add('foo/qux', GeneratedFile('fooqux'))
-        copier.add('foo/deep/nested/directory/file', GeneratedFile('fooz'))
-        copier.add('bar', GeneratedFile('bar'))
-        copier.add('qux/foo', GeneratedFile('quxfoo'))
-        copier.add('qux/bar', GeneratedFile(''))
+        copier.add('foo/bar', GeneratedFile(b'foobar'))
+        copier.add('foo/qux', GeneratedFile(b'fooqux'))
+        copier.add('foo/deep/nested/directory/file', GeneratedFile(b'fooz'))
+        copier.add('bar', GeneratedFile(b'bar'))
+        copier.add('qux/foo', GeneratedFile(b'quxfoo'))
+        copier.add('qux/bar', GeneratedFile(b''))
 
         result = copier.copy(self.tmpdir)
         self.assertEqual(self.all_files(self.tmpdir), set(copier.paths()))
         self.assertEqual(self.all_dirs(self.tmpdir),
                          set(['foo/deep/nested/directory', 'qux']))
 
         self.assertEqual(result.updated_files, set(self.tmppath(p) for p in
                                                    self.all_files(self.tmpdir)))
         self.assertEqual(result.existing_files, set())
         self.assertEqual(result.removed_files, set())
         self.assertEqual(result.removed_directories, set())
 
         copier.remove('foo')
-        copier.add('test', GeneratedFile('test'))
+        copier.add('test', GeneratedFile(b'test'))
         result = copier.copy(self.tmpdir)
         self.assertEqual(self.all_files(self.tmpdir), set(copier.paths()))
         self.assertEqual(self.all_dirs(self.tmpdir), set(['qux']))
         self.assertEqual(result.removed_files, set(self.tmppath(p) for p in
                                                    ('foo/bar', 'foo/qux',
                                                     'foo/deep/nested/directory/file')))
 
     def test_symlink_directory_replaced(self):
         """Directory symlinks in destination are replaced if they need to be
         real directories."""
         if not self.symlink_supported:
             return
 
         dest = self.tmppath('dest')
 
         copier = FileCopier()
-        copier.add('foo/bar/baz', GeneratedFile('foobarbaz'))
+        copier.add('foo/bar/baz', GeneratedFile(b'foobarbaz'))
 
         os.makedirs(self.tmppath('dest/foo'))
         dummy = self.tmppath('dummy')
         os.mkdir(dummy)
         link = self.tmppath('dest/foo/bar')
         os.symlink(dummy, link)
 
         result = copier.copy(dest)
@@ -266,17 +266,17 @@ class TestFileCopier(TestWithTmpDir):
         remove_all_directory_symlinks.
         """
         if not self.symlink_supported:
             return
 
         dest = self.tmppath('dest')
 
         copier = FileCopier()
-        copier.add('foo/bar/baz', GeneratedFile('foobarbaz'))
+        copier.add('foo/bar/baz', GeneratedFile(b'foobarbaz'))
 
         os.makedirs(self.tmppath('dest/foo'))
         dummy = self.tmppath('dummy')
         os.mkdir(dummy)
 
         os.mkdir(self.tmppath('dest/zot'))
         link = self.tmppath('dest/zot/zap')
         os.symlink(dummy, link)
@@ -340,24 +340,24 @@ class TestFileCopier(TestWithTmpDir):
 
         # Make file and directory unwritable. Reminder: making a directory
         # unwritable prevents modifications (including deletes) from the list
         # of files in that directory.
         os.chmod(p, 0o400)
         os.chmod(self.tmpdir, 0o400)
 
         copier = FileCopier()
-        copier.add('dummy', GeneratedFile('content'))
+        copier.add('dummy', GeneratedFile(b'content'))
         result = copier.copy(self.tmpdir)
         self.assertEqual(result.removed_files_count, 1)
         self.assertFalse(os.path.exists(p))
 
     def test_no_remove(self):
         copier = FileCopier()
-        copier.add('foo', GeneratedFile('foo'))
+        copier.add('foo', GeneratedFile(b'foo'))
 
         with open(self.tmppath('bar'), 'a'):
             pass
 
         os.mkdir(self.tmppath('emptydir'))
         d = self.tmppath('populateddir')
         os.mkdir(d)
 
@@ -370,17 +370,17 @@ class TestFileCopier(TestWithTmpDir):
                                                            'populateddir/foo']))
         self.assertEqual(self.all_dirs(self.tmpdir), set(['populateddir']))
         self.assertEqual(result.removed_files, set())
         self.assertEqual(result.removed_directories,
                          set([self.tmppath('emptydir')]))
 
     def test_no_remove_empty_directories(self):
         copier = FileCopier()
-        copier.add('foo', GeneratedFile('foo'))
+        copier.add('foo', GeneratedFile(b'foo'))
 
         with open(self.tmppath('bar'), 'a'):
             pass
 
         os.mkdir(self.tmppath('emptydir'))
         d = self.tmppath('populateddir')
         os.mkdir(d)
 
@@ -425,37 +425,37 @@ class TestFileCopier(TestWithTmpDir):
         self.assertIn(self.tmppath('dest/foo/bar'), result.existing_files)
 
     def test_remove_unaccounted_file_registry(self):
         """Test FileCopier.copy(remove_unaccounted=FileRegistry())"""
 
         dest = self.tmppath('dest')
 
         copier = FileCopier()
-        copier.add('foo/bar/baz', GeneratedFile('foobarbaz'))
-        copier.add('foo/bar/qux', GeneratedFile('foobarqux'))
-        copier.add('foo/hoge/fuga', GeneratedFile('foohogefuga'))
-        copier.add('foo/toto/tata', GeneratedFile('footototata'))
+        copier.add('foo/bar/baz', GeneratedFile(b'foobarbaz'))
+        copier.add('foo/bar/qux', GeneratedFile(b'foobarqux'))
+        copier.add('foo/hoge/fuga', GeneratedFile(b'foohogefuga'))
+        copier.add('foo/toto/tata', GeneratedFile(b'footototata'))
 
         os.makedirs(os.path.join(dest, 'bar'))
         with open(os.path.join(dest, 'bar', 'bar'), 'w') as fh:
             fh.write('barbar')
         os.makedirs(os.path.join(dest, 'foo', 'toto'))
         with open(os.path.join(dest, 'foo', 'toto', 'toto'), 'w') as fh:
             fh.write('foototototo')
 
         result = copier.copy(dest, remove_unaccounted=False)
 
         self.assertEqual(self.all_files(dest),
                          set(copier.paths()) | {'foo/toto/toto', 'bar/bar'})
         self.assertEqual(self.all_dirs(dest),
                          {'foo/bar', 'foo/hoge', 'foo/toto', 'bar'})
 
         copier2 = FileCopier()
-        copier2.add('foo/hoge/fuga', GeneratedFile('foohogefuga'))
+        copier2.add('foo/hoge/fuga', GeneratedFile(b'foohogefuga'))
 
         # We expect only files copied from the first copier to be removed,
         # not the extra file that was there beforehand.
         result = copier2.copy(dest, remove_unaccounted=copier)
 
         self.assertEqual(self.all_files(dest),
                          set(copier2.paths()) | {'foo/toto/toto', 'bar/bar'})
         self.assertEqual(self.all_dirs(dest),
@@ -475,34 +475,34 @@ class TestJarrer(unittest.TestCase):
         jar = JarReader(fileobj=dest)
         self.assertEqual([f.filename for f in jar], copier.paths())
         for f in jar:
             self.assertEqual(f.uncompressed_data.read(),
                              copier[f.filename].content)
 
     def test_jarrer(self):
         copier = Jarrer()
-        copier.add('foo/bar', GeneratedFile('foobar'))
-        copier.add('foo/qux', GeneratedFile('fooqux'))
-        copier.add('foo/deep/nested/directory/file', GeneratedFile('fooz'))
-        copier.add('bar', GeneratedFile('bar'))
-        copier.add('qux/foo', GeneratedFile('quxfoo'))
-        copier.add('qux/bar', GeneratedFile(''))
+        copier.add('foo/bar', GeneratedFile(b'foobar'))
+        copier.add('foo/qux', GeneratedFile(b'fooqux'))
+        copier.add('foo/deep/nested/directory/file', GeneratedFile(b'fooz'))
+        copier.add('bar', GeneratedFile(b'bar'))
+        copier.add('qux/foo', GeneratedFile(b'quxfoo'))
+        copier.add('qux/bar', GeneratedFile(b''))
 
         dest = MockDest()
         copier.copy(dest)
         self.check_jar(dest, copier)
 
         copier.remove('foo')
-        copier.add('test', GeneratedFile('test'))
+        copier.add('test', GeneratedFile(b'test'))
         copier.copy(dest)
         self.check_jar(dest, copier)
 
         copier.remove('test')
-        copier.add('test', GeneratedFile('replaced-content'))
+        copier.add('test', GeneratedFile(b'replaced-content'))
         copier.copy(dest)
         self.check_jar(dest, copier)
 
         copier.copy(dest)
         self.check_jar(dest, copier)
 
         preloaded = ['qux/bar', 'bar']
         copier.preload(preloaded)
@@ -511,18 +511,18 @@ class TestJarrer(unittest.TestCase):
         dest.seek(0)
         jar = JarReader(fileobj=dest)
         self.assertEqual([f.filename for f in jar], preloaded +
                          [p for p in copier.paths() if p not 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)
+        copier.add('foo/bar', GeneratedFile(b'ffffff'))
+        copier.add('foo/qux', GeneratedFile(b'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)
--- a/python/mozbuild/mozpack/test/test_errors.py
+++ b/python/mozbuild/mozpack/test/test_errors.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozpack.errors import (
     errors,
     ErrorMessage,
     AccumulatedErrors,
 )
 import unittest
 import mozunit
--- a/python/mozbuild/mozpack/test/test_files.py
+++ b/python/mozbuild/mozpack/test/test_files.py
@@ -1,15 +1,18 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
-from mozbuild.util import ensureParentDir
+from mozbuild.util import (
+    ensureParentDir,
+    ensure_bytes,
+)
 
 from mozpack.errors import (
     ErrorMessage,
     errors,
 )
 from mozpack.files import (
     AbsoluteSymlinkFile,
     ComposedFinder,
@@ -52,17 +55,16 @@ from mozpack.chrome.manifest import (
     ManifestLocale,
     ManifestOverride,
 )
 import unittest
 import mozfile
 import mozunit
 import os
 import random
-import string
 import sys
 import tarfile
 import mozpack.path as mozpath
 from tempfile import mkdtemp
 from io import BytesIO
 from StringIO import StringIO
 
 
@@ -156,28 +158,30 @@ class TestDest(TestWithTmpDir):
         dest.close()
         self.assertEqual(dest.read(), 'bar')
         dest.write('foo')
         dest.close()
         dest.write('qux')
         self.assertEqual(dest.read(), 'qux')
 
 
-rand = ''.join(random.choice(string.letters) for i in xrange(131597))
+rand = b''.join(random.choice(
+    b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+                for i in xrange(131597))
 samples = [
-    '',
-    'test',
-    'fooo',
-    'same',
-    'same',
-    'Different and longer',
+    b'',
+    b'test',
+    b'fooo',
+    b'same',
+    b'same',
+    b'Different and longer',
     rand,
     rand,
-    rand[:-1] + '_',
-    'test'
+    rand[:-1] + b'_',
+    b'test',
 ]
 
 
 class TestFile(TestWithTmpDir):
     def test_file(self):
         '''
         Check that File.copy yields the proper content in the destination file
         in all situations that trigger different code paths:
@@ -624,147 +628,148 @@ class TestGeneratedFile(TestWithTmpDir):
             f.copy(dest)
             self.assertEqual(content, open(dest, 'rb').read())
 
     def test_generated_file_open(self):
         '''
         Test whether GeneratedFile.open returns an appropriately reset file
         object.
         '''
-        content = ''.join(samples)
+        content = b''.join(samples)
         f = GeneratedFile(content)
         self.assertEqual(content[:42], f.open().read(42))
         self.assertEqual(content, f.open().read())
 
     def test_generated_file_no_write(self):
         '''
         Test various conditions where GeneratedFile.copy is expected not to
         write in the destination file.
         '''
         dest = self.tmppath('dest')
 
         # Initial copy
-        f = GeneratedFile('test')
+        f = GeneratedFile(b'test')
         f.copy(dest)
 
         # Ensure subsequent copies won't trigger writes
         f.copy(DestNoWrite(dest))
-        self.assertEqual('test', open(dest, 'rb').read())
+        self.assertEqual(b'test', open(dest, 'rb').read())
 
         # When using a new instance with the same content, no copy should occur
-        f = GeneratedFile('test')
+        f = GeneratedFile(b'test')
         f.copy(DestNoWrite(dest))
-        self.assertEqual('test', open(dest, 'rb').read())
+        self.assertEqual(b'test', open(dest, 'rb').read())
 
         # Double check that under conditions where a copy occurs, we would get
         # an exception.
-        f = GeneratedFile('fooo')
+        f = GeneratedFile(b'fooo')
         self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
 
     def test_generated_file_function(self):
         '''
         Test GeneratedFile behavior with functions.
         '''
         dest = self.tmppath('dest')
         data = {
             'num_calls': 0,
         }
 
         def content():
             data['num_calls'] += 1
-            return 'content'
+            return b'content'
 
         f = GeneratedFile(content)
         self.assertEqual(data['num_calls'], 0)
         f.copy(dest)
         self.assertEqual(data['num_calls'], 1)
-        self.assertEqual('content', open(dest, 'rb').read())
-        self.assertEqual('content', f.open().read())
-        self.assertEqual('content', f.read())
-        self.assertEqual(len('content'), f.size())
+        self.assertEqual(b'content', open(dest, 'rb').read())
+        self.assertEqual(b'content', f.open().read())
+        self.assertEqual(b'content', f.read())
+        self.assertEqual(len(b'content'), f.size())
         self.assertEqual(data['num_calls'], 1)
 
-        f.content = 'modified'
+        f.content = b'modified'
         f.copy(dest)
         self.assertEqual(data['num_calls'], 1)
-        self.assertEqual('modified', open(dest, 'rb').read())
-        self.assertEqual('modified', f.open().read())
-        self.assertEqual('modified', f.read())
-        self.assertEqual(len('modified'), f.size())
+        self.assertEqual(b'modified', open(dest, 'rb').read())
+        self.assertEqual(b'modified', f.open().read())
+        self.assertEqual(b'modified', f.read())
+        self.assertEqual(len(b'modified'), f.size())
 
         f.content = content
         self.assertEqual(data['num_calls'], 1)
-        self.assertEqual('content', f.read())
+        self.assertEqual(b'content', f.read())
         self.assertEqual(data['num_calls'], 2)
 
 
 class TestDeflatedFile(TestWithTmpDir):
     def test_deflated_file(self):
         '''
         Check that DeflatedFile.copy yields the proper content in the
         destination file in all situations that trigger different code paths
         (see TestFile.test_file)
         '''
         src = self.tmppath('src.jar')
         dest = self.tmppath('dest')
 
         contents = {}
         with JarWriter(src) as jar:
             for content in samples:
-                name = ''.join(random.choice(string.letters)
-                               for i in xrange(8))
+                name = b''.join(random.choice(
+                    b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+                    for i in xrange(8))
                 jar.add(name, content, compress=True)
                 contents[name] = content
 
         for j in JarReader(src):
             f = DeflatedFile(j)
             f.copy(dest)
             self.assertEqual(contents[j.filename], open(dest, 'rb').read())
 
     def test_deflated_file_open(self):
         '''
         Test whether DeflatedFile.open returns an appropriately reset file
         object.
         '''
         src = self.tmppath('src.jar')
-        content = ''.join(samples)
+        content = b''.join(samples)
         with JarWriter(src) as jar:
             jar.add('content', content)
 
         f = DeflatedFile(JarReader(src)['content'])
         self.assertEqual(content[:42], f.open().read(42))
         self.assertEqual(content, f.open().read())
 
     def test_deflated_file_no_write(self):
         '''
         Test various conditions where DeflatedFile.copy is expected not to
         write in the destination file.
         '''
         src = self.tmppath('src.jar')
         dest = self.tmppath('dest')
 
         with JarWriter(src) as jar:
-            jar.add('test', 'test')
-            jar.add('test2', 'test')
-            jar.add('fooo', 'fooo')
+            jar.add('test', b'test')
+            jar.add('test2', b'test')
+            jar.add('fooo', b'fooo')
 
         jar = JarReader(src)
         # Initial copy
         f = DeflatedFile(jar['test'])
         f.copy(dest)
 
         # Ensure subsequent copies won't trigger writes
         f.copy(DestNoWrite(dest))
-        self.assertEqual('test', open(dest, 'rb').read())
+        self.assertEqual(b'test', open(dest, 'rb').read())
 
         # When using a different file with the same content, no copy should
         # occur
         f = DeflatedFile(jar['test2'])
         f.copy(DestNoWrite(dest))
-        self.assertEqual('test', open(dest, 'rb').read())
+        self.assertEqual(b'test', open(dest, 'rb').read())
 
         # Double check that under conditions where a copy occurs, we would get
         # an exception.
         f = DeflatedFile(jar['fooo'])
         self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
 
 
 class TestManifestFile(TestWithTmpDir):
@@ -860,67 +865,67 @@ foo2_xpt = GeneratedFile(
     b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
     b'\x05\x00\x80\x06\x00\x00\x00'
 )
 
 
 class TestMinifiedProperties(TestWithTmpDir):
     def test_minified_properties(self):
         propLines = [
-            '# Comments are removed',
-            'foo = bar',
-            '',
-            '# Another comment',
+            b'# Comments are removed',
+            b'foo = bar',
+            b'',
+            b'# Another comment',
         ]
-        prop = GeneratedFile('\n'.join(propLines))
+        prop = GeneratedFile(b'\n'.join(propLines))
         self.assertEqual(MinifiedProperties(prop).open().readlines(),
                          ['foo = bar\n', '\n'])
         open(self.tmppath('prop'), 'wb').write('\n'.join(propLines))
         MinifiedProperties(File(self.tmppath('prop'))) \
             .copy(self.tmppath('prop2'))
         self.assertEqual(open(self.tmppath('prop2')).readlines(),
                          ['foo = bar\n', '\n'])
 
 
 class TestMinifiedJavaScript(TestWithTmpDir):
     orig_lines = [
-        '// Comment line',
-        'let foo = "bar";',
-        'var bar = true;',
-        '',
-        '// Another comment',
+        b'// Comment line',
+        b'let foo = "bar";',
+        b'var bar = true;',
+        b'',
+        b'// Another comment',
     ]
 
     def test_minified_javascript(self):
-        orig_f = GeneratedFile('\n'.join(self.orig_lines))
+        orig_f = GeneratedFile(b'\n'.join(self.orig_lines))
         min_f = MinifiedJavaScript(orig_f)
 
         mini_lines = min_f.open().readlines()
         self.assertTrue(mini_lines)
         self.assertTrue(len(mini_lines) < len(self.orig_lines))
 
     def _verify_command(self, code):
         our_dir = os.path.abspath(os.path.dirname(__file__))
         return [
             sys.executable,
             os.path.join(our_dir, 'support', 'minify_js_verify.py'),
             code,
         ]
 
     def test_minified_verify_success(self):
-        orig_f = GeneratedFile('\n'.join(self.orig_lines))
+        orig_f = GeneratedFile(b'\n'.join(self.orig_lines))
         min_f = MinifiedJavaScript(orig_f,
                                    verify_command=self._verify_command('0'))
 
         mini_lines = min_f.open().readlines()
         self.assertTrue(mini_lines)
         self.assertTrue(len(mini_lines) < len(self.orig_lines))
 
     def test_minified_verify_failure(self):
-        orig_f = GeneratedFile('\n'.join(self.orig_lines))
+        orig_f = GeneratedFile(b'\n'.join(self.orig_lines))
         errors.out = StringIO()
         min_f = MinifiedJavaScript(orig_f,
                                    verify_command=self._verify_command('1'))
 
         mini_lines = min_f.open().readlines()
         output = errors.out.getvalue()
         errors.out = sys.stderr
         self.assertEqual(output,
@@ -1092,17 +1097,17 @@ class TestFileFinder(MatchTestTemplate, 
         self.finder = FileFinder(self.tmpdir, find_dotfiles=True,
                                  ignore=['foo/.bar/**'])
         self.do_check('foo/**', ['foo/.foo', 'foo/bar', 'foo/baz',
                                  'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'])
 
 
 class TestJarFinder(MatchTestTemplate, TestWithTmpDir):
     def add(self, path):
-        self.jar.add(path, path, compress=True)
+        self.jar.add(path, ensure_bytes(path), compress=True)
 
     def do_check(self, pattern, result):
         do_check(self, self.finder, pattern, result)
 
     def test_jar_finder(self):
         self.jar = JarWriter(file=self.tmppath('test.jar'))
         self.prepare_match_test()
         self.jar.finish()
@@ -1185,20 +1190,21 @@ class TestMercurialRevisionFinder(MatchT
                 client.close()
 
         self._clients[:] = []
 
         super(TestMercurialRevisionFinder, self).tearDown()
 
     def _client(self):
         configs = (
-            'ui.username="Dummy User <dummy@example.com>"',
+            # b'' because py2 needs !unicode
+            b'ui.username="Dummy User <dummy@example.com>"',
         )
-
-        client = hglib.open(self.tmpdir, encoding='UTF-8',
+        client = hglib.open(self.tmpdir,
+                            encoding=b'UTF-8',  # b'' because py2 needs !unicode
                             configs=configs)
         self._clients.append(client)
         return client
 
     def add(self, path):
         with self._client() as c:
             ensureParentDir(self.tmppath(path))
             with open(self.tmppath(path), 'wb') as fh:
--- a/python/mozbuild/mozpack/test/test_mozjar.py
+++ b/python/mozbuild/mozpack/test/test_mozjar.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozpack.files import FileFinder
 from mozpack.mozjar import (
     JarReaderError,
     JarWriterError,
     JarStruct,
     JarReader,
     JarWriter,
@@ -90,44 +90,44 @@ class TestJarStruct(unittest.TestCase):
 
 
 class TestDeflater(unittest.TestCase):
     def wrap(self, data):
         return data
 
     def test_deflater_no_compress(self):
         deflater = Deflater(False)
-        deflater.write(self.wrap('abc'))
+        deflater.write(self.wrap(b'abc'))
         self.assertFalse(deflater.compressed)
         self.assertEqual(deflater.uncompressed_size, 3)
         self.assertEqual(deflater.compressed_size, deflater.uncompressed_size)
-        self.assertEqual(deflater.compressed_data, 'abc')
+        self.assertEqual(deflater.compressed_data, b'abc')
         self.assertEqual(deflater.crc32, 0x352441c2)
 
     def test_deflater_compress_no_gain(self):
         deflater = Deflater(True)
-        deflater.write(self.wrap('abc'))
+        deflater.write(self.wrap(b'abc'))
         self.assertFalse(deflater.compressed)
         self.assertEqual(deflater.uncompressed_size, 3)
         self.assertEqual(deflater.compressed_size, deflater.uncompressed_size)
-        self.assertEqual(deflater.compressed_data, 'abc')
+        self.assertEqual(deflater.compressed_data, b'abc')
         self.assertEqual(deflater.crc32, 0x352441c2)
 
     def test_deflater_compress(self):
         deflater = Deflater(True)
-        deflater.write(self.wrap('aaaaaaaaaaaaanopqrstuvwxyz'))
+        deflater.write(self.wrap(b'aaaaaaaaaaaaanopqrstuvwxyz'))
         self.assertTrue(deflater.compressed)
         self.assertEqual(deflater.uncompressed_size, 26)
         self.assertNotEqual(deflater.compressed_size,
                             deflater.uncompressed_size)
         self.assertEqual(deflater.crc32, 0xd46b97ed)
         # The CRC is the same as when not compressed
         deflater = Deflater(False)
         self.assertFalse(deflater.compressed)
-        deflater.write(self.wrap('aaaaaaaaaaaaanopqrstuvwxyz'))
+        deflater.write(self.wrap(b'aaaaaaaaaaaaanopqrstuvwxyz'))
         self.assertEqual(deflater.crc32, 0xd46b97ed)
 
     def test_deflater_empty(self):
         deflater = Deflater(False)
         self.assertFalse(deflater.compressed)
         self.assertEqual(deflater.uncompressed_size, 0)
         self.assertEqual(deflater.compressed_size, deflater.uncompressed_size)
         self.assertEqual(deflater.compressed_data, '')
@@ -138,63 +138,63 @@ class TestDeflaterMemoryView(TestDeflate
     def wrap(self, data):
         return memoryview(data)
 
 
 class TestJar(unittest.TestCase):
     def test_jar(self):
         s = MockDest()
         with JarWriter(fileobj=s) as jar:
-            jar.add('foo', 'foo')
-            self.assertRaises(JarWriterError, jar.add, 'foo', 'bar')
-            jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz')
-            jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', False)
-            jar.add('baz\\backslash', 'aaaaaaaaaaaaaaa')
+            jar.add('foo', b'foo')
+            self.assertRaises(JarWriterError, jar.add, 'foo', b'bar')
+            jar.add('bar', b'aaaaaaaaaaaaanopqrstuvwxyz')
+            jar.add('baz/qux', b'aaaaaaaaaaaaanopqrstuvwxyz', False)
+            jar.add('baz\\backslash', b'aaaaaaaaaaaaaaa')
 
         files = [j for j in JarReader(fileobj=s)]
 
         self.assertEqual(files[0].filename, 'foo')
         self.assertFalse(files[0].compressed)
-        self.assertEqual(files[0].read(), 'foo')
+        self.assertEqual(files[0].read(), b'foo')
 
         self.assertEqual(files[1].filename, 'bar')
         self.assertTrue(files[1].compressed)
-        self.assertEqual(files[1].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[1].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         self.assertEqual(files[2].filename, 'baz/qux')
         self.assertFalse(files[2].compressed)
-        self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[2].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         if os.sep == '\\':
             self.assertEqual(files[3].filename, 'baz/backslash',
                              'backslashes in filenames on Windows should get normalized')
         else:
             self.assertEqual(files[3].filename, 'baz\\backslash',
                              'backslashes in filenames on POSIX platform are untouched')
 
         s = MockDest()
         with JarWriter(fileobj=s, compress=False) as jar:
-            jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz')
-            jar.add('foo', 'foo')
-            jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', True)
+            jar.add('bar', b'aaaaaaaaaaaaanopqrstuvwxyz')
+            jar.add('foo', b'foo')
+            jar.add('baz/qux', b'aaaaaaaaaaaaanopqrstuvwxyz', True)
 
         jar = JarReader(fileobj=s)
         files = [j for j in jar]
 
         self.assertEqual(files[0].filename, 'bar')
         self.assertFalse(files[0].compressed)
-        self.assertEqual(files[0].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[0].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         self.assertEqual(files[1].filename, 'foo')
         self.assertFalse(files[1].compressed)
-        self.assertEqual(files[1].read(), 'foo')
+        self.assertEqual(files[1].read(), b'foo')
 
         self.assertEqual(files[2].filename, 'baz/qux')
         self.assertTrue(files[2].compressed)
-        self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[2].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         self.assertTrue('bar' in jar)
         self.assertTrue('foo' in jar)
         self.assertFalse('baz' in jar)
         self.assertTrue('baz/qux' in jar)
         self.assertTrue(jar['bar'], files[1])
         self.assertTrue(jar['foo'], files[0])
         self.assertTrue(jar['baz/qux'], files[2])
@@ -219,74 +219,74 @@ class TestJar(unittest.TestCase):
         files[2].seek(0)
         self.assertEqual(jar['baz/qux'].filename, files[2].filename)
         self.assertEqual(jar['baz/qux'].compressed, files[2].compressed)
         self.assertEqual(jar['baz/qux'].read(), files[2].read())
 
     def test_rejar(self):
         s = MockDest()
         with JarWriter(fileobj=s) as jar:
-            jar.add('foo', 'foo')
-            jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz')
-            jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', False)
+            jar.add('foo', b'foo')
+            jar.add('bar', b'aaaaaaaaaaaaanopqrstuvwxyz')
+            jar.add('baz/qux', b'aaaaaaaaaaaaanopqrstuvwxyz', False)
 
         new = MockDest()
         with JarWriter(fileobj=new) as jar:
             for j in JarReader(fileobj=s):
                 jar.add(j.filename, j)
 
         jar = JarReader(fileobj=new)
         files = [j for j in jar]
 
         self.assertEqual(files[0].filename, 'foo')
         self.assertFalse(files[0].compressed)
-        self.assertEqual(files[0].read(), 'foo')
+        self.assertEqual(files[0].read(), b'foo')
 
         self.assertEqual(files[1].filename, 'bar')
         self.assertTrue(files[1].compressed)
-        self.assertEqual(files[1].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[1].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         self.assertEqual(files[2].filename, 'baz/qux')
         self.assertTrue(files[2].compressed)
-        self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz')
+        self.assertEqual(files[2].read(), b'aaaaaaaaaaaaanopqrstuvwxyz')
 
     def test_add_from_finder(self):
         s = MockDest()
         with JarWriter(fileobj=s) as jar:
             finder = FileFinder(test_data_path)
             for p, f in finder.find('test_data'):
                 jar.add('test_data', f)
 
         jar = JarReader(fileobj=s)
         files = [j for j in jar]
 
         self.assertEqual(files[0].filename, 'test_data')
         self.assertFalse(files[0].compressed)
-        self.assertEqual(files[0].read(), 'test_data')
+        self.assertEqual(files[0].read(), b'test_data')
 
 
 class TestPreload(unittest.TestCase):
     def test_preload(self):
         s = MockDest()
         with JarWriter(fileobj=s) as jar:
-            jar.add('foo', 'foo')
-            jar.add('bar', 'abcdefghijklmnopqrstuvwxyz')
-            jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz')
+            jar.add('foo', b'foo')
+            jar.add('bar', b'abcdefghijklmnopqrstuvwxyz')
+            jar.add('baz/qux', b'aaaaaaaaaaaaanopqrstuvwxyz')
 
         jar = JarReader(fileobj=s)
         self.assertEqual(jar.last_preloaded, None)
 
         with JarWriter(fileobj=s) as jar:
-            jar.add('foo', 'foo')
-            jar.add('bar', 'abcdefghijklmnopqrstuvwxyz')
-            jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz')
-            jar.preload(['baz/qux', 'bar'])
+            jar.add('foo', b'foo')
+            jar.add('bar', b'abcdefghijklmnopqrstuvwxyz')
+            jar.add('baz/qux', b'aaaaaaaaaaaaanopqrstuvwxyz')
+            jar.preload(['baz/qux', b'bar'])
 
         jar = JarReader(fileobj=s)
-        self.assertEqual(jar.last_preloaded, 'bar')
+        self.assertEqual(jar.last_preloaded, b'bar')
         files = [j for j in jar]
 
         self.assertEqual(files[0].filename, 'baz/qux')
         self.assertEqual(files[1].filename, 'bar')
         self.assertEqual(files[2].filename, 'foo')
 
 
 class TestJarLog(unittest.TestCase):
--- a/python/mozbuild/mozpack/test/test_packager.py
+++ b/python/mozbuild/mozpack/test/test_packager.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 import mozunit
 import os
 from buildconfig import topobjdir
 from mozpack.packager import (
     preprocess_manifest,
     CallDeque,
@@ -142,116 +142,116 @@ class TestSimplePackager(unittest.TestCa
                 GeneratedFile.__init__(self, content)
                 self.path = path
 
         formatter = MockFormatter()
         packager = SimplePackager(formatter)
         curdir = os.path.abspath(os.curdir)
         file = GeneratedFileWithPath(os.path.join(curdir, 'foo',
                                                   'bar.manifest'),
-                                     'resource bar bar/\ncontent bar bar/')
+                                     b'resource bar bar/\ncontent bar bar/')
         with errors.context('manifest', 1):
             packager.add('foo/bar.manifest', file)
 
         file = GeneratedFileWithPath(os.path.join(curdir, 'foo',
                                                   'baz.manifest'),
-                                     'resource baz baz/')
+                                     b'resource baz baz/')
         with errors.context('manifest', 2):
             packager.add('bar/baz.manifest', file)
 
         with errors.context('manifest', 3):
             packager.add('qux/qux.manifest',
-                         GeneratedFile(''.join([
-                            'resource qux qux/\n',
-                            'binary-component qux.so\n',
+                         GeneratedFile(b''.join([
+                            b'resource qux qux/\n',
+                            b'binary-component qux.so\n',
                          ])))
-        bar_xpt = GeneratedFile('bar.xpt')
-        qux_xpt = GeneratedFile('qux.xpt')
-        foo_html = GeneratedFile('foo_html')
-        bar_html = GeneratedFile('bar_html')
+        bar_xpt = GeneratedFile(b'bar.xpt')
+        qux_xpt = GeneratedFile(b'qux.xpt')
+        foo_html = GeneratedFile(b'foo_html')
+        bar_html = GeneratedFile(b'bar_html')
         with errors.context('manifest', 4):
             packager.add('foo/bar.xpt', bar_xpt)
         with errors.context('manifest', 5):
             packager.add('foo/bar/foo.html', foo_html)
             packager.add('foo/bar/bar.html', bar_html)
 
         file = GeneratedFileWithPath(os.path.join(curdir, 'foo.manifest'),
-                                     ''.join([
-                                         'manifest foo/bar.manifest\n',
-                                         'manifest bar/baz.manifest\n',
+                                     b''.join([
+                                         b'manifest foo/bar.manifest\n',
+                                         b'manifest bar/baz.manifest\n',
                                      ]))
         with errors.context('manifest', 6):
             packager.add('foo.manifest', file)
         with errors.context('manifest', 7):
             packager.add('foo/qux.xpt', qux_xpt)
 
         file = GeneratedFileWithPath(os.path.join(curdir, 'addon',
                                                   'chrome.manifest'),
-                                     'resource hoge hoge/')
+                                     b'resource hoge hoge/')
         with errors.context('manifest', 8):
             packager.add('addon/chrome.manifest', file)
 
-        install_rdf = GeneratedFile('<RDF></RDF>')
+        install_rdf = GeneratedFile(b'<RDF></RDF>')
         with errors.context('manifest', 9):
             packager.add('addon/install.rdf', install_rdf)
 
         with errors.context('manifest', 10):
             packager.add('addon2/install.rdf', install_rdf)
             packager.add('addon2/chrome.manifest',
-                         GeneratedFile('binary-component addon2.so'))
+                         GeneratedFile(b'binary-component addon2.so'))
 
         with errors.context('manifest', 11):
             packager.add('addon3/install.rdf', install_rdf)
             packager.add('addon3/chrome.manifest', GeneratedFile(
-                'manifest components/components.manifest'))
+                b'manifest components/components.manifest'))
             packager.add('addon3/components/components.manifest',
-                         GeneratedFile('binary-component addon3.so'))
+                         GeneratedFile(b'binary-component addon3.so'))
 
         with errors.context('manifest', 12):
             install_rdf_addon4 = GeneratedFile(
-                '<RDF>\n<...>\n<em:unpack>true</em:unpack>\n<...>\n</RDF>')
+                b'<RDF>\n<...>\n<em:unpack>true</em:unpack>\n<...>\n</RDF>')
             packager.add('addon4/install.rdf', install_rdf_addon4)
 
         with errors.context('manifest', 13):
             install_rdf_addon5 = GeneratedFile(
-                '<RDF>\n<...>\n<em:unpack>false</em:unpack>\n<...>\n</RDF>')
+                b'<RDF>\n<...>\n<em:unpack>false</em:unpack>\n<...>\n</RDF>')
             packager.add('addon5/install.rdf', install_rdf_addon5)
 
         with errors.context('manifest', 14):
             install_rdf_addon6 = GeneratedFile(
-                '<RDF>\n<... em:unpack=true>\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack=true>\n<...>\n</RDF>')
             packager.add('addon6/install.rdf', install_rdf_addon6)
 
         with errors.context('manifest', 15):
             install_rdf_addon7 = GeneratedFile(
-                '<RDF>\n<... em:unpack=false>\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack=false>\n<...>\n</RDF>')
             packager.add('addon7/install.rdf', install_rdf_addon7)
 
         with errors.context('manifest', 16):
             install_rdf_addon8 = GeneratedFile(
-                '<RDF>\n<... em:unpack="true">\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack="true">\n<...>\n</RDF>')
             packager.add('addon8/install.rdf', install_rdf_addon8)
 
         with errors.context('manifest', 17):
             install_rdf_addon9 = GeneratedFile(
-                '<RDF>\n<... em:unpack="false">\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack="false">\n<...>\n</RDF>')
             packager.add('addon9/install.rdf', install_rdf_addon9)
 
         with errors.context('manifest', 18):
             install_rdf_addon10 = GeneratedFile(
-                '<RDF>\n<... em:unpack=\'true\'>\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack=\'true\'>\n<...>\n</RDF>')
             packager.add('addon10/install.rdf', install_rdf_addon10)
 
         with errors.context('manifest', 19):
             install_rdf_addon11 = GeneratedFile(
-                '<RDF>\n<... em:unpack=\'false\'>\n<...>\n</RDF>')
+                b'<RDF>\n<... em:unpack=\'false\'>\n<...>\n</RDF>')
             packager.add('addon11/install.rdf', install_rdf_addon11)
 
         we_manifest = GeneratedFile(
-            '{"manifest_version": 2, "name": "Test WebExtension", "version": "1.0"}')
+            b'{"manifest_version": 2, "name": "Test WebExtension", "version": "1.0"}')
         # hybrid and hybrid2 are both bootstrapped extensions with
         # embedded webextensions, they differ in the order in which
         # the manifests are added to the packager.
         with errors.context('manifest', 20):
             packager.add('hybrid/install.rdf', install_rdf)
 
         with errors.context('manifest', 21):
             packager.add('hybrid/webextension/manifest.json', we_manifest)
@@ -260,17 +260,17 @@ class TestSimplePackager(unittest.TestCa
             packager.add('hybrid2/webextension/manifest.json', we_manifest)
 
         with errors.context('manifest', 23):
             packager.add('hybrid2/install.rdf', install_rdf)
 
         with errors.context('manifest', 24):
             packager.add('webextension/manifest.json', we_manifest)
 
-        non_we_manifest = GeneratedFile('{"not a webextension": true}')
+        non_we_manifest = GeneratedFile(b'{"not a webextension": true}')
         with errors.context('manifest', 25):
             packager.add('nonwebextension/manifest.json', non_we_manifest)
 
         self.assertEqual(formatter.log, [])
 
         with errors.context('dummy', 1):
             packager.close()
         self.maxDiff = None
@@ -352,78 +352,78 @@ class TestSimplePackager(unittest.TestCa
         self.assertEqual(packager.get_bases(addons=False), set(['', 'qux']))
 
     def test_simple_packager_manifest_consistency(self):
         formatter = MockFormatter()
         # bar/ is detected as an addon because of install.rdf, but top-level
         # includes a manifest inside bar/.
         packager = SimplePackager(formatter)
         packager.add('base.manifest', GeneratedFile(
-            'manifest foo/bar.manifest\n'
-            'manifest bar/baz.manifest\n'
+            b'manifest foo/bar.manifest\n'
+            b'manifest bar/baz.manifest\n'
         ))
-        packager.add('foo/bar.manifest', GeneratedFile('resource bar bar'))
-        packager.add('bar/baz.manifest', GeneratedFile('resource baz baz'))
-        packager.add('bar/install.rdf', GeneratedFile(''))
+        packager.add('foo/bar.manifest', GeneratedFile(b'resource bar bar'))
+        packager.add('bar/baz.manifest', GeneratedFile(b'resource baz baz'))
+        packager.add('bar/install.rdf', GeneratedFile(b''))
 
         with self.assertRaises(ErrorMessage) as e:
             packager.close()
 
         self.assertEqual(e.exception.message,
                          'Error: "bar/baz.manifest" is included from "base.manifest", '
                          'which is outside "bar"')
 
         # bar/ is detected as a separate base because of chrome.manifest that
         # is included nowhere, but top-level includes another manifest inside
         # bar/.
         packager = SimplePackager(formatter)
         packager.add('base.manifest', GeneratedFile(
-            'manifest foo/bar.manifest\n'
-            'manifest bar/baz.manifest\n'
+            b'manifest foo/bar.manifest\n'
+            b'manifest bar/baz.manifest\n'
         ))
-        packager.add('foo/bar.manifest', GeneratedFile('resource bar bar'))
-        packager.add('bar/baz.manifest', GeneratedFile('resource baz baz'))
-        packager.add('bar/chrome.manifest', GeneratedFile('resource baz baz'))
+        packager.add('foo/bar.manifest', GeneratedFile(b'resource bar bar'))
+        packager.add('bar/baz.manifest', GeneratedFile(b'resource baz baz'))
+        packager.add('bar/chrome.manifest', GeneratedFile(b'resource baz baz'))
 
         with self.assertRaises(ErrorMessage) as e:
             packager.close()
 
         self.assertEqual(e.exception.message,
                          'Error: "bar/baz.manifest" is included from "base.manifest", '
                          'which is outside "bar"')
 
         # bar/ is detected as a separate base because of chrome.manifest that
         # is included nowhere, but chrome.manifest includes baz.manifest from
         # the same directory. This shouldn't error out.
         packager = SimplePackager(formatter)
         packager.add('base.manifest', GeneratedFile(
-            'manifest foo/bar.manifest\n'
+            b'manifest foo/bar.manifest\n'
         ))
-        packager.add('foo/bar.manifest', GeneratedFile('resource bar bar'))
-        packager.add('bar/baz.manifest', GeneratedFile('resource baz baz'))
+        packager.add('foo/bar.manifest', GeneratedFile(b'resource bar bar'))
+        packager.add('bar/baz.manifest', GeneratedFile(b'resource baz baz'))
         packager.add('bar/chrome.manifest',
-                     GeneratedFile('manifest baz.manifest'))
+                     GeneratedFile(b'manifest baz.manifest'))
         packager.close()
 
 
 class TestSimpleManifestSink(unittest.TestCase):
     def test_simple_manifest_parser(self):
         formatter = MockFormatter()
-        foobar = GeneratedFile('foobar')
-        foobaz = GeneratedFile('foobaz')
-        fooqux = GeneratedFile('fooqux')
-        foozot = GeneratedFile('foozot')
+        foobar = GeneratedFile(b'foobar')
+        foobaz = GeneratedFile(b'foobaz')
+        fooqux = GeneratedFile(b'fooqux')
+        foozot = GeneratedFile(b'foozot')
         finder = MockFinder({
             'bin/foo/bar': foobar,
             'bin/foo/baz': foobaz,
             'bin/foo/qux': fooqux,
             'bin/foo/zot': foozot,
-            'bin/foo/chrome.manifest': GeneratedFile('resource foo foo/'),
+            'bin/foo/chrome.manifest': GeneratedFile(b'resource foo foo/'),
             'bin/chrome.manifest':
-            GeneratedFile('manifest foo/chrome.manifest'),
+            GeneratedFile(b'manifest foo/chrome.manifest'),
         })
         parser = SimpleManifestSink(finder, formatter)
         component0 = Component('component0')
         component1 = Component('component1')
         component2 = Component('component2', destdir='destdir')
         parser.add(component0, 'bin/foo/b*')
         parser.add(component1, 'bin/foo/qux')
         parser.add(component1, 'bin/foo/chrome.manifest')
--- a/python/mozbuild/mozpack/test/test_packager_formats.py
+++ b/python/mozbuild/mozpack/test/test_packager_formats.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import mozunit
 import unittest
 from mozpack.packager.formats import (
     FlatFormatter,
     JarFormatter,
     OmniJarFormatter,
 )
@@ -53,32 +53,32 @@ CONTENTS = {
         ManifestBinaryComponent('components', 'foo.so'),
         ManifestContent('app/chrome', 'content', 'foo/'),
         ManifestComponent('app/components', '{foo-id}', 'foo.js'),
         ManifestContent('addon0/chrome', 'addon0', 'foo/bar/'),
         ManifestContent('addon1/chrome', 'addon1', 'foo/bar/'),
         ManifestContent('app/chrome/addons/addon2/chrome', 'addon2', 'foo/bar/'),
     ],
     'files': {
-        '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'),
+        'chrome/f/oo/bar/baz': GeneratedFile(b'foobarbaz'),
+        'chrome/f/oo/baz': GeneratedFile(b'foobaz'),
+        'chrome/f/oo/qux': GeneratedFile(b'fooqux'),
+        'components/foo.so': GeneratedFile(b'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'),
+        'foo': GeneratedFile(b'foo'),
+        'app/chrome/foo/foo': GeneratedFile(b'appfoo'),
+        'app/components/foo.js': GeneratedFile(b'foo.js'),
+        'addon0/chrome/foo/bar/baz': GeneratedFile(b'foobarbaz'),
         'addon0/components/foo.xpt': foo2_xpt,
         'addon0/components/bar.xpt': bar_xpt,
-        'addon1/chrome/foo/bar/baz': GeneratedFile('foobarbaz'),
+        'addon1/chrome/foo/bar/baz': GeneratedFile(b'foobarbaz'),
         'addon1/components/foo.xpt': foo2_xpt,
         'addon1/components/bar.xpt': bar_xpt,
-        'app/chrome/addons/addon2/chrome/foo/bar/baz': GeneratedFile('foobarbaz'),
+        'app/chrome/addons/addon2/chrome/foo/bar/baz': GeneratedFile(b'foobarbaz'),
         'app/chrome/addons/addon2/components/foo.xpt': foo2_xpt,
         'app/chrome/addons/addon2/components/bar.xpt': bar_xpt,
     },
 }
 
 FILES = CONTENTS['files']
 
 RESULT_FLAT = {
@@ -270,17 +270,17 @@ CONTENTS_WITH_BASE = {
     ],
     'files': {
         mozpath.join('base/root', p): f
         for p, f in CONTENTS['files'].iteritems()
     },
 }
 
 EXTRA_CONTENTS = {
-    'extra/file': GeneratedFile('extra file'),
+    'extra/file': GeneratedFile(b'extra file'),
 }
 
 CONTENTS_WITH_BASE['files'].update(EXTRA_CONTENTS)
 
 
 def result_with_base(results):
     result = {
         mozpath.join('base/root', p): v
@@ -403,17 +403,17 @@ class TestFormatters(TestErrors, unittes
             registry = FileRegistry()
             f = OmniJarFormatter(registry, 'omni.foo', non_resources=[
                 'defaults/messenger/mailViews.dat',
                 'defaults/foo/*',
                 '*/dummy',
             ])
             f.add_base('')
             f.add_base('app')
-            f.add(mozpath.join(base, path), GeneratedFile(''))
+            f.add(mozpath.join(base, path), GeneratedFile(b''))
             if f.copier.contains(mozpath.join(base, path)):
                 return False
             self.assertTrue(f.copier.contains(mozpath.join(base, 'omni.foo')))
             self.assertTrue(f.copier[mozpath.join(base, 'omni.foo')]
                             .contains(path))
             return True
 
         for base in ['', 'app/']:
--- a/python/mozbuild/mozpack/test/test_packager_l10n.py
+++ b/python/mozbuild/mozpack/test/test_packager_l10n.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 import mozunit
 from test_packager import MockFinder
 from mozpack.packager import l10n
 from mozpack.files import (
     GeneratedFile,
     ManifestFile,
@@ -18,26 +18,26 @@ from mozpack.chrome.manifest import (
     ManifestContent,
 )
 from mozpack.copier import FileRegistry
 from mozpack.packager.formats import FlatFormatter
 
 
 class TestL10NRepack(unittest.TestCase):
     def test_l10n_repack(self):
-        foo = GeneratedFile('foo')
-        foobar = GeneratedFile('foobar')
-        qux = GeneratedFile('qux')
-        bar = GeneratedFile('bar')
-        baz = GeneratedFile('baz')
-        dict_aa = GeneratedFile('dict_aa')
-        dict_bb = GeneratedFile('dict_bb')
-        dict_cc = GeneratedFile('dict_cc')
-        barbaz = GeneratedFile('barbaz')
-        lst = GeneratedFile('foo\nbar')
+        foo = GeneratedFile(b'foo')
+        foobar = GeneratedFile(b'foobar')
+        qux = GeneratedFile(b'qux')
+        bar = GeneratedFile(b'bar')
+        baz = GeneratedFile(b'baz')
+        dict_aa = GeneratedFile(b'dict_aa')
+        dict_bb = GeneratedFile(b'dict_bb')
+        dict_cc = GeneratedFile(b'dict_cc')
+        barbaz = GeneratedFile(b'barbaz')
+        lst = GeneratedFile(b'foo\nbar')
         app_finder = MockFinder({
             'bar/foo': foo,
             'chrome/foo/foobar': foobar,
             'chrome/qux/qux.properties': qux,
             'chrome/qux/baz/baz.properties': baz,
             'chrome/chrome.manifest': ManifestFile('chrome', [
                 ManifestContent('chrome', 'foo', 'foo/'),
                 ManifestLocale('chrome', 'qux', 'en-US', 'qux/'),
@@ -54,21 +54,21 @@ class TestL10NRepack(unittest.TestCase):
             'app/dict/bb': dict_bb,
             'app/dict/cc': dict_cc,
             'app/chrome/bar/search/foo.xml': foo,
             'app/chrome/bar/search/bar.xml': bar,
             'app/chrome/bar/search/lst.txt': lst,
         })
         app_finder.jarlogs = {}
         app_finder.base = 'app'
-        foo_l10n = GeneratedFile('foo_l10n')
-        qux_l10n = GeneratedFile('qux_l10n')
-        baz_l10n = GeneratedFile('baz_l10n')
-        barbaz_l10n = GeneratedFile('barbaz_l10n')
-        lst_l10n = GeneratedFile('foo\nqux')
+        foo_l10n = GeneratedFile(b'foo_l10n')
+        qux_l10n = GeneratedFile(b'qux_l10n')
+        baz_l10n = GeneratedFile(b'baz_l10n')
+        barbaz_l10n = GeneratedFile(b'barbaz_l10n')
+        lst_l10n = GeneratedFile(b'foo\nqux')
         l10n_finder = MockFinder({
             'chrome/qux-l10n/qux.properties': qux_l10n,
             'chrome/qux-l10n/baz/baz.properties': baz_l10n,
             'chrome/chrome.manifest': ManifestFile('chrome', [
                 ManifestLocale('chrome', 'qux', 'x-test', 'qux-l10n/'),
             ]),
             'chrome.manifest':
             ManifestFile('', [Manifest('', 'chrome/chrome.manifest')]),
--- a/python/mozbuild/mozpack/test/test_packager_unpack.py
+++ b/python/mozbuild/mozpack/test/test_packager_unpack.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 import mozunit
 from mozpack.packager.formats import (
     FlatFormatter,
     JarFormatter,
     OmniJarFormatter,
 )
 from mozpack.packager.unpack import unpack_to_registry
--- a/python/mozbuild/mozpack/test/test_path.py
+++ b/python/mozbuild/mozpack/test/test_path.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, unicode_literals
 
 from mozpack.path import (
     relpath,
     join,
     normpath,
     dirname,
     commonprefix,
     basename,