android.py revision c7f1593f9af3ea1b9264b37628c36f3a70e1749a
1#!/usr/bin/python
2#
3# Copyright 2011 Google Inc. All Rights Reserved.
4
5"""Helper modules for Android toolchain test infrastructure.
6
7Provides following Android toolchain test jobs and commands.
8. Checkout Android toolchain source code
9. Build Android toolchain
10. Checkout and build Android tree
11. Checkout/build/run Android benchmarks, generate size dashboard
12. Transform size dashboard to report, send perflab jobid to
13  perflab dashboard server.(TODO)
14"""
15
16__author__ = 'jingyu@google.com (Jing Yu)'
17
18import os.path
19
20from automation.clients.helper import jobs
21from automation.clients.helper import perforce
22from automation.common import command as cmd
23from automation.common import job
24
25
26class JobsFactory(object):
27  def __init__(self, gcc_version='4.4.3', build_type='DEVELOPMENT'):
28    assert gcc_version in ['4.4.3', '4.6', 'google_main', 'fsf_trunk']
29    assert build_type in ['DEVELOPMENT', 'RELEASE']
30
31    self.gcc_version = gcc_version
32    self.commands = CommandsFactory(gcc_version, build_type)
33    self.tc_tag = 'gcc-%s-%s' % (gcc_version, build_type)
34
35  def CheckoutAndroidToolchain(self):
36    """Check out Android toolchain sources by release and gcc version."""
37    command = self.commands.CheckoutAndroidToolchain()
38    new_job = jobs.CreateLinuxJob('AndroidCheckoutToolchain(%s)' % self.tc_tag,
39                                  command)
40    checkout_dir_dep = job.FolderDependency(new_job, self.commands.CHECKOUT_DIR)
41    return new_job, checkout_dir_dep
42
43  def BuildAndroidToolchain(self, checkout_dir_dep):
44    """Build Android Toolchain."""
45    command = self.commands.BuildAndroidToolchain()
46    new_job = jobs.CreateLinuxJob('AndroidBuildToolchain(%s)' % self.tc_tag,
47                                  command)
48    new_job.DependsOnFolder(checkout_dir_dep)
49    tc_prefix_dep = job.FolderDependency(
50        new_job, self.commands.toolchain_prefix_dir)
51    return new_job, tc_prefix_dep
52
53  def BuildAndroidImage(self, tc_prefix_dep, product='stingray',
54                        branch='ics-release'):
55    assert product in ['stingray', 'passion', 'trygon', 'soju']
56    assert branch in ['honeycomb-release', 'ics-release']
57    command = self.commands.BuildAndroidImage(product, branch)
58    new_job = jobs.CreateLinuxJob('AndroidGetBuildTree(%s)' % self.tc_tag,
59                                  command)
60    new_job.DependsOnFolder(tc_prefix_dep)
61    return new_job
62
63  def Benchmark(self, tc_prefix_dep, arch='soju'):
64    assert arch in ['soju', 'stingray']
65    script_cmd = self.commands.CheckoutScripts()
66    experiment_tag = 'android/nightly/%s/%s/$JOB_ID' % (self.tc_tag, arch)
67    build_run_benchmark_cmd = self.commands.BuildRunBenchmark(arch,
68                                                              experiment_tag)
69    command = cmd.Chain(script_cmd, build_run_benchmark_cmd)
70    new_job = jobs.CreateLinuxJob('AndroidBenchmarking(%s)' % self.tc_tag,
71                                  command)
72    new_job.DependsOnFolder(tc_prefix_dep)
73    return new_job
74
75
76class CommandsFactory(object):
77  CHECKOUT_DIR = 'androidtc-checkout-dir'
78  TOOLCHAIN_SRC_DIR = os.path.join(CHECKOUT_DIR, 'src')
79  TOOLCHAIN_BUILD_DIR = 'obj'
80  ANDROID_TREES_DIR = 'android_trees'
81  TOOLS_DIR = 'android-tools'
82  BENCHMARK_OUT_DIR = 'results'
83
84  def __init__(self, gcc_version, build_type):
85    assert gcc_version in ['4.4.3', '4.6', 'google_main', 'fsf_trunk']
86    assert build_type in ['DEVELOPMENT', 'RELEASE']
87
88    self.build_type = build_type
89    self.gcc_version = gcc_version
90    self.binutils_version = '2.21'
91    self.gold_version = '2.21'
92    self.toolchain_prefix_dir = 'install-gcc-%s-%s' % (
93        gcc_version, build_type)
94    self.p4client = self._CreatePerforceClient()
95    self.scripts = ScriptsFactory(self.gcc_version, self.binutils_version,
96                                  self.gold_version)
97
98  def _CreatePerforceClient(self):
99    p4_dev_path = 'gcctools/google_vendor_src_branch'
100    mobile_rel_branch = ('branches/'
101                         'mobile_toolchain_v15_release_branch/gcctools/'
102                         'google_vendor_src_branch')
103    gcc_443_rel_branch = ('branches/'
104                          'android_compiler_v14_release_branch/gcctools/'
105                          'google_vendor_src_branch')
106
107    # Common views for tools
108    p4view = perforce.View('depot2',
109                           perforce.PathMapping.ListFromPathTuples(
110                               [('gcctools/android/build/...', 'src/build/...'),
111                                ('gcctools/android/Tarballs/...',
112                                 'src/tarballs/...')]))
113    for mapping in perforce.PathMapping.ListFromPathDict(
114        {'gcctools/android': ['tools/scripts/...', 'master/...']}):
115      p4view.add(mapping)
116
117    # Add views for gdb
118    p4view.add(perforce.PathMapping(p4_dev_path, 'src',
119                                    'gdb/gdb-7.1.x-android/...'))
120
121    # Add view for binutils for ld and gold
122    if self.build_type is 'RELEASE':
123      binutils_branch = mobile_rel_branch
124    else:
125      binutils_branch = p4_dev_path
126    p4view.add(perforce.PathMapping(binutils_branch, 'src',
127                                    ('binutils/binutils-%s/...' %
128                                     self.binutils_version)))
129    if self.binutils_version != self.gold_version:
130      p4view.add(perforce.PathMapping(binutils_branch, 'src',
131                                      ('binutils/binutils-%s/...' %
132                                       self.gold_version)))
133
134    # Add view for gcc if gcc_version is '4.4.3'.
135    if self.gcc_version == '4.4.3':
136      gcc443_path = 'gcc/gcc-4.4.3/...'
137      if self.build_type is 'RELEASE':
138        p4view.add(perforce.PathMapping(gcc_443_rel_branch, 'src', gcc443_path))
139      else:
140        p4view.add(perforce.PathMapping(p4_dev_path, 'src', gcc443_path))
141
142    return perforce.CommandsFactory(self.CHECKOUT_DIR, p4view)
143
144  def _CheckoutGCCFromSVN(self):
145    """Check out gcc from fsf svn.
146
147       Return the command that check out gcc from svn
148       to gcc_required_dir (=TOOLCHAIN_SRC_DIR/src/gcc/gcc-xxx).
149
150       TODO:
151         Create a svn class that does these jobs.
152         Parallelize p4 checkout and svn checkout.
153    """
154    if self.gcc_version == '4.4.3':
155      return ''
156    assert self.gcc_version in ['4.6', 'google_main', 'fsf_trunk']
157
158    gcc_branches_dir = {'4.6': 'branches/google/gcc-4_6',
159                        'google_main': 'branches/google/main',
160                        'fsf_trunk': 'trunk'}
161
162    # Find GCC revision number, output it to TOOLCHAIN_SRC_DIR/CLNUM_GCC
163    svn_get_revision = cmd.Pipe(
164        cmd.Shell('svn', 'info'),
165        cmd.Shell('grep', '"Revision:"'),
166        cmd.Shell('sed', '-E', '"s,Revision: ([0-9]+).*,\\1,"'),
167        output='../../../CLNUM_GCC')
168
169    svn_co_command = 'svn co svn://gcc.gnu.org/svn/gcc/%s .' % (
170        gcc_branches_dir[self.gcc_version])
171
172    gcc_required_dir = os.path.join(self.TOOLCHAIN_SRC_DIR, 'gcc',
173                                    'gcc-%s' % self.gcc_version)
174
175    return cmd.Chain(cmd.MakeDir(gcc_required_dir),
176                     cmd.Wrapper(cmd.Chain(svn_co_command, svn_get_revision),
177                                 cwd=gcc_required_dir))
178
179  def CheckoutAndroidToolchain(self):
180    p4client = self.p4client
181    command = p4client.SetupAndDo(p4client.Sync(),
182                                  p4client.SaveCurrentCLNumber('CLNUM'),
183                                  p4client.Remove())
184    if self.gcc_version != '4.4.3':
185      command.append(self._CheckoutGCCFromSVN())
186
187    return command
188
189  def BuildAndroidToolchain(self):
190    script_cmd = self.scripts.BuildAndroidToolchain(self.toolchain_prefix_dir,
191                                                    self.CHECKOUT_DIR,
192                                                    self.TOOLCHAIN_BUILD_DIR,
193                                                    self.TOOLCHAIN_SRC_DIR)
194
195    # Record toolchain and gcc CL number
196    record_cl_cmd = cmd.Copy(os.path.join(self.CHECKOUT_DIR, 'CLNUM*'),
197                             to_dir=self.toolchain_prefix_dir)
198    save_cmd = cmd.Tar(os.path.join('$JOB_TMP', 'results',
199                                    '%s.tar.bz2' % self.toolchain_prefix_dir),
200                       self.toolchain_prefix_dir)
201    return cmd.Chain(script_cmd, record_cl_cmd, save_cmd)
202
203  def _BuildAndroidTree(self, local_android_branch_dir, product):
204    target_tools_prefix = os.path.join('$JOB_TMP', self.toolchain_prefix_dir,
205                                       'bin', 'arm-linux-androideabi-')
206    java_path = '/usr/lib/jvm/java-6-sun/bin'
207    build_cmd = cmd.Shell('make', '-j8',
208                          'PRODUCT-%s-userdebug' % product,
209                          'TARGET_TOOLS_PREFIX=%s' % target_tools_prefix,
210                          'PATH=%s:$PATH' % java_path)
211    return cmd.Wrapper(build_cmd, cwd=local_android_branch_dir)
212
213  def BuildAndroidImage(self, product, branch):
214    assert product in ['stingray', 'passion', 'trygon', 'soju']
215
216    # Copy the tree from atree.mtv.corp to ANDROID_TREES_DIR/branch
217    androidtrees_host = 'atree.mtv.corp.google.com'
218    androidtrees_path = ('/usr/local/google2/home/mobiletc-prebuild/'
219                         'android_trees')
220    remote_android_branch_path = os.path.join(androidtrees_path, branch)
221    local_android_branch_dir = os.path.join(self.ANDROID_TREES_DIR, branch)
222    gettree_cmd = cmd.RemoteCopyFrom(androidtrees_host,
223                                     remote_android_branch_path,
224                                     local_android_branch_dir)
225
226    # Configure and build the tree
227    buildtree_cmd = self._BuildAndroidTree(local_android_branch_dir, product)
228
229    # Compress and copy system.img to result
230    result_system_img = os.path.join(local_android_branch_dir, 'out', 'target',
231                                     'product', product, 'system.img')
232    copy_img = cmd.Copy(result_system_img, to_dir='results')
233    compress_img = cmd.Shell('bzip2', os.path.join('results', 'system.img'))
234
235    return cmd.Chain(gettree_cmd, buildtree_cmd, copy_img, compress_img)
236
237  def CheckoutScripts(self):
238    p4view = perforce.View('depot2', [perforce.PathMapping(
239        'gcctools/android/tools/...', 'tools/...')])
240    p4client = perforce.CommandsFactory(self.TOOLS_DIR, p4view)
241    return p4client.SetupAndDo(p4client.Sync(), p4client.Remove())
242
243  def BuildRunBenchmark(self, arch, run_experiment):
244    # Copy base benchmark binaries from atree.mtv.corp
245    base_benchbin_host = 'atree.mtv.corp.google.com'
246    base_benchbin_path = ('/usr/local/google2/home/mobiletc-prebuild/'
247                          'archive/v3binaries/2011-10-18')
248    local_basebenchbin_dir = 'base_benchmark_bin'
249    getbase_cmd = cmd.RemoteCopyFrom(base_benchbin_host,
250                                     base_benchbin_path,
251                                     local_basebenchbin_dir)
252
253    # Build and run benchmark.
254    android_arch = 'android_%s' % arch
255    run_label = 'normal'
256    benchmark_cmd = self.scripts.RunBenchmark(self.toolchain_prefix_dir,
257                                              self.TOOLS_DIR,
258                                              self.BENCHMARK_OUT_DIR,
259                                              run_label, run_experiment,
260                                              android_arch,
261                                              local_basebenchbin_dir)
262
263    # Extract jobid from BENCHMARK_OUT_DIR/log/jobid_normal.log file.
264    # Copy jobid to www server to generate performance dashboard.
265    # TODO(jingyu)
266
267    return cmd.Chain(getbase_cmd, benchmark_cmd)
268
269
270class ScriptsFactory(object):
271  def __init__(self, gcc_version, binutils_version, gold_version):
272    self._gcc_version = gcc_version
273    self._binutils_version = binutils_version
274    self._gold_version = gold_version
275
276  def BuildAndroidToolchain(self, toolchain_prefix_dir, checkout_dir,
277                            toolchain_build_dir, androidtc_src_dir):
278    if self._gcc_version == '4.4.3':
279      gold_option = 'both/gold'
280    else:
281      gold_option = 'default'
282
283    return cmd.Shell(
284        'build_androidtoolchain.sh',
285        '--toolchain-src=%s' % os.path.join('$JOB_TMP', androidtc_src_dir),
286        '--build-path=%s' % os.path.join('$JOB_TMP', toolchain_build_dir),
287        '--install-prefix=%s' % os.path.join('$JOB_TMP', toolchain_prefix_dir),
288        '--target=arm-linux-androideabi',
289        '--enable-gold=%s' % gold_option,
290        '--with-gcc-version=%s' % self._gcc_version,
291        '--with-binutils-version=%s' % self._binutils_version,
292        '--with-gold-version=%s' % self._gold_version,
293        '--with-gdb-version=7.1.x-android',
294        '--log-path=%s/logs' % '$JOB_HOME',
295        '--android-sysroot=%s' %
296        os.path.join('$JOB_TMP', checkout_dir, 'gcctools', 'android',
297                     'master', 'honeycomb_generic_sysroot'),
298        path=os.path.join(checkout_dir, 'gcctools', 'android', 'tools',
299                          'scripts'))
300
301  def RunBenchmark(self, toolchain_prefix_dir, checkout_dir, output_dir,
302                   run_label, run_experiment, arch, base_bench_bin=None):
303    if base_bench_bin:
304      base_bench_opt = '--base_benchmark_bin=%s' % base_bench_bin
305    else:
306      base_bench_opt = ''
307
308    return cmd.Shell(
309        'benchmark.sh',
310        '--android_toolchain=%s' % os.path.join('$JOB_TMP',
311                                                toolchain_prefix_dir),
312        '--bench_space=%s' % os.path.join('$JOB_TMP', 'bench'),
313        '--benchmark_bin=%s' % os.path.join('$JOB_TMP', output_dir,
314                                            'bench_bin'),
315        base_bench_opt,
316        '--log_path=%s' % os.path.join('$JOB_TMP', output_dir, 'log'),
317        '--arch=%s' % arch,
318        '--run_label=%s' % run_label,
319        '--run_experiment=%s' % run_experiment,
320        path=os.path.join(checkout_dir, 'tools', 'scripts'))
321