Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: testing/android/generate_native_test.py

Issue 10662010: Use rsync to copy android testing directories (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # On Android we build unit test bundles as shared libraries. To run 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 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 8 # then jumps into it. Since java is required for many tests
9 # (e.g. PathUtils.java), a "pure native" test bundle is inadequate. 9 # (e.g. PathUtils.java), a "pure native" test bundle is inadequate.
10 # 10 #
11 # This script, generate_native_test.py, is used to generate the source 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 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 13 # allows us to have a single boiler-plate application be used across
14 # all unit test bundles. 14 # all unit test bundles.
15 15
16 import logging 16 import logging
17 import optparse 17 import optparse
18 import os 18 import os
19 import re 19 import re
20 import shutil 20 import shutil
21 import subprocess 21 import subprocess
22 import sys 22 import sys
23 23
24 # cmd_helper.py is under ../../build/android/ 24 # cmd_helper.py is under ../../build/android/
25 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 25 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..',
26 '..', 'build', 'android')) 26 '..', 'build', 'android')))
27 import cmd_helper # pylint: disable=F0401 27 import cmd_helper # pylint: disable=F0401
28 28
29 29
30 class NativeTestApkGenerator(object): 30 class NativeTestApkGenerator(object):
31 """Generate a native test apk source tree. 31 """Generate a native test apk source tree.
32 32
33 TODO(jrg): develop this more so the activity name is replaced as 33 TODO(jrg): develop this more so the activity name is replaced as
34 well. That will allow multiple test runners to be installed at the 34 well. That will allow multiple test runners to be installed at the
35 same time. (The complication is that it involves renaming a java 35 same time. (The complication is that it involves renaming a java
36 class, which implies regeneration of a jni header, and on and on...) 36 class, which implies regeneration of a jni header, and on and on...)
37 """ 37 """
38 38
39 # Files or directories we need to copy to create a complete apk test shell. 39 # Files or directories we need to copy to create a complete apk test shell.
40 _SOURCE_FILES = ['AndroidManifest.xml', 40 _SOURCE_FILES = ['AndroidManifest.xml',
41 'native_test_apk.xml', 41 'native_test_apk.xml',
42 'res', # res/values/strings.xml 42 'res', # res/values/strings.xml
43 'java', # .../ChromeNativeTestActivity.java 43 'java', # .../ChromeNativeTestActivity.java
44 ] 44 ]
45 45
46 # Files in the destion directory that have a "replaceme" string 46 # Files in the destion directory that have a "replaceme" string
47 # which should be replaced by the basename of the shared library. 47 # which should be replaced by the basename of the shared library.
48 # Note we also update the filename if 'replaceme' is itself found in 48 # Note we also update the filename if 'replaceme' is itself found in
49 # the filename. 49 # the filename.
50 _REPLACEME_FILES = ['AndroidManifest.xml', 50 _REPLACEME_FILES = ['AndroidManifest.xml',
51 'native_test_apk.xml', 51 'native_test_apk.xml',
52 'res/values/strings.xml'] 52 'res/values/strings.xml']
53 53
54 def __init__(self, native_library, jars, output_directory, target_abi): 54 def __init__(self, native_library, jars, output_directory, target_abi):
55 self._native_library = native_library 55 self._native_library = native_library
56 self._jars = jars 56 self._jars = jars
57 self._output_directory = output_directory 57 self._output_directory = os.path.abspath(output_directory)
58 self._target_abi = target_abi 58 self._target_abi = target_abi
59 self._root_name = None 59 self._root_name = None
60 if self._native_library: 60 if self._native_library:
61 self._root_name = self._LibraryRoot() 61 self._root_name = self._LibraryRoot()
62 logging.warn('root name: %s' % self._root_name) 62 logging.warn('root name: %s', self._root_name)
63
64 63
65 def _LibraryRoot(self): 64 def _LibraryRoot(self):
66 """Return a root name for a shared library. 65 """Return a root name for a shared library.
67 66
68 The root name should be suitable for substitution in apk files 67 The root name should be suitable for substitution in apk files
69 like the manifest. For example, blah/foo/libbase_unittests.so 68 like the manifest. For example, blah/foo/libbase_unittests.so
70 becomes base_unittests. 69 becomes base_unittests.
71 """ 70 """
72 rootfinder = re.match('.?lib(.+).so', 71 rootfinder = re.match('.?lib(.+).so',
73 os.path.basename(self._native_library)) 72 os.path.basename(self._native_library))
74 if rootfinder: 73 if rootfinder:
75 return rootfinder.group(1) 74 return rootfinder.group(1)
76 else: 75 else:
77 return None 76 return None
78 77
79 def _CopyTemplateFiles(self): 78 def _CopyTemplateFilesAndClearDir(self):
80 """Copy files needed to build a new apk. 79 """Copy files needed to build a new apk.
81 80
82 TODO(jrg): add more smarts so we don't change file timestamps if 81 Uses rsync to avoid unnecessary io. This call also clears outstanding
83 the files don't change? 82 files in the directory.
84 """ 83 """
85 srcdir = os.path.dirname(os.path.realpath( __file__)) 84 srcdir = os.path.abspath(os.path.dirname(__file__))
86 if not os.path.exists(self._output_directory): 85 destdir = self._output_directory
87 os.makedirs(self._output_directory) 86 if not os.path.exists(destdir):
88 for f in self._SOURCE_FILES: 87 os.makedirs(destdir)
89 src = os.path.join(srcdir, f) 88 elif not '/out/' in destdir:
90 dest = os.path.join(self._output_directory, f) 89 raise Exception('Unbelievable output directory; bailing for safety')
91 if os.path.isfile(src): 90 logging.warning('rsync %s --> %s', self._SOURCE_FILES, destdir)
92 if os.path.exists(dest): 91 logging.info(cmd_helper.GetCmdOutput(
93 os.remove(dest) 92 ['rsync', '-aRv', '--delete', '--exclude', '.svn'] +
94 logging.warn('%s --> %s' % (src, dest)) 93 self._SOURCE_FILES + [destdir], cwd=srcdir))
95 shutil.copyfile(src, dest)
96 else: # directory
97 if os.path.exists(dest):
98 # One more sanity check since we're deleting a directory...
99 if not '/out/' in dest:
100 raise Exception('Unbelievable output directory; bailing for safety')
101 shutil.rmtree(dest)
102 logging.warn('%s --> %s' % (src, dest))
103 shutil.copytree(src, dest)
104 94
105 def _ReplaceStrings(self): 95 def _ReplaceStrings(self):
106 """Replace 'replaceme' strings in generated files with a root libname. 96 """Replace 'replaceme' strings in generated files with a root libname.
107 97
108 If we have no root libname (e.g. no shlib was specified), do nothing. 98 If we have no root libname (e.g. no shlib was specified), do nothing.
109 """ 99 """
110 if not self._root_name: 100 if not self._root_name:
111 return 101 return
112 logging.warn('Replacing "replaceme" with ' + self._root_name) 102 logging.warn('Replacing "replaceme" with ' + self._root_name)
113 for f in self._REPLACEME_FILES: 103 for f in self._REPLACEME_FILES:
114 dest = os.path.join(self._output_directory, f) 104 dest = os.path.join(self._output_directory, f)
115 contents = open(dest).read() 105 contents = open(dest).read()
116 contents = contents.replace('replaceme', self._root_name) 106 contents = contents.replace('replaceme', self._root_name)
117 dest = dest.replace('replaceme', self._root_name) # update the filename! 107 dest = dest.replace('replaceme', self._root_name) # update the filename!
118 open(dest, "w").write(contents) 108 open(dest, 'w').write(contents)
119 109
120 def _CopyLibraryAndJars(self): 110 def _CopyLibraryAndJars(self):
121 """Copy the shlib and jars into the apk source tree (if relevant)""" 111 """Copy the shlib and jars into the apk source tree (if relevant)."""
122 if self._native_library: 112 if self._native_library:
123 destdir = os.path.join(self._output_directory, 'libs/' + self._target_abi) 113 destdir = os.path.join(self._output_directory, 'libs/' + self._target_abi)
124 if not os.path.exists(destdir): 114 if not os.path.exists(destdir):
125 os.makedirs(destdir) 115 os.makedirs(destdir)
126 dest = os.path.join(destdir, os.path.basename(self._native_library)) 116 dest = os.path.join(destdir, os.path.basename(self._native_library))
127 logging.warn('strip %s --> %s' % (self._native_library, dest)) 117 logging.warn('strip %s --> %s', self._native_library, dest)
128 strip = os.environ['STRIP'] 118 strip = os.environ['STRIP']
129 cmd_helper.RunCmd( 119 cmd_helper.RunCmd(
130 [strip, '--strip-unneeded', self._native_library, '-o', dest]) 120 [strip, '--strip-unneeded', self._native_library, '-o', dest])
131 if self._jars: 121 if self._jars:
132 destdir = os.path.join(self._output_directory, 'libs') 122 destdir = os.path.join(self._output_directory, 'libs')
133 if not os.path.exists(destdir): 123 if not os.path.exists(destdir):
134 os.makedirs(destdir) 124 os.makedirs(destdir)
135 for jar in self._jars: 125 for jar in self._jars:
136 dest = os.path.join(destdir, os.path.basename(jar)) 126 dest = os.path.join(destdir, os.path.basename(jar))
137 logging.warn('%s --> %s' % (jar, dest)) 127 logging.warn('%s --> %s', jar, dest)
138 shutil.copyfile(jar, dest) 128 shutil.copyfile(jar, dest)
139 129
140 def CreateBundle(self, ant_compile): 130 def CreateBundle(self, ant_compile):
141 """Create the apk bundle source and assemble components.""" 131 """Create the apk bundle source and assemble components."""
142 if not ant_compile: 132 if not ant_compile:
143 self._SOURCE_FILES.append('Android.mk') 133 self._SOURCE_FILES.append('Android.mk')
144 self._REPLACEME_FILES.append('Android.mk') 134 self._REPLACEME_FILES.append('Android.mk')
145 self._CopyTemplateFiles() 135 self._CopyTemplateFilesAndClearDir()
146 self._ReplaceStrings() 136 self._ReplaceStrings()
147 self._CopyLibraryAndJars() 137 self._CopyLibraryAndJars()
148 138
149 def Compile(self, ant_args): 139 def Compile(self, ant_args):
150 """Build the generated apk with ant. 140 """Build the generated apk with ant.
151 141
152 Args: 142 Args:
153 ant_args: extra args to pass to ant 143 ant_args: extra args to pass to ant
154 """ 144 """
155 cmd = ['ant'] 145 cmd = ['ant']
156 if ant_args: 146 if ant_args:
157 cmd.append(ant_args) 147 cmd.append(ant_args)
158 cmd.extend(['-buildfile', 148 cmd.extend(['-buildfile',
159 os.path.join(self._output_directory, 'native_test_apk.xml')]) 149 os.path.join(self._output_directory, 'native_test_apk.xml')])
160 logging.warn(cmd) 150 logging.warn(cmd)
161 p = subprocess.Popen(cmd, stderr=subprocess.STDOUT) 151 p = subprocess.Popen(cmd, stderr=subprocess.STDOUT)
162 (stdout, _) = p.communicate() 152 (stdout, _) = p.communicate()
163 logging.warn(stdout) 153 logging.warn(stdout)
164 if p.returncode != 0: 154 if p.returncode != 0:
165 logging.error('Ant return code %d' % p.returncode) 155 logging.error('Ant return code %d', p.returncode)
166 sys.exit(p.returncode) 156 sys.exit(p.returncode)
167 157
168 def CompileAndroidMk(self): 158 def CompileAndroidMk(self):
169 """Build the generated apk within Android source tree using Android.mk.""" 159 """Build the generated apk within Android source tree using Android.mk."""
170 try: 160 try:
171 import compile_android_mk # pylint: disable=F0401 161 import compile_android_mk # pylint: disable=F0401
172 except: 162 except:
173 raise AssertionError('Not in Android source tree. ' 163 raise AssertionError('Not in Android source tree. '
174 'Please use --ant-compile.') 164 'Please use --ant-compile.')
175 compile_android_mk.CompileAndroidMk(self._native_library, 165 compile_android_mk.CompileAndroidMk(self._native_library,
176 self._output_directory) 166 self._output_directory)
177 167
178 168
179 def main(argv): 169 def main(argv):
180 parser = optparse.OptionParser() 170 parser = optparse.OptionParser()
181 parser.add_option('--verbose', 171 parser.add_option('--verbose',
182 help='Be verbose') 172 help='Be verbose')
183 parser.add_option('--native_library', 173 parser.add_option('--native_library',
184 help='Full name of native shared library test bundle') 174 help='Full name of native shared library test bundle')
185 parser.add_option('--jars', 175 parser.add_option('--jars',
186 help='Space separated list of jars to be included') 176 help='Space separated list of jars to be included')
187 parser.add_option('--output', 177 parser.add_option('--output',
188 help='Output directory for generated files.') 178 help='Output directory for generated files.')
189 parser.add_option('--app_abi', default='armeabi', 179 parser.add_option('--app_abi', default='armeabi',
190 help='ABI for native shared library') 180 help='ABI for native shared library')
191 parser.add_option('--ant-compile', action='store_true', 181 parser.add_option('--ant-compile', action='store_true',
192 help='If specified, build the generated apk with ant. ' 182 help=('If specified, build the generated apk with ant. '
193 'Otherwise assume compiling within the Android ' 183 'Otherwise assume compiling within the Android '
194 'source tree using Android.mk.') 184 'source tree using Android.mk.'))
195 parser.add_option('--ant-args', 185 parser.add_option('--ant-args',
196 help='extra args for ant') 186 help='extra args for ant')
197 187
198 options, _ = parser.parse_args(argv) 188 options, _ = parser.parse_args(argv)
199 189
200 # It is not an error to specify no native library; the apk should 190 # It is not an error to specify no native library; the apk should
201 # still be generated and build. It will, however, print 191 # still be generated and build. It will, however, print
202 # NATIVE_LOADER_FAILED when run. 192 # NATIVE_LOADER_FAILED when run.
203 if not options.output: 193 if not options.output:
204 raise Exception('No output directory specified for generated files') 194 raise Exception('No output directory specified for generated files')
(...skipping 14 matching lines...) Expand all
219 209
220 if options.ant_compile: 210 if options.ant_compile:
221 ntag.Compile(options.ant_args) 211 ntag.Compile(options.ant_args)
222 else: 212 else:
223 ntag.CompileAndroidMk() 213 ntag.CompileAndroidMk()
224 214
225 logging.warn('COMPLETE.') 215 logging.warn('COMPLETE.')
226 216
227 if __name__ == '__main__': 217 if __name__ == '__main__':
228 sys.exit(main(sys.argv)) 218 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698