Bug 924617 - Part 1: Properly format jar.py; r=mshal
authorGregory Szorc <gps@mozilla.com>
Fri, 11 Oct 2013 13:20:28 -0700
changeset 164358 a6a1f08b5561c4ca3e561c30fd2a9eb3fbced4f8
parent 164357 a83e068892bbef0dd6df474694fa8af5f917eb76
child 164359 c71abced5149f0527f0377130a4fe5d2c5048658
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmshal
bugs924617
milestone27.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 924617 - Part 1: Properly format jar.py; r=mshal
python/mozbuild/mozbuild/jar.py
--- a/python/mozbuild/mozbuild/jar.py
+++ b/python/mozbuild/mozbuild/jar.py
@@ -2,485 +2,536 @@
 # 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/.
 
 '''jarmaker.py provides a python class to package up chrome content by
 processing jar.mn files.
 
 See the documentation for jar.mn on MDC for further details on the format.
 '''
+
 import sys
 import os
 import errno
 import re
 import logging
 from time import localtime
 from MozZipFile import ZipFile
 from cStringIO import StringIO
 
 from mozbuild.util import (
     lock_file,
     PushbackIter,
 )
 
 from Preprocessor import Preprocessor
 from mozbuild.action.buildlist import addEntriesToListFile
-if sys.platform == "win32":
-  from ctypes import windll, WinError
-  CreateHardLink = windll.kernel32.CreateHardLinkA
+if sys.platform == 'win32':
+    from ctypes import windll, WinError
+    CreateHardLink = windll.kernel32.CreateHardLinkA
 
 __all__ = ['JarMaker']
 
-class ZipEntry:
-  '''Helper class for jar output.
+
+class ZipEntry(object):
+    '''Helper class for jar output.
+
+      This class defines a simple file-like object for a zipfile.ZipEntry
+      so that we can consecutively write to it and then close it.
+      This methods hooks into ZipFile.writestr on close().
+      '''
 
-  This class defines a simple file-like object for a zipfile.ZipEntry
-  so that we can consecutively write to it and then close it.
-  This methods hooks into ZipFile.writestr on close().
-  '''
-  def __init__(self, name, zipfile):
-    self._zipfile = zipfile
-    self._name = name
-    self._inner = StringIO()
+    def __init__(self, name, zipfile):
+        self._zipfile = zipfile
+        self._name = name
+        self._inner = StringIO()
+
+    def write(self, content):
+        '''Append the given content to this zip entry'''
 
-  def write(self, content):
-    'Append the given content to this zip entry'
-    self._inner.write(content)
-    return
+        self._inner.write(content)
+        return
 
-  def close(self):
-    'The close method writes the content back to the zip file.'
-    self._zipfile.writestr(self._name, self._inner.getvalue())
+    def close(self):
+        '''The close method writes the content back to the zip file.'''
+
+        self._zipfile.writestr(self._name, self._inner.getvalue())
+
 
 def getModTime(aPath):
-  if not os.path.isfile(aPath):
-    return 0
-  mtime = os.stat(aPath).st_mtime
-  return localtime(mtime)
+    if not os.path.isfile(aPath):
+        return 0
+    mtime = os.stat(aPath).st_mtime
+    return localtime(mtime)
 
 
 class JarMaker(object):
-  '''JarMaker reads jar.mn files and process those into jar files or
-  flat directories, along with chrome.manifest files.
-  '''
+    '''JarMaker reads jar.mn files and process those into jar files or
+      flat directories, along with chrome.manifest files.
+      '''
 
-  ignore = re.compile('\s*(\#.*)?$')
-  jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
-  relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
-  regline = re.compile('\%\s+(.*)$')
-  entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
-  entryline = re.compile(entryre + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@]+)\))?\s*$')
+    ignore = re.compile('\s*(\#.*)?$')
+    jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
+    relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
+    regline = re.compile('\%\s+(.*)$')
+    entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
+    entryline = re.compile(entryre
+                           + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@]+)\))?\s*$'
+                           )
+
+    def __init__(self, outputFormat='flat', useJarfileManifest=True,
+        useChromeManifest=False):
 
