Bug 887121 - Make packager install and szip .so libraries in assets/ directly. r=glandium

This adds a Component type to the mozbuild.mozpack package manifest
parser, and teaches the packager to accept components of the form
[name destdir="dir"].  Then we update the Android package manifest and
simplify the packager code.

I would have liked to make the packager put mozglue.so and
MOZ_CHILD_PROCESS_NAME in lib/$(ABI_DIR) directly, but this turned out
to be awkward.  Since MOZ_CHILD_PROCESS_NAME needs to have lib/ in its
name to load successfully on Android, we would have to add notation in
package manifests to install bin/lib/*plugin-container* to
lib/$(ABI_DIR)/*plugin-container*.
This commit is contained in:
Nick Alexander 2013-06-28 09:10:57 -07:00
parent 0e66954343
commit 66582b795d
4 changed files with 228 additions and 57 deletions

View File

@ -30,8 +30,7 @@
@BINPATH@/dictionaries/*
@BINPATH@/hyphenation/*
[xpcom]
@BINPATH@/dependentlibs.list
[assets destdir="assets"]
#ifndef MOZ_STATIC_JS
@BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
#endif
@ -41,7 +40,6 @@
@BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
#endif
@BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@omxplugin@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@omxplugingb@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@omxplugingb235@DLL_SUFFIX@
@ -50,6 +48,34 @@
@BINPATH@/@DLL_PREFIX@omxpluginsony@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@omxpluginfroyo@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
#endif
@BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@freebl3@DLL_SUFFIX@
#ifndef CROSS_COMPILE
@BINPATH@/@DLL_PREFIX@freebl3.chk
@BINPATH@/@DLL_PREFIX@softokn3.chk
#endif
#ifndef NSS_DISABLE_DBM
@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#ifndef CROSS_COMPILE
@BINPATH@/@DLL_PREFIX@nssdbm3.chk
#endif
#endif
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
#endif
[xpcom]
@BINPATH@/dependentlibs.list
@BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@
@BINPATH@/AndroidManifest.xml
@ -72,9 +98,6 @@
#endif
@BINPATH@/application.ini
@BINPATH@/platform.ini
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
#endif
@BINPATH@/blocklist.xml
#ifdef XP_UNIX
@BINPATH@/run-mozilla.sh
@ -478,28 +501,9 @@
; [Personal Security Manager]
;
@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
@BINPATH@/components/pipboot.xpt
@BINPATH@/components/pipnss.xpt
@BINPATH@/components/pippki.xpt
@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
#endif
@BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@freebl3@DLL_SUFFIX@
#ifndef CROSS_COMPILE
@BINPATH@/@DLL_PREFIX@freebl3.chk
@BINPATH@/@DLL_PREFIX@softokn3.chk
#endif
#ifndef NSS_DISABLE_DBM
@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#ifndef CROSS_COMPILE
@BINPATH@/@DLL_PREFIX@nssdbm3.chk
#endif
#endif
@BINPATH@/chrome/pippki@JAREXT@
@BINPATH@/chrome/pippki.manifest

View File

@ -17,6 +17,108 @@ import mozpack.path
from collections import deque
class Component(object):
'''
Class that represents a component in a package manifest.
'''
def __init__(self, name, destdir=''):
if name.find(' ') > 0:
errors.fatal('Malformed manifest: space in component name "%s"'
% component)
self._name = name
self._destdir = destdir
def __repr__(self):
s = self.name
if self.destdir:
s += ' destdir="%s"' % self.destdir
return s
@property
def name(self):
return self._name
@property
def destdir(self):
return self._destdir
@staticmethod
def _triples(lst):
'''
Split [1, 2, 3, 4, 5, 6, 7] into [(1, 2, 3), (4, 5, 6)].
'''
return zip(*[iter(lst)] * 3)
KEY_VALUE_RE = re.compile(r'''
\s* # optional whitespace.
([a-zA-Z0-9_]+) # key.
\s*=\s* # optional space around =.
"([^"]*)" # value without surrounding quotes.
(?:\s+|$)
''', re.VERBOSE)
@staticmethod
def _split_options(string):
'''
Split 'key1="value1" key2="value2"' into
{'key1':'value1', 'key2':'value2'}.
Returned keys and values are all strings.
Throws ValueError if the input is malformed.
'''
options = {}
splits = Component.KEY_VALUE_RE.split(string)
if len(splits) % 3 != 1:
# This should never happen -- we expect to always split
# into ['', ('key', 'val', '')*].
raise ValueError("Bad input")
if splits[0]:
raise ValueError('Unrecognized input ' + splits[0])
for key, val, no_match in Component._triples(splits[1:]):
if no_match:
raise ValueError('Unrecognized input ' + no_match)
options[key] = val
return options
@staticmethod
def _split_component_and_options(string):
'''
Split 'name key1="value1" key2="value2"' into
('name', {'key1':'value1', 'key2':'value2'}).
Returned name, keys and values are all strings.
Raises ValueError if the input is malformed.
'''
splits = string.strip().split(None, 1)
if not splits:
raise ValueError('No component found')
component = splits[0].strip()
if not component:
raise ValueError('No component found')
if not re.match('[a-zA-Z0-9_\-]+$', component):
raise ValueError('Bad component name ' + component)
options = Component._split_options(splits[1]) if len(splits) > 1 else {}
return component, options
@staticmethod
def from_string(string):
'''
Create a component from a string.
'''
try:
name, options = Component._split_component_and_options(string)
except ValueError as e:
errors.fatal('Malformed manifest: %s' % e)
return
destdir = options.pop('destdir', '')
if options:
errors.fatal('Malformed manifest: options %s not recognized'
% options.keys())
return Component(name, destdir=destdir)
class PackageManifestParser(object):
'''
Class for parsing of a package manifest, after preprocessing.
@ -29,14 +131,16 @@ class PackageManifestParser(object):
; file comment
The parser takes input from the preprocessor line by line, and pushes
parsed information to a sink object. The add and remove methods of the
sink object are called with the current component and a path.
parsed information to a sink object.
The add and remove methods of the sink object are called with the
current Component instance and a path.
'''
def __init__(self, sink):
'''
Initialize the package manifest parser with the given sink.
'''
self._component = ''
self._component = Component('')
self._sink = sink
def handle_line(self, str):
@ -49,10 +153,7 @@ class PackageManifestParser(object):
if not str or str.startswith(';'):
return
if str.startswith('[') and str.endswith(']'):
if str == '[]' or re.search(r'[\[\]\s]', str[1:-1]):
errors.fatal('Malformed manifest')
else:
self._component = str[1:-1]
self._component = Component.from_string(str[1:-1])
elif str.startswith('-'):
str = str[1:]
self._sink.remove(self._component, str)
@ -227,9 +328,9 @@ class SimpleManifestSink(object):
return mozpack.path.relpath(path, 'bin')
return path
def add(self, section, pattern):
def add(self, component, pattern):
'''
Add files with the given pattern.
Add files with the given pattern in the given component.
'''
assert not self._closed
added = False
@ -237,11 +338,15 @@ class SimpleManifestSink(object):
added = True
if is_manifest(p):
self._manifests.add(p)
self.packager.add(SimpleManifestSink.normalize_path(p), f)
dest = mozpack.path.join(component.destdir, SimpleManifestSink.normalize_path(p))
self.packager.add(dest, f)
if not added:
errors.error('Missing file(s): %s' % pattern)
def remove(self, section, pattern):
def remove(self, component, pattern):
'''
Remove files with the given pattern in the given component.
'''
assert not self._closed
errors.fatal('Removal is unsupported')

View File

@ -7,9 +7,10 @@ import mozunit
import os
from mozpack.packager import (
preprocess_manifest,
CallDeque,
Component,
SimplePackager,
SimpleManifestSink,
CallDeque,
)
from mozpack.files import GeneratedFile
from mozpack.chrome.manifest import (
@ -30,6 +31,8 @@ bar/*
foo/*
-foo/bar
chrome.manifest
[zot destdir="destdir"]
foo/zot
; comment
#ifdef baz
[baz]
@ -45,7 +48,8 @@ class TestPreprocessManifest(unittest.TestCase):
((MANIFEST_PATH, 2), 'add', '', 'bar/*'),
((MANIFEST_PATH, 4), 'add', 'foo', 'foo/*'),
((MANIFEST_PATH, 5), 'remove', 'foo', 'foo/bar'),
((MANIFEST_PATH, 6), 'add', 'foo', 'chrome.manifest')
((MANIFEST_PATH, 6), 'add', 'foo', 'chrome.manifest'),
((MANIFEST_PATH, 8), 'add', 'zot destdir="destdir"', 'foo/zot'),
]
def setUp(self):
@ -53,11 +57,11 @@ class TestPreprocessManifest(unittest.TestCase):
def __init__(self):
self.log = []
def add(self, section, path):
self._log(errors.get_context(), 'add', section, path)
def add(self, component, path):
self._log(errors.get_context(), 'add', repr(component), path)
def remove(self, section, path):
self._log(errors.get_context(), 'remove', section, path)
def remove(self, component, path):
self._log(errors.get_context(), 'remove', repr(component), path)
def _log(self, *args):
self.log.append(args)
@ -84,7 +88,7 @@ class TestPreprocessManifest(unittest.TestCase):
preprocess_manifest(self.sink, 'manifest',
{'baz': 1, 'SUFFIX': '.exe'})
self.assertEqual(self.sink.log, self.EXPECTED_LOG +
[((self.MANIFEST_PATH, 10), 'add', 'baz', 'baz.exe')])
[((self.MANIFEST_PATH, 12), 'add', 'baz', 'baz.exe')])
class MockFinder(object):
@ -199,19 +203,25 @@ class TestSimpleManifestSink(unittest.TestCase):
foobar = GeneratedFile('foobar')
foobaz = GeneratedFile('foobaz')
fooqux = GeneratedFile('fooqux')
foozot = GeneratedFile('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/chrome.manifest':
GeneratedFile('manifest foo/chrome.manifest'),
})
parser = SimpleManifestSink(finder, formatter)
parser.add('section0', 'bin/foo/b*')
parser.add('section1', 'bin/foo/qux')
parser.add('section1', 'bin/foo/chrome.manifest')
self.assertRaises(ErrorMessage, parser.add, 'section1', 'bin/bar')
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')
parser.add(component2, 'bin/foo/zot')
self.assertRaises(ErrorMessage, parser.add, 'component1', 'bin/bar')
self.assertEqual(formatter.log, [])
parser.close()
@ -221,12 +231,14 @@ class TestSimpleManifestSink(unittest.TestCase):
(None, 'add', 'foo/bar', foobar),
(None, 'add', 'foo/baz', foobaz),
(None, 'add', 'foo/qux', fooqux),
(None, 'add', 'destdir/foo/zot', foozot),
])
self.assertEqual(finder.log, [
'bin/foo/b*',
'bin/foo/qux',
'bin/foo/chrome.manifest',
'bin/foo/zot',
'bin/bar',
'bin/chrome.manifest'
])
@ -258,5 +270,59 @@ class TestCallDeque(unittest.TestCase):
d.execute()
self.assertEqual(logger._log, ['foo', 'bar', 'baz', 'qux'])
class TestComponent(unittest.TestCase):
def do_split(self, string, name, options):
n, o = Component._split_component_and_options(string)
self.assertEqual(name, n)
self.assertEqual(options, o)
def test_component_split_component_and_options(self):
self.do_split('component', 'component', {})
self.do_split('trailingspace ', 'trailingspace', {})
self.do_split(' leadingspace', 'leadingspace', {})
self.do_split(' trim ', 'trim', {})
self.do_split(' trim key="value"', 'trim', {'key':'value'})
self.do_split(' trim empty=""', 'trim', {'empty':''})
self.do_split(' trim space=" "', 'trim', {'space':' '})
self.do_split('component key="value" key2="second" ',
'component', {'key':'value', 'key2':'second'})
self.do_split( 'trim key=" value with spaces " key2="spaces again"',
'trim', {'key':' value with spaces ', 'key2': 'spaces again'})
def do_split_error(self, string):
self.assertRaises(ValueError, Component._split_component_and_options, string)
def test_component_split_component_and_options_errors(self):
self.do_split_error('"component')
self.do_split_error('comp"onent')
self.do_split_error('component"')
self.do_split_error('"component"')
self.do_split_error('=component')
self.do_split_error('comp=onent')
self.do_split_error('component=')
self.do_split_error('key="val"')
self.do_split_error('component key=')
self.do_split_error('component key="val')
self.do_split_error('component key=val"')
self.do_split_error('component key="val" x')
self.do_split_error('component x key="val"')
self.do_split_error('component key1="val" x key2="val"')
def do_from_string(self, string, name, destdir=''):
component = Component.from_string(string)
self.assertEqual(name, component.name)
self.assertEqual(destdir, component.destdir)
def test_component_from_string(self):
self.do_from_string('component', 'component')
self.do_from_string('component-with-hyphen', 'component-with-hyphen')
self.do_from_string('component destdir="foo/bar"', 'component', 'foo/bar')
self.do_from_string('component destdir="bar spc"', 'component', 'bar spc')
self.assertRaises(ErrorMessage, Component.from_string, '')
self.assertRaises(ErrorMessage, Component.from_string, 'component novalue=')
self.assertRaises(ErrorMessage, Component.from_string, 'component badoption=badvalue')
if __name__ == '__main__':
mozunit.main()

View File

@ -345,17 +345,16 @@ ifdef MOZ_OMX_PLUGIN
DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginsony.so libomxpluginfroyo.so libomxpluginjb-htc.so
endif
SO_LIBRARIES := $(filter-out $(MOZ_CHILD_PROCESS_NAME),$(filter %.so,$(DIST_FILES)))
# These libraries are moved into the assets/ directory of the APK.
ASSET_SO_LIBRARIES := $(addprefix assets/,$(SO_LIBRARIES))
SO_LIBRARIES := $(filter %.so,$(DIST_FILES))
# These libraries are placed in the assets/ directory by packager.py.
ASSET_SO_LIBRARIES := $(addprefix assets/,$(filter-out libmozglue.so $(MOZ_CHILD_PROCESS_NAME),$(SO_LIBRARIES)))
DIST_FILES := $(filter-out $(SO_LIBRARIES),$(DIST_FILES))
NON_DIST_FILES += $(SO_LIBARIES)
NON_DIST_FILES += libmozglue.so $(MOZ_CHILD_PROCESS_NAME) $(ASSET_SO_LIBRARIES)
ifdef MOZ_ENABLE_SZIP
# These libraries are szipped (before being moved into the assets/
# directory of the APK).
SZIP_LIBRARIES := $(SO_LIBRARIES)
# These libraries are szipped in-place in the assets/ directory.
SZIP_LIBRARIES := $(ASSET_SO_LIBRARIES)
endif
PKG_SUFFIX = .apk
@ -364,10 +363,8 @@ INNER_MAKE_PACKAGE = \
make -C $(GECKO_APP_AP_PATH) gecko.ap_ && \
cp $(GECKO_APP_AP_PATH)/gecko.ap_ $(_ABS_DIST) && \
( cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH) && \
mkdir -p assets && \
mkdir -p lib/$(ABI_DIR) && \
mv libmozglue.so $(MOZ_CHILD_PROCESS_NAME) lib/$(ABI_DIR) && \
mv $(SO_LIBRARIES) assets && \
unzip -o $(_ABS_DIST)/gecko.ap_ && \
rm $(_ABS_DIST)/gecko.ap_ && \
$(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(ASSET_SO_LIBRARIES) && \
@ -387,7 +384,6 @@ INNER_UNMAKE_PACKAGE = \
$(UNZIP) $(UNPACKAGE) && \
mv lib/$(ABI_DIR)/libmozglue.so . && \
mv lib/$(ABI_DIR)/*plugin-container* $(MOZ_CHILD_PROCESS_NAME) && \
mv $(ASSET_SO_LIBRARIES) . && \
rm -rf lib/$(ABI_DIR) )
endif