• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2014 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7cr_cronet.py - cr - like helper tool for cronet developers
8"""
9
10import argparse
11import os
12import re
13import shlex
14import subprocess
15import sys
16from datetime import datetime
17
18
19def quoted_args(args):
20  return ' '.join([shlex.quote(arg) for arg in args])
21
22
23def run(command, **kwargs):
24  print(command, kwargs)
25  return subprocess.call(command, **kwargs)
26
27
28def run_shell(command, extra_options=''):
29  command = command + ' ' + extra_options
30  print(command)
31  return os.system(command)
32
33
34def gn(out_dir, gn_args, gn_extra=None):
35  cmd = ['gn', 'gen', out_dir, '--args=%s' % gn_args]
36  if gn_extra:
37    cmd += gn_extra
38  return run(cmd)
39
40
41def build(out_dir, build_target, extra_options=None):
42  cmd = ['ninja', '-C', out_dir, build_target] + get_ninja_jobs_options()
43  if extra_options:
44    cmd += extra_options
45  return run(cmd)
46
47
48def install(out_dir):
49  cmd = ['build/android/adb_install_apk.py']
50  # Propagate PATH to avoid issues with missing tools http://crbug/1217979
51  env = {
52      'BUILDTYPE': out_dir[4:],
53      'PATH': os.environ.get('PATH', '')
54  }
55  return run(cmd + ['CronetTestInstrumentation.apk'], env=env) or \
56      run(cmd + ['ChromiumNetTestSupport.apk'], env=env)
57
58
59def test(out_dir, extra_options):
60  # Ideally we would fetch this path from somewhere. Though, that's not trivial
61  # and very unlikely to change. This being "best effort test code", it should
62  # be fine just to hardcode it.
63  remote_netlog_dir = '/data/data/org.chromium.net.tests/app_cronet_test/NetLog'
64  run(['adb', 'shell', 'rm', '-rf', remote_netlog_dir])
65  run([out_dir + '/bin/run_cronet_test_instrumentation_apk'] + extra_options)
66  local_netlog_dir = out_dir + '/netlogs_for-' + datetime.now().strftime(
67      "%y_%m_%d-%H_%M_%S")
68  return run(['adb', 'pull', remote_netlog_dir, local_netlog_dir])
69
70
71def unittest(out_dir, extra_options):
72  return run([out_dir + '/bin/run_cronet_unittests_android'] +
73             extra_options)
74
75
76def debug(extra_options):
77  return run(['build/android/adb_gdb', '--start',
78             '--activity=.CronetTestActivity',
79             '--program-name=CronetTest',
80             '--package-name=org.chromium.net'] +
81             extra_options)
82
83
84def stack(out_dir):
85  return run_shell('adb logcat -d | CHROMIUM_OUTPUT_DIR=' +
86                   shlex.quote(out_dir) +
87                   ' third_party/android_platform/development/scripts/stack')
88
89
90def use_goma():
91  goma_dir = (subprocess.check_output(['goma_ctl', 'goma_dir'])
92                        .decode('utf-8')
93                        .strip())
94  result = run(['goma_ctl', 'ensure_start'])
95  if not result:
96    return 'use_goma=true goma_dir="' + goma_dir + '" '
97  return ''
98
99
100def get_ninja_jobs_options():
101  if use_goma():
102    return ["-j1000"]
103  return []
104
105
106def map_config_to_android_builder(is_release, target_cpu):
107  target_cpu_to_base_builder = {
108      'x86': 'android-cronet-x86',
109      'x64': 'android-cronet-x64',
110      'arm': 'android-cronet-arm',
111      'arm64': 'android-cronet-arm64',
112      'riscv64': 'android-cronet-riscv64',
113  }
114  if target_cpu not in target_cpu_to_base_builder:
115    raise ValueError('Unsupported target CPU')
116
117  builder_name = target_cpu_to_base_builder[target_cpu]
118  if is_release:
119    builder_name += '-rel'
120  else:
121    builder_name += '-dbg'
122  return builder_name
123
124
125def filter_gn_args(gn_args):
126  gn_arg_matcher = re.compile("^.*=.*$")
127  # `mb_py lookup` prints out a bunch of metadata lines which we don't
128  # care about, we only want the GN args.
129  assert len(gn_args) > 4
130  actual_gn_args = gn_args[1:-3]
131  for line in gn_args:
132    if line in actual_gn_args:
133      assert gn_arg_matcher.match(line), \
134             f'Not dropping {line}, which does not look like a GN arg'
135    else:
136      assert not gn_arg_matcher.match(line), \
137             f'Dropping {line}, which looks like a GN arg'
138
139  return list(filter(lambda string: "remoteexec" not in string, actual_gn_args))
140
141
142def android_gn_gen(is_release, target_cpu, out_dir):
143  group_name = 'chromium.android'
144  mb_script = 'tools/mb/mb.py'
145  builder_name = map_config_to_android_builder(is_release, target_cpu)
146  # Ideally we would call `mb_py gen` directly, but we need to filter out the
147  # use_remoteexec arg, as that cannot be used in a local environment.
148  gn_args = subprocess.check_output([
149      'python3', mb_script, 'lookup', '-m', group_name, '-b', builder_name
150  ]).decode('utf-8').strip()
151  gn_args = filter_gn_args(gn_args.split("\n"))
152  return gn(out_dir, ' '.join(gn_args))
153
154
155def main():
156  parser = argparse.ArgumentParser()
157  parser.add_argument('command',
158                      choices=['gn',
159                               'sync',
160                               'build',
161                               'install',
162                               'proguard',
163                               'test',
164                               'build-test',
165                               'unit',
166                               'build-unit',
167                               'stack',
168                               'debug',
169                               'build-debug'])
170  parser.add_argument('-d', '--out_dir', action='store',
171                      help='name of the build directory')
172  parser.add_argument('-x', '--x86', action='store_true',
173                      help='build for Intel x86 architecture')
174  parser.add_argument('--x64',
175                      action='store_true',
176                      help='build for Intel x86_64 architecture')
177  parser.add_argument('-R',
178                      '--riscv64',
179                      action='store_true',
180                      help='build for riscv64 architecture')
181  parser.add_argument('-r', '--release', action='store_true',
182                      help='use release configuration')
183  parser.add_argument('-a', '--asan', action='store_true',
184                      help='use address sanitizer')
185
186  options, extra_options = parser.parse_known_args()
187  print("Options:", options)
188  print("Extra options:", extra_options)
189
190  test_target = 'cronet_test_instrumentation_apk'
191  unit_target = 'cronet_unittests_android'
192  if options.x86:
193    target_cpu = 'x86'
194    out_dir_suffix = '-x86'
195  elif options.x64:
196    target_cpu = 'x64'
197    out_dir_suffix = '-x64'
198  elif options.riscv64:
199    target_cpu = 'riscv64'
200    out_dir_suffix = '-riscv64'
201  else:
202    target_cpu = 'arm64'
203    out_dir_suffix = '-arm64'
204
205  if options.asan:
206    # ASAN on Android requires one-time setup described here:
207    # https://www.chromium.org/developers/testing/addresssanitizer
208    out_dir_suffix += '-asan'
209
210  if options.out_dir:
211    out_dir = options.out_dir
212  else:
213    if options.release:
214      out_dir = 'out/Release' + out_dir_suffix
215    else:
216      out_dir = 'out/Debug' + out_dir_suffix
217
218  if (options.command=='gn'):
219    return android_gn_gen(options.release, target_cpu, out_dir)
220  if (options.command=='sync'):
221    return run(['git', 'pull', '--rebase']) or run(['gclient', 'sync'])
222  if (options.command=='build'):
223    return build(out_dir, test_target, extra_options)
224  if (options.command == 'install'):
225    return install(out_dir)
226  if (options.command == 'proguard'):
227    return build(out_dir, 'cronet_sample_proguard_apk')
228  if (options.command == 'test'):
229    return install(out_dir) or test(out_dir, extra_options)
230  if (options.command == 'build-test'):
231    return build(out_dir, test_target) or install(out_dir) or \
232        test(out_dir, extra_options)
233  if (options.command == 'stack'):
234    return stack(out_dir)
235  if (options.command == 'debug'):
236    return install(out_dir) or debug(extra_options)
237  if (options.command == 'build-debug'):
238    return build(out_dir, test_target) or install(out_dir) or \
239        debug(extra_options)
240  if (options.command == 'unit'):
241    return unittest(out_dir, extra_options)
242  if (options.command == 'build-unit'):
243    return build(out_dir, unit_target) or unittest(out_dir, extra_options)
244
245  parser.print_help()
246  return 1
247
248
249if __name__ == '__main__':
250  sys.exit(main())
251