-  def __init__(self, outputFormat = 'flat', useJarfileManifest = True,
-               useChromeManifest = False):
-    self.outputFormat = outputFormat
-    self.useJarfileManifest = useJarfileManifest
-    self.useChromeManifest = useChromeManifest
-    self.pp = Preprocessor()
-    self.topsourcedir = None
-    self.sourcedirs = []
-    self.localedirs = None
-    self.l10nbase = None
-    self.l10nmerge = None
-    self.relativesrcdir = None
-    self.rootManifestAppId = None
+        self.outputFormat = outputFormat
+        self.useJarfileManifest = useJarfileManifest
+        self.useChromeManifest = useChromeManifest
+        self.pp = Preprocessor()
+        self.topsourcedir = None
+        self.sourcedirs = []
+        self.localedirs = None
+        self.l10nbase = None
+        self.l10nmerge = None
+        self.relativesrcdir = None
+        self.rootManifestAppId = None
 
-  def getCommandLineParser(self):
-    '''Get a optparse.OptionParser for jarmaker.
+    def getCommandLineParser(self):
+        '''Get a optparse.OptionParser for jarmaker.
+
+        This OptionParser has the options for jarmaker as well as
+        the options for the inner PreProcessor.
+        '''
+
+        # HACK, we need to unescape the string variables we get,
+        # the perl versions didn't grok strings right
 
-    This OptionParser has the options for jarmaker as well as
-    the options for the inner PreProcessor.
-    '''
-    # HACK, we need to unescape the string variables we get,
-    # the perl versions didn't grok strings right
-    p = self.pp.getCommandLineParser(unescapeDefines = True)
-    p.add_option('-f', type="choice", default="jar",
-                 choices=('jar', 'flat', 'symlink'),
-                 help="fileformat used for output", metavar="[jar, flat, symlink]")
-    p.add_option('-v', action="store_true", dest="verbose",
-                 help="verbose output")
-    p.add_option('-q', action="store_false", dest="verbose",
-                 help="verbose output")
-    p.add_option('-e', action="store_true",
-                 help="create chrome.manifest instead of jarfile.manifest")
-    p.add_option('--both-manifests', action="store_true",
-                 dest="bothManifests",
-                 help="create chrome.manifest and jarfile.manifest")
-    p.add_option('-s', type="string", action="append", default=[],
-                 help="source directory")
-    p.add_option('-t', type="string",
-                 help="top source directory")
-    p.add_option('-c', '--l10n-src', type="string", action="append",
-                 help="localization directory")
-    p.add_option('--l10n-base', type="string", action="store",
-                 help="base directory to be used for localization (requires relativesrcdir)")
-    p.add_option('--locale-mergedir', type="string", action="store",
-                 help="base directory to be used for l10n-merge (requires l10n-base and relativesrcdir)")
-    p.add_option('--relativesrcdir', type="string",
-                 help="relativesrcdir to be used for localization")
-    p.add_option('-j', type="string",
-                 help="jarfile directory")
-    p.add_option('--root-manifest-entry-appid', type="string",
-                 help="add an app id specific root chrome manifest entry.")
-    return p
+        p = self.pp.getCommandLineParser(unescapeDefines=True)
+        p.add_option('-f', type='choice', default='jar',
+            choices=('jar', 'flat', 'symlink'),
+            help='fileformat used for output',
+            metavar='[jar, flat, symlink]',
+            )
+        p.add_option('-v', action='store_true', dest='verbose',
+                     help='verbose output')
+        p.add_option('-q', action='store_false', dest='verbose',
+                     help='verbose output')
+        p.add_option('-e', action='store_true',
+                     help='create chrome.manifest instead of jarfile.manifest'
+                     )
+        p.add_option('--both-manifests', action='store_true',
+                     dest='bothManifests',
+                     help='create chrome.manifest and jarfile.manifest')
+        p.add_option('-s', type='string', action='append', default=[],
+                     help='source directory')
+        p.add_option('-t', type='string', help='top source directory')
+        p.add_option('-c', '--l10n-src', type='string', action='append'
+                     , help='localization directory')
+        p.add_option('--l10n-base', type='string', action='store',
+                     help='base directory to be used for localization (requires relativesrcdir)'
+                     )
+        p.add_option('--locale-mergedir', type='string', action='store'
+                     ,
+                     help='base directory to be used for l10n-merge (requires l10n-base and relativesrcdir)'
+                     )
+        p.add_option('--relativesrcdir', type='string',
+                     help='relativesrcdir to be used for localization')
+        p.add_option('-j', type='string', help='jarfile directory')
+        p.add_option('--root-manifest-entry-appid', type='string',
+                     help='add an app id specific root chrome manifest entry.'
+                     )
+        return p
 
