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 pipes 13import re 14import subprocess 15import sys 16 17 18def quoted_args(args): 19 return ' '.join([pipes.quote(arg) for arg in args]) 20 21 22def run(command, **kwargs): 23 print(command, kwargs) 24 return subprocess.call(command, **kwargs) 25 26 27def run_shell(command, extra_options=''): 28 command = command + ' ' + extra_options 29 print(command) 30 return os.system(command) 31 32 33def gn(out_dir, gn_args, gn_extra=None): 34 cmd = ['gn', 'gen', out_dir, '--args=%s' % gn_args] 35 if gn_extra: 36 cmd += gn_extra 37 return run(cmd) 38 39 40def build(out_dir, build_target, extra_options=None): 41 cmd = ['ninja', '-C', out_dir, build_target] + get_ninja_jobs_options() 42 if extra_options: 43 cmd += extra_options 44 return run(cmd) 45 46 47def install(out_dir): 48 cmd = ['build/android/adb_install_apk.py'] 49 # Propagate PATH to avoid issues with missing tools http://crbug/1217979 50 env = { 51 'BUILDTYPE': out_dir[4:], 52 'PATH': os.environ.get('PATH', '') 53 } 54 return run(cmd + ['CronetTestInstrumentation.apk'], env=env) or \ 55 run(cmd + ['ChromiumNetTestSupport.apk'], env=env) 56 57 58def test(out_dir, extra_options): 59 return run([out_dir + '/bin/run_cronet_test_instrumentation_apk'] + 60 extra_options) 61 62 63def unittest(out_dir, extra_options): 64 return run([out_dir + '/bin/run_cronet_unittests_android'] + 65 extra_options) 66 67 68def test_ios(out_dir, extra_options): 69 return run([out_dir + '/iossim', '-c', quoted_args(extra_options), 70 out_dir + '/cronet_test.app']) 71 72 73def unittest_ios(out_dir, extra_options): 74 return run([out_dir + '/iossim', '-c', quoted_args(extra_options), 75 out_dir + '/cronet_unittests_ios.app']) 76 77 78def debug(extra_options): 79 return run(['build/android/adb_gdb', '--start', 80 '--activity=.CronetTestActivity', 81 '--program-name=CronetTest', 82 '--package-name=org.chromium.net'] + 83 extra_options) 84 85 86def stack(out_dir): 87 return run_shell( 88 'adb logcat -d | CHROMIUM_OUTPUT_DIR=' + pipes.quote(out_dir) + 89 ' third_party/android_platform/development/scripts/stack') 90 91 92def use_goma(): 93 goma_dir = (subprocess.check_output(['goma_ctl', 'goma_dir']) 94 .decode('utf-8') 95 .strip()) 96 result = run(['goma_ctl', 'ensure_start']) 97 if not result: 98 return 'use_goma=true goma_dir="' + goma_dir + '" ' 99 return '' 100 101 102def get_ninja_jobs_options(): 103 if use_goma(): 104 return ["-j1000"] 105 return [] 106 107 108def map_config_to_android_builder(is_release, target_cpu): 109 target_cpu_to_base_builder = { 110 'x86': 'android-cronet-x86', 111 'arm': 'android-cronet-arm', 112 'arm64': 'android-cronet-arm64', 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 get_ios_gn_args(is_release, target_cpu): 126 print(is_release, target_cpu) 127 gn_args = [ 128 'target_os = "ios"', 129 'enable_websockets = false', 130 'disable_file_support = true', 131 'disable_brotli_filter = false', 132 'is_component_build = false', 133 'use_crash_key_stubs = true', 134 'use_partition_alloc = false', 135 'include_transport_security_state_preload_list = false', 136 use_goma(), 137 'use_platform_icu_alternatives = true', 138 'is_cronet_build = true', 139 'enable_remoting = false', 140 'ios_app_bundle_id_prefix = "org.chromium"', 141 'ios_deployment_target = "10.0"', 142 'enable_dsyms = true', 143 'ios_stack_profiler_enabled = false', 144 f'target_cpu = "{target_cpu}"', 145 ] 146 if is_release: 147 gn_args += [ 148 'is_debug = false', 149 'is_official_build = true', 150 ], 151 return ' '.join(gn_args) 152 153 154def ios_gn_gen(is_release, target_cpu, out_dir): 155 gn_extra = ['--ide=xcode', '--filters=//components/cronet/*'] 156 return gn(out_dir, get_ios_gn_args(is_release, target_cpu), gn_extra) 157 158 159def filter_gn_args(gn_args): 160 gn_arg_matcher = re.compile("^.*=.*$") 161 # `mb_py lookup` prints out a bunch of metadata lines which we don't 162 # care about, we only want the GN args. 163 assert len(gn_args) > 4 164 actual_gn_args = gn_args[1:-3] 165 for line in gn_args: 166 if line in actual_gn_args: 167 assert gn_arg_matcher.match(line), \ 168 f'Not dropping {line}, which does not look like a GN arg' 169 else: 170 assert not gn_arg_matcher.match(line), \ 171 f'Dropping {line}, which looks like a GN arg' 172 173 return list(filter(lambda string: "remoteexec" not in string, actual_gn_args)) 174 175 176def android_gn_gen(is_release, target_cpu, out_dir): 177 group_name = 'chromium.android' 178 mb_script = 'tools/mb/mb.py' 179 builder_name = map_config_to_android_builder(is_release, target_cpu) 180 # Ideally we would call `mb_py gen` directly, but we need to filter out the 181 # use_remoteexec arg, as that cannot be used in a local environment. 182 gn_args = subprocess.check_output([ 183 'python', mb_script, 'lookup', '-m', group_name, '-b', builder_name 184 ]).decode('utf-8').strip() 185 gn_args = filter_gn_args(gn_args.split("\n")) 186 return gn(out_dir, ' '.join(gn_args)) 187 188 189def gn_gen(is_release, target_cpu, out_dir, is_ios): 190 if is_ios: 191 return ios_gn_gen(is_release, target_cpu, out_dir) 192 return android_gn_gen(is_release, target_cpu, out_dir) 193 194 195def main(): 196 is_ios = (sys.platform == 'darwin') 197 parser = argparse.ArgumentParser() 198 parser.add_argument('command', 199 choices=['gn', 200 'sync', 201 'build', 202 'install', 203 'proguard', 204 'test', 205 'build-test', 206 'unit', 207 'build-unit', 208 'stack', 209 'debug', 210 'build-debug']) 211 parser.add_argument('-d', '--out_dir', action='store', 212 help='name of the build directory') 213 parser.add_argument('-x', '--x86', action='store_true', 214 help='build for Intel x86 architecture') 215 parser.add_argument('-r', '--release', action='store_true', 216 help='use release configuration') 217 parser.add_argument('-a', '--asan', action='store_true', 218 help='use address sanitizer') 219 if is_ios: 220 parser.add_argument('-i', '--iphoneos', action='store_true', 221 help='build for physical iphone') 222 parser.add_argument('-b', '--bundle-id-prefix', action='store', 223 dest='bundle_id_prefix', default='org.chromium', 224 help='configure bundle id prefix') 225 226 options, extra_options = parser.parse_known_args() 227 print("Options:", options) 228 print("Extra options:", extra_options) 229 230 if is_ios: 231 test_target = 'cronet_test' 232 unit_target = 'cronet_unittests_ios' 233 if options.iphoneos: 234 out_dir_suffix = '-iphoneos' 235 target_cpu = 'arm64' 236 else: 237 out_dir_suffix = '-iphonesimulator' 238 target_cpu = 'x64' 239 else: # is_android 240 test_target = 'cronet_test_instrumentation_apk' 241 unit_target = 'cronet_unittests_android' 242 if options.x86: 243 target_cpu = 'x86' 244 out_dir_suffix = '-x86' 245 else: 246 target_cpu = 'arm64' 247 out_dir_suffix = '-arm64' 248 if options.asan: 249 # ASAN on Android requires one-time setup described here: 250 # https://www.chromium.org/developers/testing/addresssanitizer 251 out_dir_suffix += '-asan' 252 253 if options.out_dir: 254 out_dir = options.out_dir 255 else: 256 if options.release: 257 out_dir = 'out/Release' + out_dir_suffix 258 else: 259 out_dir = 'out/Debug' + out_dir_suffix 260 261 if (options.command=='gn'): 262 return gn_gen(options.release, target_cpu, out_dir, is_ios) 263 if (options.command=='sync'): 264 return run(['git', 'pull', '--rebase']) or run(['gclient', 'sync']) 265 if (options.command=='build'): 266 return build(out_dir, test_target, extra_options) 267 if (not is_ios): 268 if (options.command=='install'): 269 return install(out_dir) 270 if (options.command=='proguard'): 271 return build(out_dir, 'cronet_sample_proguard_apk') 272 if (options.command=='test'): 273 return install(out_dir) or test(out_dir, extra_options) 274 if (options.command=='build-test'): 275 return build(out_dir, test_target) or install(out_dir) or \ 276 test(out_dir, extra_options) 277 if (options.command=='stack'): 278 return stack(out_dir) 279 if (options.command=='debug'): 280 return install(out_dir) or debug(extra_options) 281 if (options.command=='build-debug'): 282 return build(out_dir, test_target) or install(out_dir) or \ 283 debug(extra_options) 284 if (options.command=='unit'): 285 return unittest(out_dir, extra_options) 286 if (options.command=='build-unit'): 287 return build(out_dir, unit_target) or unittest(out_dir, extra_options) 288 else: 289 if (options.command=='test'): 290 return test_ios(out_dir, extra_options) 291 if (options.command=='build-test'): 292 return build(out_dir, test_target) or test_ios(out_dir, extra_options) 293 if (options.command=='unit'): 294 return unittest_ios(out_dir, extra_options) 295 if (options.command=='build-unit'): 296 return build(out_dir, unit_target) or unittest_ios(out_dir, extra_options) 297 298 parser.print_help() 299 return 1 300 301 302if __name__ == '__main__': 303 sys.exit(main()) 304