mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1210687 - Separate out jar.mn parsing in a separate class. r=gps
This commit is contained in:
parent
ccf3953f83
commit
e127dc20b1
@ -18,16 +18,17 @@ import logging
|
||||
from time import localtime
|
||||
from MozZipFile import ZipFile
|
||||
from cStringIO import StringIO
|
||||
from collections import defaultdict
|
||||
|
||||
from mozbuild.util import (
|
||||
ensureParentDir,
|
||||
lock_file,
|
||||
PushbackIter,
|
||||
)
|
||||
|
||||
from mozbuild.preprocessor import Preprocessor
|
||||
from mozbuild.action.buildlist import addEntriesToListFile
|
||||
from mozpack.files import FileFinder
|
||||
import mozpack.path as mozpath
|
||||
if sys.platform == 'win32':
|
||||
from ctypes import windll, WinError
|
||||
CreateHardLink = windll.kernel32.CreateHardLinkA
|
||||
@ -67,13 +68,28 @@ def getModTime(aPath):
|
||||
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.
|
||||
'''
|
||||
class JarManifestEntry(object):
|
||||
def __init__(self, output, source, is_locale=False, preprocess=False,
|
||||
overwrite=False):
|
||||
self.output = output
|
||||
self.source = source
|
||||
self.is_locale = is_locale
|
||||
self.preprocess = preprocess
|
||||
self.overwrite = overwrite
|
||||
|
||||
|
||||
class JarInfo(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.relativesrcdir = None
|
||||
self.chrome_manifests = []
|
||||
self.entries = []
|
||||
|
||||
|
||||
class JarManifestParser(object):
|
||||
|
||||
ignore = re.compile('\s*(\#.*)?$')
|
||||
jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/{}]+).jar\:)|(?:\s*(\#.*)?)\s*$')
|
||||
jarline = re.compile('(?P<jarfile>[\w\d.\-\_\\\/{}]+).jar\:$')
|
||||
relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
|
||||
regline = re.compile('\%\s+(.*)$')
|
||||
entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
|
||||
@ -81,6 +97,85 @@ class JarMaker(object):
|
||||
+ '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@\*]+)\))?\s*$'
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self._current_jar = None
|
||||
self._jars = []
|
||||
|
||||
def write(self, line):
|
||||
# A Preprocessor instance feeds the parser through calls to this method.
|
||||
|
||||
# Ignore comments and empty lines
|
||||
if self.ignore.match(line):
|
||||
return
|
||||
|
||||
# A jar manifest file can declare several different sections, each of
|
||||
# which applies to a given "jar file". Each of those sections starts
|
||||
# with "<name>.jar:".
|
||||
if self._current_jar is None:
|
||||
m = self.jarline.match(line)
|
||||
if not m:
|
||||
raise RuntimeError(line)
|
||||
self._current_jar = JarInfo(m.group('jarfile'))
|
||||
self._jars.append(self._current_jar)
|
||||
return
|
||||
|
||||
# Within each section, there can be three different types of entries:
|
||||
|
||||
# - indications of the relative source directory we pretend to be in
|
||||
# when considering localization files, in the following form;
|
||||
# "relativesrcdir <path>:"
|
||||
m = self.relsrcline.match(line)
|
||||
if m:
|
||||
if self._current_jar.chrome_manifests or self._current_jar.entries:
|
||||
self._current_jar = JarInfo(self._current_jar.name)
|
||||
self._jars.append(self._current_jar)
|
||||
self._current_jar.relativesrcdir = m.group('relativesrcdir')
|
||||
return
|
||||
|
||||
# - chrome manifest entries, prefixed with "%".
|
||||
m = self.regline.match(line)
|
||||
if m:
|
||||
rline = m.group(1)
|
||||
if rline not in self._current_jar.chrome_manifests:
|
||||
self._current_jar.chrome_manifests.append(rline)
|
||||
return
|
||||
|
||||
# - entries indicating files to be part of the given jar. They are
|
||||
# formed thusly:
|
||||
# "<dest_path>"
|
||||
# or
|
||||
# "<dest_path> (<source_path>)"
|
||||
# The <dest_path> is where the file(s) will be put in the chrome jar.
|
||||
# The <source_path> is where the file(s) can be found in the source
|
||||
# directory. The <source_path> may start with a "%" for files part
|
||||
# of a localization directory, in which case the "%" counts as the
|
||||
# locale.
|
||||
# Each entry can be prefixed with "*" for preprocessing and "+" to
|
||||
# always overwrite the destination independently of file timestamps
|
||||
# (the usefulness of the latter is dubious in the modern days).
|
||||
m = self.entryline.match(line)
|
||||
if m:
|
||||
self._current_jar.entries.append(JarManifestEntry(
|
||||
m.group('output'),
|
||||
m.group('source') or mozpath.basename(m.group('output')),
|
||||
is_locale=bool(m.group('locale')),
|
||||
preprocess=bool(m.group('optPreprocess')),
|
||||
overwrite=bool(m.group('optOverwrite')),
|
||||
))
|
||||
return
|
||||
|
||||
self._current_jar = None
|
||||
self.write(line)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._jars)
|
||||
|
||||
|
||||
class JarMaker(object):
|
||||
'''JarMaker reads jar.mn files and process those into jar files or
|
||||
flat directories, along with chrome.manifest files.
|
||||
'''
|
||||
|
||||
def __init__(self, outputFormat='flat', useJarfileManifest=True,
|
||||
useChromeManifest=False):
|
||||
|
||||
@ -205,7 +300,7 @@ class JarMaker(object):
|
||||
lock = lock_file(manifestPath + '.lck')
|
||||
try:
|
||||
myregister = dict.fromkeys(map(lambda s: s.replace('%',
|
||||
chromebasepath), register.iterkeys()))
|
||||
chromebasepath), register))
|
||||
manifestExists = os.path.isfile(manifestPath)
|
||||
mode = manifestExists and 'r+b' or 'wb'
|
||||
mf = open(manifestPath, mode)
|
||||
@ -243,24 +338,11 @@ class JarMaker(object):
|
||||
logging.info('processing ' + infile)
|
||||
self.sourcedirs.append(_normpath(os.path.dirname(infile)))
|
||||
pp = self.pp.clone()
|
||||
pp.out = StringIO()
|
||||
pp.out = JarManifestParser()
|
||||
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
|
||||
|
||||
for info in pp.out:
|
||||
self.processJarSection(info, jardir)
|
||||
|
||||
def generateLocaleDirs(self, relativesrcdir):
|
||||
if os.path.basename(relativesrcdir) == 'locales':
|
||||
@ -281,25 +363,21 @@ class JarMaker(object):
|
||||
relativesrcdir, 'en-US'))
|
||||
return locdirs
|
||||
|
||||
def processJarSection(self, jarfile, lines, jardir):
|
||||
def processJarSection(self, jarinfo, 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)
|
||||
chromebasepath = '{0}' + os.path.basename(jarinfo.name)
|
||||
if self.outputFormat == 'jar':
|
||||
chromebasepath = 'jar:' + chromebasepath + '.jar!'
|
||||
chromebasepath += '/'
|
||||
|
||||
jarfile = os.path.join(jardir, jarfile)
|
||||
jarfile = os.path.join(jardir, jarinfo.name)
|
||||
jf = None
|
||||
if self.outputFormat == 'jar':
|
||||
# jar
|
||||
@ -314,59 +392,24 @@ class JarMaker(object):
|
||||
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
|
||||
if jarinfo.relativesrcdir:
|
||||
self.localedirs = self.generateLocaleDirs(jarinfo.relativesrcdir)
|
||||
|
||||
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()
|
||||
for e in jarinfo.entries:
|
||||
self._processEntryLine(e, outHelper, jf)
|
||||
|
||||
# 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
|
||||
self.finalizeJar(jarfile, chromebasepath, jarinfo.chrome_manifests)
|
||||
if jf is not None:
|
||||
jf.close()
|
||||
|
||||
def _processEntryLine(self, m, outHelper, jf):
|
||||
out = m.group('output')
|
||||
src = m.group('source') or os.path.basename(out)
|
||||
def _processEntryLine(self, e, outHelper, jf):
|
||||
out = e.output
|
||||
src = e.source
|
||||
|
||||
# pick the right sourcedir -- l10n, topsrc or src
|
||||
|
||||
if m.group('locale'):
|
||||
if e.is_locale:
|
||||
src_base = self.localedirs
|
||||
elif src.startswith('/'):
|
||||
# path/in/jar/file_name.xul (/path/in/sourcetree/file_name.xul)
|
||||
@ -379,26 +422,22 @@ class JarMaker(object):
|
||||
src_base = self.sourcedirs + [os.getcwd()]
|
||||
|
||||
if '*' in src:
|
||||
if not out.endswith('/'):
|
||||
out += '/'
|
||||
def _prefix(s):
|
||||
for p in s.split('/'):
|
||||
if '*' not in p:
|
||||
yield p + '/'
|
||||
prefix = ''.join(_prefix(src))
|
||||
fmt = '%s%s %s%%s (%s%%s)' % (
|
||||
m.group('optPreprocess') or '',
|
||||
m.group('optOverwrite') or '',
|
||||
out,
|
||||
m.group('locale').replace('%', '%%') or '',
|
||||
)
|
||||
for _srcdir in src_base:
|
||||
finder = FileFinder(_srcdir, find_executables=False)
|
||||
for path, _ in finder.find(src):
|
||||
line = fmt % (path[len(prefix):], path)
|
||||
m = self.entryline.match(line)
|
||||
if m:
|
||||
self._processEntryLine(m, outHelper, jf)
|
||||
e = JarManifestEntry(
|
||||
mozpath.join(out, path[len(prefix):]),
|
||||
path,
|
||||
is_locale=e.is_locale,
|
||||
preprocess=e.preprocess,
|
||||
overwrite=e.overwrite
|
||||
)
|
||||
self._processEntryLine(e, outHelper, jf)
|
||||
return
|
||||
|
||||
# check if the source file exists
|
||||
@ -412,7 +451,7 @@ class JarMaker(object):
|
||||
jf.close()
|
||||
raise RuntimeError('File "{0}" not found in {1}'.format(src,
|
||||
', '.join(src_base)))
|
||||
if m.group('optPreprocess'):
|
||||
if e.preprocess:
|
||||
outf = outHelper.getOutput(out)
|
||||
inf = open(realsrc)
|
||||
pp = self.pp.clone()
|
||||
@ -427,8 +466,8 @@ class JarMaker(object):
|
||||
|
||||
# copy or symlink if newer or overwrite
|
||||
|
||||
if m.group('optOverwrite') or getModTime(realsrc) \
|
||||
> outHelper.getDestModTime(m.group('output')):
|
||||
if e.overwrite or getModTime(realsrc) \
|
||||
> outHelper.getDestModTime(e.output):
|
||||
if self.outputFormat == 'symlink':
|
||||
outHelper.symlink(realsrc, out)
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user