-  def processIncludes(self, includes):
-    '''Process given includes with the inner PreProcessor.
+    def processIncludes(self, includes):
+        '''Process given includes with the inner PreProcessor.
+
+        Only use this for #defines, the includes shouldn't generate
+        content.
+        '''
+
+        self.pp.out = StringIO()
+        for inc in includes:
+            self.pp.do_include(inc)
+        includesvalue = self.pp.out.getvalue()
+        if includesvalue:
+            logging.info('WARNING: Includes produce non-empty output')
+        self.pp.out = None
+
+    def finalizeJar(self, jarPath, chromebasepath, register, doZip=True):
+        '''Helper method to write out the chrome registration entries to
+         jarfile.manifest or chrome.manifest, or both.
+
+        The actual file processing is done in updateManifest.
+        '''
+
+        # rewrite the manifest, if entries given
+        if not register:
+            return
 
-    Only use this for #defines, the includes shouldn't generate
-    content.
-    '''
-    self.pp.out = StringIO()
-    for inc in includes:
-      self.pp.do_include(inc)
-    includesvalue = self.pp.out.getvalue()
-    if includesvalue:
-      logging.info("WARNING: Includes produce non-empty output")
-    self.pp.out = None
-    pass
+        chromeManifest = os.path.join(os.path.dirname(jarPath), '..',
+                'chrome.manifest')
 
-  def finalizeJar(self, jarPath, chromebasepath, register,
-                  doZip=True):
-    '''Helper method to write out the chrome registration entries to
-    jarfile.manifest or chrome.manifest, or both.
+        if self.useJarfileManifest:
+            self.updateManifest(jarPath + '.manifest',
+                                chromebasepath.format(''), register)
+            addEntriesToListFile(chromeManifest,
+                                 ['manifest chrome/{0}.manifest'.format(os.path.basename(jarPath))])
+        if self.useChromeManifest:
+            self.updateManifest(chromeManifest,
+                                chromebasepath.format('chrome/'),
+                                register)
+
+        # If requested, add a root chrome manifest entry (assumed to be in the parent directory
+        # of chromeManifest) with the application specific id. In cases where we're building
+        # lang packs, the root manifest must know about application sub directories.
 
-    The actual file processing is done in updateManifest.
-    '''
-    # rewrite the manifest, if entries given
-    if not register:
-      return
-
-    chromeManifest = os.path.join(os.path.dirname(jarPath),
-                                  '..', 'chrome.manifest')
+        if self.rootManifestAppId:
+            rootChromeManifest = \
+                os.path.join(os.path.normpath(os.path.dirname(chromeManifest)),
+                             '..', 'chrome.manifest')
+            rootChromeManifest = os.path.normpath(rootChromeManifest)
+            chromeDir = \
+                os.path.basename(os.path.dirname(os.path.normpath(chromeManifest)))
+            logging.info("adding '%s' entry to root chrome manifest appid=%s"
+                          % (chromeDir, self.rootManifestAppId))
+            addEntriesToListFile(rootChromeManifest,
+                                 ['manifest %s/chrome.manifest application=%s'
+                                  % (chromeDir,
+                                 self.rootManifestAppId)])
 
-    if self.useJarfileManifest:
-      self.updateManifest(jarPath + '.manifest', chromebasepath.format(''),
-                          register)
-      addEntriesToListFile(chromeManifest, ['manifest chrome/{0}.manifest'
-                                            .format(os.path.basename(jarPath))])
-    if self.useChromeManifest:
-      self.updateManifest(chromeManifest, chromebasepath.format('chrome/'),
-                          register)
+    def updateManifest(self, manifestPath, chromebasepath, register):
+        '''updateManifest replaces the % in the chrome registration entries
+        with the given chrome base path, and updates the given manifest file.
+        '''
 
-    # If requested, add a root chrome manifest entry (assumed to be in the parent directory
-    # of chromeManifest) with the application specific id. In cases where we're building
-    # lang packs, the root manifest must know about application sub directories.
-    if self.rootManifestAppId:
-      rootChromeManifest = os.path.join(os.path.normpath(os.path.dirname(chromeManifest)),
-                                        '..', 'chrome.manifest')
-      rootChromeManifest = os.path.normpath(rootChromeManifest)
-      chromeDir = os.path.basename(os.path.dirname(os.path.normpath(chromeManifest)))
-      logging.info("adding '%s' entry to root chrome manifest appid=%s" % (chromeDir, self.rootManifestAppId))
-      addEntriesToListFile(rootChromeManifest, ['manifest %s/chrome.manifest application=%s' % (chromeDir, self.rootManifestAppId)])
+        lock = lock_file(manifestPath + '.lck')
+        try:
+            myregister = dict.fromkeys(map(lambda s: s.replace('%',
+                    chromebasepath), register.iterkeys()))
+            manifestExists = os.path.isfile(manifestPath)
+            mode = manifestExists and 'r+b' or 'wb'
+            mf = open(manifestPath, mode)
+            if manifestExists:
+                # import previous content into hash, ignoring empty ones and comments
+                imf = re.compile('(#.*)?$')
+                for l in re.split('[\r\n]+', mf.read()):
+                    if imf.match(l):
+                        continue
+                    myregister[l] = None
+                mf.seek(0)
+            for k in myregister.iterkeys():
+                mf.write(k + os.linesep)
+            mf.close()
+        finally:
+            lock = None
+
+    def makeJar(self, infile, jardir):
+        '''makeJar is the main entry point to JarMaker.
+
+        It takes the input file, the output directory, the source dirs and the
+        top source dir as argument, and optionally the l10n dirs.
+        '''
 
