OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 # On Android we build unit test bundles as shared libraries. To run | |
7 # tests, we launch a special "test runner" apk which loads the library | |
8 # then jumps into it. Since java is required for many tests | |
9 # (e.g. PathUtils.java), a "pure native" test bundle is inadequate. | |
10 # | |
11 # This script, generate_native_test.py, is used to generate the source | |
12 # for an apk that wraps a unit test shared library bundle. That | |
13 # allows us to have a single boiler-plate application be used across | |
14 # all unit test bundles. | |
bulach
2012/04/12 15:51:42
as above, I'd suggest calling ant and building the
| |
15 | |
16 import logging | |
17 import optparse | |
18 import os | |
19 import re | |
20 import shutil | |
21 import sys | |
22 | |
23 | |
24 class NativeTestApkGenerator(object): | |
25 """Generate a native test apk source tree. | |
26 | |
27 TODO(jrg): develop this more so the activity name is replaced as | |
28 well. That will allow multiple test runners to be installed at the | |
29 same time. (The complication is that it involves renaming a java | |
30 class, which implies regeneration of a jni header, and on and on...) | |
31 """ | |
32 | |
33 # Files or directories we need to copy to create a complete apk test shell. | |
34 _SOURCE_FILES = ['AndroidManifest.xml', | |
35 'native_test_apk.xml', | |
36 'res', # res/values/strings.xml | |
37 'java', # .../ChromeNativeTestActivity.java | |
38 ] | |
39 | |
40 # Files in the destion directory that have a "replaceme" string | |
41 # which should be replaced by the basename of the shared library. | |
42 # Note we also update the filename if 'replaceme' is itself found in | |
43 # the filename. | |
44 _REPLACEME_FILES = ['AndroidManifest.xml', | |
45 'native_test_apk.xml', | |
46 'res/values/strings.xml'] | |
47 | |
48 def __init__(self, native_library, jars, output_directory): | |
49 self._native_library = native_library | |
50 self._jars = jars | |
51 self._output_directory = output_directory | |
52 self._root_name = None | |
53 if self._native_library: | |
54 self._root_name = self._LibraryRoot() | |
55 logging.warn('root name: %s' % self._root_name) | |
56 | |
57 | |
58 def _LibraryRoot(self): | |
59 """Return a root name for a shared library. | |
60 | |
61 The root name should be suitable for substitution in apk files | |
62 like the manifest. For example, blah/foo/libbase_unittests.so | |
63 becomes base_unittests. | |
64 """ | |
65 rootfinder = re.match('.?lib(.+).so', | |
66 os.path.basename(self._native_library)) | |
67 if rootfinder: | |
68 return rootfinder.group(1) | |
69 else: | |
70 return None | |
71 | |
72 def _CopyTemplateFiles(self): | |
73 """Copy files needed to build a new apk. | |
74 | |
75 TODO(jrg): add more smarts so we don't change file timestamps if | |
76 the files don't change? | |
77 """ | |
78 srcdir = os.path.dirname(os.path.realpath( __file__)) | |
79 if not os.path.exists(self._output_directory): | |
80 os.makedirs(self._output_directory) | |
81 for f in self._SOURCE_FILES: | |
82 src = os.path.join(srcdir, f) | |
83 dest = os.path.join(self._output_directory, f) | |
84 if os.path.isfile(src): | |
85 if os.path.exists(dest): | |
86 os.remove(dest) | |
87 logging.warn('%s --> %s' % (src, dest)) | |
88 shutil.copyfile(src, dest) | |
89 else: # directory | |
90 if os.path.exists(dest): | |
91 # One more sanity check since we're deleting a directory... | |
92 if not '/out/' in dest: | |
93 raise Exception('Unbelievable output directory; bailing for safety') | |
bulach
2012/04/12 15:51:42
nit: probably best to do this sanity check at the
John Grabowski
2012/04/13 01:11:37
Makes me feel safer to do it "right before" the rm
| |
94 shutil.rmtree(dest) | |
95 logging.warn('%s --> %s' % (src, dest)) | |
96 shutil.copytree(src, dest) | |
97 | |
98 def _ReplaceStrings(self): | |
99 """Replace 'replaceme' strings in generated files with a root libname. | |
100 | |
101 If we have no root libname (e.g. no shlib was specified), do nothing. | |
102 """ | |
103 if not self._root_name: | |
104 return | |
105 logging.warn('Replacing \'replaceme\' with ' + self._root_name) | |
bulach
2012/04/12 15:51:42
nit: use " inside the string, avoids escaping..
John Grabowski
2012/04/13 01:11:37
Done.
| |
106 for f in self._REPLACEME_FILES: | |
107 dest = os.path.join(self._output_directory, f) | |
108 contents = open(dest).read() | |
109 contents = contents.replace('replaceme', self._root_name) | |
110 dest = dest.replace('replaceme', self._root_name) # update the filename! | |
111 open(dest, "w").write(contents) | |
112 | |
113 def _CopyLibraryAndJars(self): | |
114 """Copy the shlib and jars into the apk (if relevant)""" | |
bulach
2012/04/12 15:51:42
not sure what you mean "into the apk".. the apk it
John Grabowski
2012/04/13 01:11:37
clarified
| |
115 if self._native_library: | |
116 destdir = os.path.join(self._output_directory, 'libs/armeabi') | |
117 if not os.path.exists(destdir): | |
118 os.makedirs(destdir) | |
119 dest = os.path.join(destdir, os.path.basename(self._native_library)) | |
120 logging.warn('%s --> %s' % (self._native_library, dest)) | |
121 shutil.copyfile(self._native_library, dest) | |
122 if self._jars: | |
123 destdir = os.path.join(self._output_directory, 'libs') | |
124 if not os.path.exists(destdir): | |
125 os.makedirs(destdir) | |
126 for jar in self._jars.split(','): | |
127 dest = os.path.join(destdir, os.path.basename(jar)) | |
128 logging.warn('%s --> %s' % (jar, dest)) | |
129 shutil.copyfile(jar, dest) | |
130 | |
131 def CreateBundle(self): | |
132 self._CopyTemplateFiles() | |
133 self._ReplaceStrings() | |
134 self._CopyLibraryAndJars() | |
135 | |
136 | |
137 def main(argv): | |
138 parser = optparse.OptionParser() | |
139 parser.add_option('--verbose', | |
140 help='Be verbose') | |
141 parser.add_option('--native_library', | |
142 help='Full name of native shared library test bundle') | |
143 parser.add_option('--jars', | |
144 help='Comma seperated list of jars to include') | |
bulach
2012/04/12 15:51:42
nit: instead of splitting here, maybe just use --j
John Grabowski
2012/04/13 01:11:37
Done.
| |
145 parser.add_option('--output', | |
146 help='Output directory for generated files.') | |
147 options, args = parser.parse_args(argv) # pylint: disable=W0612 | |
bulach
2012/04/12 15:51:42
is W0612 unused locals? would replacing args with
John Grabowski
2012/04/13 01:11:37
Done.
| |
148 | |
149 # It is not an error to specify no native library; the apk should | |
150 # still be generated and build. It will, however, print | |
151 # NATIVE_LOADER_FAILED wnen run. | |
Yaron
2012/04/12 01:29:53
s/wnen/when
John Grabowski
2012/04/13 01:11:37
Done.
| |
152 if not options.output: | |
153 raise Exception('No output directory specified for generated files') | |
154 | |
155 if options.verbose: | |
156 logging.basicConfig(level=logging.DEBUG, format=' %(message)s') | |
157 | |
158 ntag = NativeTestApkGenerator(native_library=options.native_library, | |
159 jars=options.jars, | |
160 output_directory=options.output) | |
161 ntag.CreateBundle() | |
162 logging.warn('COMPLETE.') | |
163 | |
164 if __name__ == '__main__': | |
165 sys.exit(main(sys.argv)) | |
OLD | NEW |