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