-  def updateManifest(self, manifestPath, chromebasepath, register):
-    '''updateManifest replaces the % in the chrome registration entries
-    with the given chrome base path, and updates the given manifest file.
-    '''
-    lock = lock_file(manifestPath + '.lck')
-    try:
-      myregister = dict.fromkeys(map(lambda s: s.replace('%', chromebasepath),
-                                     register.iterkeys()))
-      manifestExists = os.path.isfile(manifestPath)
-      mode = (manifestExists and 'r+b') or 'wb'
-      mf = open(manifestPath, mode)
-      if manifestExists:
-        # import previous content into hash, ignoring empty ones and comments
-        imf = re.compile('(#.*)?$')
-        for l in re.split('[\r\n]+', mf.read()):
-          if imf.match(l):
-            continue
-          myregister[l] = None
-        mf.seek(0)
-      for k in myregister.iterkeys():
-        mf.write(k + os.linesep)
-      mf.close()
-    finally:
-      lock = None
+        # making paths absolute, guess srcdir if file and add to sourcedirs
+        _normpath = lambda p: os.path.normpath(os.path.abspath(p))
+        self.topsourcedir = _normpath(self.topsourcedir)
+        self.sourcedirs = [_normpath(p) for p in self.sourcedirs]
+        if self.localedirs:
+            self.localedirs = [_normpath(p) for p in self.localedirs]
+        elif self.relativesrcdir:
+            self.localedirs = \
+                self.generateLocaleDirs(self.relativesrcdir)
+        if isinstance(infile, basestring):
+            logging.info('processing ' + infile)
+            self.sourcedirs.append(_normpath(os.path.dirname(infile)))
+        pp = self.pp.clone()
+        pp.out = StringIO()
+        pp.do_include(infile)
+        lines = PushbackIter(pp.out.getvalue().splitlines())
+        try:
+            while True:
+                l = lines.next()
+                m = self.jarline.match(l)
+                if not m:
+                    raise RuntimeError(l)
+                if m.group('jarfile') is None:
+                    # comment
+                    continue
+                self.processJarSection(m.group('jarfile'), lines,
+                        jardir)
+        except StopIteration:
+            # we read the file
+            pass
+        return
 
-  def makeJar(self, infile, jardir):
-    '''makeJar is the main entry point to JarMaker.
+    def generateLocaleDirs(self, relativesrcdir):
+        if os.path.basename(relativesrcdir) == 'locales':
+            # strip locales
+            l10nrelsrcdir = os.path.dirname(relativesrcdir)
+        else:
+            l10nrelsrcdir = relativesrcdir
+        locdirs = []
 
-    It takes the input file, the output directory, the source dirs and the
-    top source dir as argument, and optionally the l10n dirs.
-    '''
-    # making paths absolute, guess srcdir if file and add to sourcedirs
-    _normpath = lambda p: os.path.normpath(os.path.abspath(p))
-    self.topsourcedir = _normpath(self.topsourcedir)
-    self.sourcedirs = [_normpath(p) for p in self.sourcedirs]
-    if self.localedirs:
-      self.localedirs = [_normpath(p) for p in self.localedirs]
-    elif self.relativesrcdir:
-      self.localedirs = self.generateLocaleDirs(self.relativesrcdir)
-    if isinstance(infile, basestring):
-      logging.info("processing " + infile)
-      self.sourcedirs.append(_normpath(os.path.dirname(infile)))
-    pp = self.pp.clone()
-    pp.out = StringIO()
-    pp.do_include(infile)
-    lines = PushbackIter(pp.out.getvalue().splitlines())
-    try:
-      while True:
-        l = lines.next()
-        m = self.jarline.match(l)
-        if not m:
-          raise RuntimeError(l)
-        if m.group('jarfile') is None:
-          # comment
-          continue
-        self.processJarSection(m.group('jarfile'), lines, jardir)
-    except StopIteration:
-      # we read the file
-      pass
-    return
+        # generate locales dirs, merge, l10nbase, en-US
+        if self.l10nmerge:
+            locdirs.append(os.path.join(self.l10nmerge, l10nrelsrcdir))
+        if self.l10nbase:
+            locdirs.append(os.path.join(self.l10nbase, l10nrelsrcdir))
+        if self.l10nmerge or not self.l10nbase:
+            # add en-US if we merge, or if it's not l10n
+            locdirs.append(os.path.join(self.topsourcedir,
+                           relativesrcdir, 'en-US'))
+        return locdirs
+
+    def processJarSection(self, jarfile, lines, jardir):
+        '''Internal method called by makeJar to actually process a section
+        of a jar.mn file.
+
+        jarfile is the basename of the jarfile or the directory name for
+        flat output, lines is a PushbackIter of the lines of jar.mn,
+        the remaining options are carried over from makeJar.
+        '''
+
+        # chromebasepath is used for chrome registration manifests
+        # {0} is getting replaced with chrome/ for chrome.manifest, and with
+        # an empty string for jarfile.manifest
+
+        chromebasepath = '{0}' + os.path.basename(jarfile)
+        if self.outputFormat == 'jar':
+            chromebasepath = 'jar:' + chromebasepath + '.jar!'
+        chromebasepath += '/'
+
+        jarfile = os.path.join(jardir, jarfile)
+        jf = None
+        if self.outputFormat == 'jar':
+            # jar
+            jarfilepath = jarfile + '.jar'
+            try:
+                os.makedirs(os.path.dirname(jarfilepath))
+            except OSError, error:
+                if error.errno != errno.EEXIST:
+                    raise
+            jf = ZipFile(jarfilepath, 'a', lock=True)
+            outHelper = self.OutputHelper_jar(jf)
+        else:
+            outHelper = getattr(self, 'OutputHelper_'
+                                + self.outputFormat)(jarfile)
+        register = {}
+
+        # This loop exits on either
+        # - the end of the jar.mn file
+        # - an line in the jar.mn file that's not part of a jar section
+        # - on an exception raised, close the jf in that case in a finally
 
-  def generateLocaleDirs(self, relativesrcdir):
-    if os.path.basename(relativesrcdir) == 'locales':
-      # strip locales
-      l10nrelsrcdir = os.path.dirname(relativesrcdir)
-    else:
-      l10nrelsrcdir = relativesrcdir
-    locdirs = []
-    # generate locales dirs, merge, l10nbase, en-US
-    if self.l10nmerge:
-      locdirs.append(os.path.join(self.l10nmerge, l10nrelsrcdir))
-    if self.l10nbase:
-      locdirs.append(os.path.join(self.l10nbase, l10nrelsrcdir))
-    if self.l10nmerge or not self.l10nbase:
-      # add en-US if we merge, or if it's not l10n
-      locdirs.append(os.path.join(self.topsourcedir, relativesrcdir, 'en-US'))
-    return locdirs
+        try:
+            while True:
+                try:
+                    l = lines.next()
+                except StopIteration:
+                    # we're done with this jar.mn, and this jar section
+                    self.finalizeJar(jarfile, chromebasepath, register)
+                    if jf is not None:
+                        jf.close()
 
-  def processJarSection(self, jarfile, lines, jardir):
-    '''Internal method called by makeJar to actually process a section
-    of a jar.mn file.
+                    # reraise the StopIteration for makeJar
+                    raise
+                if self.ignore.match(l):
+                    continue
+                m = self.relsrcline.match(l)
+                if m:
+                    relativesrcdir = m.group('relativesrcdir')
+                    self.localedirs = \
+                        self.generateLocaleDirs(relativesrcdir)
+                    continue
+                m = self.regline.match(l)
+                if m:
+                    rline = m.group(1)
+                    register[rline] = 1
+                    continue
+                m = self.entryline.match(l)
+                if not m:
+                    # neither an entry line nor chrome reg, this jar section is done
+                    self.finalizeJar(jarfile, chromebasepath, register)
+                    if jf is not None:
+                        jf.close()
+                    lines.pushback(l)
+                    return
+                self._processEntryLine(m, outHelper, jf)
+        finally:
+            if jf is not None:
+                jf.close()
+        return
 
-    jarfile is the basename of the jarfile or the directory name for
-    flat output, lines is a PushbackIter of the lines of jar.mn,
-    the remaining options are carried over from makeJar.
-    '''
+    def _processEntryLine(self, m, outHelper, jf):
+        out = m.group('output')
+        src = m.group('source') or os.path.basename(out)
+
+        # pick the right sourcedir -- l10n, topsrc or src
 
-    # chromebasepath is used for chrome registration manifests
-    # {0} is getting replaced with chrome/ for chrome.manifest, and with
-    # an empty string for jarfile.manifest
-    chromebasepath = '{0}' + os.path.basename(jarfile)
-    if self.outputFormat == 'jar':
-      chromebasepath = 'jar:' + chromebasepath + '.jar!'
-    chromebasepath += '/'
+        if m.group('locale'):
+            src_base = self.localedirs
+        elif src.startswith('/'):
+            # path/in/jar/file_name.xul     (/path/in/sourcetree/file_name.xul)
+            # refers to a path relative to topsourcedir, use that as base
+            # and strip the leading '/'
+            src_base = [self.topsourcedir]
+            src = src[1:]
+        else:
+            # use srcdirs and the objdir (current working dir) for relative paths
+            src_base = self.sourcedirs + [os.getcwd()]
 
-    jarfile = os.path.join(jardir, jarfile)
-    jf = None
-    if self.outputFormat == 'jar':
-      #jar
-      jarfilepath = jarfile + '.jar'
-      try:
-        os.makedirs(os.path.dirname(jarfilepath))
-      except OSError as error:
-        if error.errno != errno.EEXIST:
-          raise
-      jf = ZipFile(jarfilepath, 'a', lock = True)
-      outHelper = self.OutputHelper_jar(jf)
-    else:
-      outHelper = getattr(self, 'OutputHelper_' + self.outputFormat)(jarfile)
-    register = {}
-    # This loop exits on either
-    # - the end of the jar.mn file
-    # - an line in the jar.mn file that's not part of a jar section
-    # - on an exception raised, close the jf in that case in a finally
-    try:
-      while True:
-        try:
-          l = lines.next()
-        except StopIteration:
-          # we're done with this jar.mn, and this jar section
-          self.finalizeJar(jarfile, chromebasepath, register)
-          if jf is not None:
-            jf.close()
-          # reraise the StopIteration for makeJar
-          raise
-        if self.ignore.match(l):
-          continue
-        m = self.relsrcline.match(l)
-        if m:
-          relativesrcdir = m.group('relativesrcdir')
-          self.localedirs = self.generateLocaleDirs(relativesrcdir)
-          continue
-        m = self.regline.match(l)
-        if  m:
-          rline = m.group(1)
-          register[rline] = 1
-          continue
-        m = self.entryline.match(l)
-        if not m:
-          # neither an entry line nor chrome reg, this jar section is done
-          self.finalizeJar(jarfile, chromebasepath, register)
-          if jf is not None:
-            jf.close()
-          lines.pushback(l)
-          return
-        self._processEntryLine(m, outHelper, jf)
-    finally:
-      if jf is not None:
-        jf.close()
-    return
+        # check if the source file exists
+        realsrc = None
+        for _srcdir in src_base:
+            if os.path.isfile(os.path.join(_srcdir, src)):
+                realsrc = os.path.join(_srcdir, src)
+                break
+        if realsrc is None:
+            if jf is not None:
+                jf.close()
+            raise RuntimeError('File "{0}" not found in {1}'.format(src,
+                               ', '.join(src_base)))
+        if m.group('optPreprocess'):
+            outf = outHelper.getOutput(out)
+            inf = open(realsrc)
+            pp = self.pp.clone()
+            if src[-4:] == '.css':
+                pp.setMarker('%')
+            pp.out = outf
+            pp.do_include(inf)
+            pp.warnUnused(realsrc)
+            outf.close()
+            inf.close()
+            return
+
+        # copy or symlink if newer or overwrite
+
+        if m.group('optOverwrite') or getModTime(realsrc) \
+            > outHelper.getDestModTime(m.group('output')):
+            if self.outputFormat == 'symlink':
+                outHelper.symlink(realsrc, out)
+                return
+            outf = outHelper.getOutput(out)
+
+            # open in binary mode, this can be images etc
+
+            inf = open(realsrc, 'rb')
+            outf.write(inf.read())
+            outf.close()
+            inf.close()
+
+    class OutputHelper_jar(object):
+        '''Provide getDestModTime and getOutput for a given jarfile.'''
+
+        def __init__(self, jarfile):
+            self.jarfile = jarfile
+
+        def getDestModTime(self, aPath):
+            try:
+                info = self.jarfile.getinfo(aPath)
+                return info.date_time
+            except:
+                return 0
+
+        def getOutput(self, name):
+            return ZipEntry(name, self.jarfile)
 
-  def _processEntryLine(self, m, outHelper, jf):
-      out = m.group('output')
-      src = m.group('source') or os.path.basename(out)
-      # pick the right sourcedir -- l10n, topsrc or src
-      if m.group('locale'):
-        src_base = self.localedirs
-      elif src.startswith('/'):
-        # path/in/jar/file_name.xul     (/path/in/sourcetree/file_name.xul)
-        # refers to a path relative to topsourcedir, use that as base
-        # and strip the leading '/'
-        src_base = [self.topsourcedir]
-        src = src[1:]
-      else:
-        # use srcdirs and the objdir (current working dir) for relative paths
-        src_base = self.sourcedirs + [os.getcwd()]
-      # check if the source file exists
-      realsrc = None
-      for _srcdir in src_base:
-        if os.path.isfile(os.path.join(_srcdir, src)):
-          realsrc = os.path.join(_srcdir, src)
-          break
-      if realsrc is None:
-        if jf is not None:
-          jf.close()
-        raise RuntimeError('File "{0}" not found in {1}'
-                           .format(src, ', '.join(src_base)))
-      if m.group('optPreprocess'):
-        outf = outHelper.getOutput(out)
-        inf = open(realsrc)
-        pp = self.pp.clone()
-        if src[-4:] == '.css':
-          pp.setMarker('%')
-        pp.out = outf
-        pp.do_include(inf)
-        pp.warnUnused(realsrc)
-        outf.close()
-        inf.close()
-        return
-      # copy or symlink if newer or overwrite
-      if (m.group('optOverwrite')
-          or (getModTime(realsrc) >
-              outHelper.getDestModTime(m.group('output')))):
-        if self.outputFormat == 'symlink':
-          outHelper.symlink(realsrc, out)
-          return
-        outf = outHelper.getOutput(out)
-        # open in binary mode, this can be images etc
-        inf = open(realsrc, 'rb')
-        outf.write(inf.read())
-        outf.close()
-        inf.close()
+    class OutputHelper_flat(object):
+        '''Provide getDestModTime and getOutput for a given flat
+        output directory. The helper method ensureDirFor is used by
+        the symlink subclass.
+        '''
+
+        def __init__(self, basepath):
+            self.basepath = basepath
+
+        def getDestModTime(self, aPath):
+            return getModTime(os.path.join(self.basepath, aPath))
+
+        def getOutput(self, name):
+            out = self.ensureDirFor(name)
+
+            # remove previous link or file
+            try:
+                os.remove(out)
+            except OSError, e:
+                if e.errno != errno.ENOENT:
+                    raise
+            return open(out, 'wb')
+
+        def ensureDirFor(self, name):
+            out = os.path.join(self.basepath, name)
+            outdir = os.path.dirname(out)
+            if not os.path.isdir(outdir):
+                try:
+                    os.makedirs(outdir)
+                except OSError, error:
+                    if error.errno != errno.EEXIST:
+                        raise
+            return out
+
+    class OutputHelper_symlink(OutputHelper_flat):
+        '''Subclass of OutputHelper_flat that provides a helper for
+        creating a symlink including creating the parent directories.
+        '''
+
+        def symlink(self, src, dest):
+            out = self.ensureDirFor(dest)
+
+            # remove previous link or file
+            try:
+                os.remove(out)
+            except OSError, 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)
+                if rv == 0:
+                    raise WinError()
 
 
-  class OutputHelper_jar(object):
-    '''Provide getDestModTime and getOutput for a given jarfile.
-    '''
-    def __init__(self, jarfile):
-      self.jarfile = jarfile
-    def getDestModTime(self, aPath):
-      try :
-        info = self.jarfile.getinfo(aPath)
-        return info.date_time
-      except:
-        return 0
-    def getOutput(self, name):
-      return ZipEntry(name, self.jarfile)
-
-  class OutputHelper_flat(object):
-    '''Provide getDestModTime and getOutput for a given flat
-    output directory. The helper method ensureDirFor is used by
-    the symlink subclass.
-    '''
-    def __init__(self, basepath):
-      self.basepath = basepath
-    def getDestModTime(self, aPath):
-      return getModTime(os.path.join(self.basepath, aPath))
-    def getOutput(self, name):
-      out = self.ensureDirFor(name)
-      # remove previous link or file
-      try:
-        os.remove(out)
-      except OSError as e:
-        if e.errno != errno.ENOENT:
-          raise
-      return open(out, 'wb')
-    def ensureDirFor(self, name):
-      out = os.path.join(self.basepath, name)
-      outdir = os.path.dirname(out)
-      if not os.path.isdir(outdir):
-        try:
-          os.makedirs(outdir)
-        except OSError as error:
-          if error.errno != errno.EEXIST:
-            raise
-      return out
-
-  class OutputHelper_symlink(OutputHelper_flat):
-    '''Subclass of OutputHelper_flat that provides a helper for
-    creating a symlink including creating the parent directories.
-    '''
-    def symlink(self, src, dest):
-      out = self.ensureDirFor(dest)
-      # remove previous link or file
-      try:
-        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)
-        if rv == 0:
-          raise WinError()
-
 def main(args=None):
-  args = args or sys.argv
-  jm = JarMaker()
-  p = jm.getCommandLineParser()
-  (options, args) = p.parse_args(args)
-  jm.processIncludes(options.I)
-  jm.outputFormat = options.f
-  jm.sourcedirs = options.s
-  jm.topsourcedir = options.t
-  if options.e:
-    jm.useChromeManifest = True
-    jm.useJarfileManifest = False
-  if options.bothManifests:
-    jm.useChromeManifest = True
-    jm.useJarfileManifest = True
-  if options.l10n_base:
-    if not options.relativesrcdir:
-      p.error('relativesrcdir required when using l10n-base')
-    if options.l10n_src:
-      p.error('both l10n-src and l10n-base are not supported')
-    jm.l10nbase = options.l10n_base
-    jm.relativesrcdir = options.relativesrcdir
-    jm.l10nmerge = options.locale_mergedir
-    if jm.l10nmerge and not os.path.isdir(jm.l10nmerge):
-      logging.warning("WARNING: --locale-mergedir passed, but '%s' does not "
-                      "exist. Ignore this message if the locale is complete.")
-  elif options.locale_mergedir:
-    p.error('l10n-base required when using locale-mergedir')
-  jm.localedirs = options.l10n_src
-  if options.root_manifest_entry_appid:
-    jm.rootManifestAppId = options.root_manifest_entry_appid
-  noise = logging.INFO
-  if options.verbose is not None:
-    noise = (options.verbose and logging.DEBUG) or logging.WARN
-  if sys.version_info[:2] > (2,3):
-    logging.basicConfig(format = "%(message)s")
-  else:
-    logging.basicConfig()
-  logging.getLogger().setLevel(noise)
-  topsrc = options.t
-  topsrc = os.path.normpath(os.path.abspath(topsrc))
-  if not args:
-    infile = sys.stdin
-  else:
-    infile,  = args
-  jm.makeJar(infile, options.j)
+    args = args or sys.argv
+    jm = JarMaker()
+    p = jm.getCommandLineParser()
+    (options, args) = p.parse_args(args)
+    jm.processIncludes(options.I)
+    jm.outputFormat = options.f
+    jm.sourcedirs = options.s
+    jm.topsourcedir = options.t
+    if options.e:
+        jm.useChromeManifest = True
+        jm.useJarfileManifest = False
+    if options.bothManifests:
+        jm.useChromeManifest = True
+        jm.useJarfileManifest = True
+    if options.l10n_base:
+        if not options.relativesrcdir:
+            p.error('relativesrcdir required when using l10n-base')
+        if options.l10n_src:
+            p.error('both l10n-src and l10n-base are not supported')
+        jm.l10nbase = options.l10n_base
+        jm.relativesrcdir = options.relativesrcdir
+        jm.l10nmerge = options.locale_mergedir
+        if jm.l10nmerge and not os.path.isdir(jm.l10nmerge):
+            logging.warning("WARNING: --locale-mergedir passed, but '%s' does not exist. Ignore this message if the locale is complete."
+                            )
+    elif options.locale_mergedir:
+        p.error('l10n-base required when using locale-mergedir')
+    jm.localedirs = options.l10n_src
+    if options.root_manifest_entry_appid:
+        jm.rootManifestAppId = options.root_manifest_entry_appid
+    noise = logging.INFO
+    if options.verbose is not None:
+        noise = options.verbose and logging.DEBUG or logging.WARN
+    if sys.version_info[:2] > (2, 3):
+        logging.basicConfig(format='%(message)s')
+    else:
+        logging.basicConfig()
+    logging.getLogger().setLevel(noise)
+    topsrc = options.t
+    topsrc = os.path.normpath(os.path.abspath(topsrc))
+    if not args:
+        infile = sys.stdin
+    else:
+        (infile, ) = args
+    jm.makeJar(infile, options.j)