| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 4 # for details. All rights reserved. Use of this source code is governed by a | 4 # for details. All rights reserved. Use of this source code is governed by a |
| 5 # BSD-style license that can be found in the LICENSE file. | 5 # BSD-style license that can be found in the LICENSE file. |
| 6 | 6 |
| 7 """ | 7 """ |
| 8 Find an Android device with a given ABI | 8 Find an Android device with a given ABI. |
| 9 | 9 |
| 10 The name of the Android device is printed to stdout | 10 The name of the Android device is printed to stdout. |
| 11 | 11 |
| 12 Optionally configure and launch an emulator if there's no existing device for a | 12 Optionally configure and launch an emulator if there's no existing device for a |
| 13 given ABI. Will download and install Android SDK components as needed. | 13 given ABI. Will download and install Android SDK components as needed. |
| 14 """ | 14 """ |
| 15 | 15 |
| 16 import optparse | 16 import optparse |
| 17 import os | 17 import os |
| 18 import re | 18 import re |
| 19 import sys | 19 import sys |
| 20 import traceback | 20 import traceback |
| 21 import utils | 21 import utils |
| 22 | 22 |
| 23 | 23 |
| 24 DEBUG = False | 24 DEBUG = False |
| 25 VERBOSE = False | 25 VERBOSE = False |
| 26 | 26 |
| 27 | 27 |
| 28 def BuildOptions(): | 28 def BuildOptions(): |
| 29 result = optparse.OptionParser() | 29 result = optparse.OptionParser() |
| 30 result.add_option( | 30 result.add_option( |
| 31 "-a", "--abi", | 31 "-a", "--abi", |
| 32 action="store", type="string", | 32 action="store", type="string", |
| 33 help="Desired ABI. armeabi-v7a or x86") | 33 help="Desired ABI. armeabi-v7a or x86.") |
| 34 result.add_option( | 34 result.add_option( |
| 35 "-b", "--bootstrap", | 35 "-b", "--bootstrap", |
| 36 help='Bootstrap - create an emulator, installing SDK packages if needed.', | 36 help='Bootstrap - create an emulator, installing SDK packages if needed.', |
| 37 default=False, action="store_true") | 37 default=False, action="store_true") |
| 38 result.add_option( | 38 result.add_option( |
| 39 "-d", "--debug", | 39 "-d", "--debug", |
| 40 help='Turn on debugging diagnostics.', | 40 help='Turn on debugging diagnostics.', |
| 41 default=False, action="store_true") | 41 default=False, action="store_true") |
| 42 result.add_option( | 42 result.add_option( |
| 43 "-v", "--verbose", | 43 "-v", "--verbose", |
| 44 help='Verbose output.', | 44 help='Verbose output.', |
| 45 default=False, action="store_true") | 45 default=False, action="store_true") |
| 46 return result | 46 return result |
| 47 | 47 |
| 48 | 48 |
| 49 def ProcessOptions(options): | 49 def ProcessOptions(options): |
| 50 global DEBUG | 50 global DEBUG |
| 51 DEBUG = options.debug | 51 DEBUG = options.debug |
| 52 global VERBOSE | 52 global VERBOSE |
| 53 VERBOSE = options.verbose | 53 VERBOSE = options.verbose |
| 54 if options.abi is None: | 54 if options.abi is None: |
| 55 sys.stderr.write('--abi not specified\n') | 55 sys.stderr.write('--abi not specified.\n') |
| 56 return False | 56 return False |
| 57 return True | 57 return True |
| 58 | 58 |
| 59 | 59 |
| 60 def ParseAndroidListSdkResult(text): | 60 def ParseAndroidListSdkResult(text): |
| 61 """ | 61 """ |
| 62 Parse the output of an 'android list sdk' command. | 62 Parse the output of an 'android list sdk' command. |
| 63 | 63 |
| 64 Return list of (id-num, id-key, type, description) | 64 Return list of (id-num, id-key, type, description). |
| 65 """ | 65 """ |
| 66 header_regex = re.compile( | 66 header_regex = re.compile( |
| 67 r'Packages available for installation or update: \d+\n') | 67 r'Packages available for installation or update: \d+\n') |
| 68 packages = re.split(header_regex, text) | 68 packages = re.split(header_regex, text) |
| 69 if len(packages) != 2: | 69 if len(packages) != 2: |
| 70 raise Exception("Could not get a list of packages to install") | 70 raise utils.Error("Could not get a list of packages to install") |
| 71 entry_regex = re.compile( | 71 entry_regex = re.compile( |
| 72 r'^id\: (\d+) or "([^"]*)"\n\s*Type\: ([^\n]*)\n\s*Desc\: (.*)') | 72 r'^id\: (\d+) or "([^"]*)"\n\s*Type\: ([^\n]*)\n\s*Desc\: (.*)') |
| 73 entries = [] | 73 entries = [] |
| 74 for entry in packages[1].split('----------\n'): | 74 for entry in packages[1].split('----------\n'): |
| 75 match = entry_regex.match(entry) | 75 match = entry_regex.match(entry) |
| 76 if match == None: | 76 if match == None: |
| 77 continue | 77 continue |
| 78 entries.append((int(match.group(1)), match.group(2), match.group(3), | 78 entries.append((int(match.group(1)), match.group(2), match.group(3), |
| 79 match.group(4))) | 79 match.group(4))) |
| 80 return entries | 80 return entries |
| 81 | 81 |
| 82 | 82 |
| 83 def AndroidListSdk(): | 83 def AndroidListSdk(): |
| 84 return ParseAndroidListSdkResult(utils.RunCommand( | 84 return ParseAndroidListSdkResult(utils.RunCommand( |
| 85 ["android", "list", "sdk", "-a", "-e"])) | 85 ["android", "list", "sdk", "-a", "-e"])) |
| 86 | 86 |
| 87 | 87 |
| 88 def AndroidSdkFindPackage(packages, key): | 88 def AndroidSdkFindPackage(packages, key): |
| 89 """key is (id-key, type, description-prefix)""" | 89 """ |
| 90 Args: |
| 91 packages: list of (id-num, id-key, type, description). |
| 92 key: (id-key, type, description-prefix). |
| 93 """ |
| 94 (key_id, key_type, key_description_prefix) = key |
| 90 for package in packages: | 95 for package in packages: |
| 91 if (package[1] == key[0] and package[2] == key[1] | 96 (package_num, package_id, package_type, package_description) = package |
| 92 and package[3].find(key[2]) == 0): | 97 if (package_id == key_id and package_type == key_type |
| 98 and package_description.startswith(key_description_prefix)): |
| 93 return package | 99 return package |
| 94 return None | 100 return None |
| 95 | 101 |
| 96 | 102 |
| 97 def EnsureSdkPackageInstalled(packages, key): | 103 def EnsureSdkPackageInstalled(packages, key): |
| 98 """ | 104 """ |
| 99 Makes sure the package with a given key is installed. | 105 Makes sure the package with a given key is installed. |
| 100 | 106 |
| 101 key is (id-key, type, description-prefix) | 107 key is (id-key, type, description-prefix) |
| 102 | 108 |
| 103 Returns True if the package was not already installed. | 109 Returns True if the package was not already installed. |
| 104 """ | 110 """ |
| 105 entry = AndroidSdkFindPackage(packages, key) | 111 entry = AndroidSdkFindPackage(packages, key) |
| 106 if entry is None: | 112 if entry is None: |
| 107 raise Exception("Could not find a package for key %s" % key) | 113 raise utils.Error("Could not find a package for key %s" % key) |
| 108 packageId = entry[0] | 114 packageId = entry[0] |
| 109 if VERBOSE: | 115 if VERBOSE: |
| 110 sys.stderr.write('Checking Android SDK package %s...\n' % str(entry)) | 116 sys.stderr.write('Checking Android SDK package %s...\n' % str(entry)) |
| 111 out = utils.RunCommand( | 117 out = utils.RunCommand( |
| 112 ["android", "update", "sdk", "-a", "-u", "--filter", str(packageId)]) | 118 ["android", "update", "sdk", "-a", "-u", "--filter", str(packageId)]) |
| 113 return out.find('\nInstalling Archives:\n') >= 0 | 119 return '\nInstalling Archives:\n' in out |
| 114 | 120 |
| 115 | 121 |
| 116 def SdkPackagesForAbi(abi): | 122 def SdkPackagesForAbi(abi): |
| 117 packagesForAbi = { | 123 packagesForAbi = { |
| 118 'armeabi-v7a': [ | 124 'armeabi-v7a': [ |
| 119 # The platform needed to install the armeabi ABI system image: | 125 # The platform needed to install the armeabi ABI system image: |
| 120 ('android-15', 'Platform', 'Android SDK Platform 4.0.3'), | 126 ('android-15', 'Platform', 'Android SDK Platform 4.0.3'), |
| 121 # The armeabi-v7a ABI system image: | 127 # The armeabi-v7a ABI system image: |
| 122 ('sysimg-15', 'SystemImage', 'Android SDK Platform 4.0.3') | 128 ('sysimg-15', 'SystemImage', 'Android SDK Platform 4.0.3') |
| 123 ], | 129 ], |
| 124 'x86': [ | 130 'x86': [ |
| 125 # The platform needed to install the x86 ABI system image: | 131 # The platform needed to install the x86 ABI system image: |
| 126 ('android-15', 'Platform', 'Android SDK Platform 4.0.3'), | 132 ('android-15', 'Platform', 'Android SDK Platform 4.0.3'), |
| 127 # The x86 ABI system image: | 133 # The x86 ABI system image: |
| 128 ('sysimg-15', 'SystemImage', 'Android SDK Platform 4.0.4') | 134 ('sysimg-15', 'SystemImage', 'Android SDK Platform 4.0.4') |
| 129 ] | 135 ] |
| 130 } | 136 } |
| 131 | 137 |
| 132 if abi not in packagesForAbi: | 138 if abi not in packagesForAbi: |
| 133 raise Exception('Unsupported abi %s' % abi) | 139 raise utils.Error('Unsupported abi %s' % abi) |
| 134 return packagesForAbi[abi] | 140 return packagesForAbi[abi] |
| 135 | 141 |
| 136 | 142 |
| 137 def TargetForAbi(abi): | 143 def TargetForAbi(abi): |
| 138 for package in SdkPackagesForAbi(abi): | 144 for package in SdkPackagesForAbi(abi): |
| 139 if package[1] == 'Platform': | 145 if package[1] == 'Platform': |
| 140 return package[0] | 146 return package[0] |
| 141 | 147 |
| 148 |
| 142 def EnsureAndroidSdkPackagesInstalled(abi): | 149 def EnsureAndroidSdkPackagesInstalled(abi): |
| 143 """Return true if at least one package was not already installed.""" | 150 """Return true if at least one package was not already installed.""" |
| 144 abiPackageList = SdkPackagesForAbi(abi) | 151 abiPackageList = SdkPackagesForAbi(abi) |
| 145 installedSomething = False | 152 installedSomething = False |
| 146 packages = AndroidListSdk() | 153 packages = AndroidListSdk() |
| 147 for package in abiPackageList: | 154 for package in abiPackageList: |
| 148 installedSomething |= EnsureSdkPackageInstalled(packages, package) | 155 installedSomething |= EnsureSdkPackageInstalled(packages, package) |
| 149 return installedSomething | 156 return installedSomething |
| 150 | 157 |
| 151 | 158 |
| 152 def ParseAndroidListAvdResult(text): | 159 def ParseAndroidListAvdResult(text): |
| 153 """ | 160 """ |
| 154 Parse the output of an 'android list avd' command. | 161 Parse the output of an 'android list avd' command. |
| 155 Return List of {Name: Path: Target: ABI: Skin: Sdcard:} | 162 Return List of {Name: Path: Target: ABI: Skin: Sdcard:} |
| 156 """ | 163 """ |
| 157 text = text.split('Available Android Virtual Devices:\n')[-1] | 164 text = text.split('Available Android Virtual Devices:\n')[-1] |
| 158 text = text.split( | 165 text = text.split( |
| 159 'The following Android Virtual Devices could not be loaded:\n')[0] | 166 'The following Android Virtual Devices could not be loaded:\n')[0] |
| 160 result = [] | 167 result = [] |
| 161 line_re = re.compile(r'^\s*([^\:]+)\:\s*(.*)$') | 168 line_re = re.compile(r'^\s*([^\:]+)\:\s*(.*)$') |
| 162 for chunk in text.split('\n---------\n'): | 169 for chunk in text.split('\n---------\n'): |
| 163 entry = {} | 170 entry = {} |
| 164 for line in chunk.split('\n'): | 171 for line in chunk.split('\n'): |
| 165 line = line.strip() | 172 line = line.strip() |
| 166 if len(line) == 0: | 173 if len(line) == 0: |
| 167 continue | 174 continue |
| 168 match = line_re.match(line) | 175 match = line_re.match(line) |
| 169 if match is None: | 176 if match is None: |
| 170 raise Exception('Match failed') | 177 raise utils.Error('Match failed') |
| 171 entry[match.group(1)] = match.group(2) | 178 entry[match.group(1)] = match.group(2) |
| 172 if len(entry) > 0: | 179 if len(entry) > 0: |
| 173 result.append(entry) | 180 result.append(entry) |
| 174 return result | 181 return result |
| 175 | 182 |
| 176 | 183 |
| 177 def AndroidListAvd(): | 184 def AndroidListAvd(): |
| 178 """Returns a list of available Android Virtual Devices.""" | 185 """Returns a list of available Android Virtual Devices.""" |
| 179 return ParseAndroidListAvdResult(utils.RunCommand(["android", "list", "avd"])) | 186 return ParseAndroidListAvdResult(utils.RunCommand(["android", "list", "avd"])) |
| 180 | 187 |
| 181 | 188 |
| 182 def FindAvd(avds, key): | 189 def FindAvd(avds, key): |
| 183 for avd in avds: | 190 for avd in avds: |
| 184 if avd['Name'] == key: | 191 if avd['Name'] == key: |
| 185 return avd | 192 return avd |
| 186 return None | 193 return None |
| 187 | 194 |
| 188 | 195 |
| 189 def CreateAvd(avdName, abi): | 196 def CreateAvd(avdName, abi): |
| 190 out = utils.RunCommand(["android", "create", "avd", "--name", avdName, | 197 out = utils.RunCommand(["android", "create", "avd", "--name", avdName, |
| 191 "--target", TargetForAbi(abi), '--abi', abi], | 198 "--target", TargetForAbi(abi), '--abi', abi], |
| 192 input="no\n") | 199 input="no\n") |
| 193 if out.find('Created AVD ') < 0: | 200 if out.find('Created AVD ') < 0: |
| 194 if VERBOSE: | 201 if VERBOSE: |
| 195 sys.stderr.write('Could not create AVD:\n%s\n' % out) | 202 sys.stderr.write('Could not create AVD:\n%s\n' % out) |
| 196 raise Exception('Could not create AVD') | 203 raise utils.Error('Could not create AVD') |
| 197 | 204 |
| 198 | 205 |
| 199 def AvdExists(avdName): | 206 def AvdExists(avdName): |
| 200 avdList = AndroidListAvd() | 207 avdList = AndroidListAvd() |
| 201 return FindAvd(avdList, avdName) is not None | 208 return FindAvd(avdList, avdName) is not None |
| 202 | 209 |
| 203 | 210 |
| 204 def EnsureAvdExists(avdName, abi): | 211 def EnsureAvdExists(avdName, abi): |
| 205 if AvdExists(avdName): | 212 if AvdExists(avdName): |
| 206 return | 213 return |
| 207 if VERBOSE: | 214 if VERBOSE: |
| 208 sys.stderr.write('Checking SDK packages...\n') | 215 sys.stderr.write('Checking SDK packages...\n') |
| 209 if EnsureAndroidSdkPackagesInstalled(abi): | 216 if EnsureAndroidSdkPackagesInstalled(abi): |
| 210 # Installing a new package could have made a previously invalid AVD valid | 217 # Installing a new package could have made a previously invalid AVD valid |
| 211 if AvdExists(avdName): | 218 if AvdExists(avdName): |
| 212 return | 219 return |
| 213 CreateAvd(avdName, abi) | 220 CreateAvd(avdName, abi) |
| 214 | 221 |
| 215 def dumpenv(map): | |
| 216 e = map.keys() | |
| 217 e.sort() | |
| 218 for k in e: | |
| 219 sys.stderr.write("%s: %s\n" % (k, map[k])) | |
| 220 | |
| 221 | 222 |
| 222 def StartEmulator(abi, avdName, pollFn): | 223 def StartEmulator(abi, avdName, pollFn): |
| 223 """ | 224 """ |
| 224 Start an emulator for a given abi and svdName. | 225 Start an emulator for a given abi and svdName. |
| 225 | 226 |
| 226 Echo the emulator's stderr and stdout output to our stderr. | 227 Echo the emulator's stderr and stdout output to our stderr. |
| 227 | 228 |
| 228 Call pollFn repeatedly until it returns False. Leave the emulator running | 229 Call pollFn repeatedly until it returns False. Leave the emulator running |
| 229 when we return. | 230 when we return. |
| 230 | 231 |
| 231 Implementation note: Normally we would call the 'emulator' binary, which | 232 Implementation note: Normally we would call the 'emulator' binary, which |
| 232 is a wrapper that launches the appropriate abi-specific emulator. But there | 233 is a wrapper that launches the appropriate abi-specific emulator. But there |
| 233 is a bug that causes the emulator to exit immediately with a result code of | 234 is a bug that causes the emulator to exit immediately with a result code of |
| 234 -11 if run from a ssh shell or a NX Machine shell. (And only if called from | 235 -11 if run from a ssh shell or a No Machine shell. (And only if called from |
| 235 three levels of nested python scripts.) Calling the ABI-specific versions | 236 three levels of nested python scripts.) Calling the ABI-specific versions |
| 236 of the emulator directly works around this bug. | 237 of the emulator directly works around this bug. |
| 237 """ | 238 """ |
| 238 emulatorName = {'x86' : 'emulator-x86', 'armeabi-v7a': 'emulator-arm'}[abi] | 239 emulatorName = {'x86' : 'emulator-x86', 'armeabi-v7a': 'emulator-arm'}[abi] |
| 239 command = [emulatorName, '-avd', avdName, '-no-boot-anim', '-no-window'] | 240 command = [emulatorName, '-avd', avdName, '-no-boot-anim', '-no-window'] |
| 240 utils.RunCommand(command, pollFn=pollFn, killOnEarlyReturn=False, | 241 utils.RunCommand(command, pollFn=pollFn, killOnEarlyReturn=False, |
| 241 outStream=sys.stderr, errStream=sys.stderr) | 242 outStream=sys.stderr, errStream=sys.stderr) |
| 242 | 243 |
| 243 | 244 |
| 244 def ParseAndroidDevices(text): | 245 def ParseAndroidDevices(text): |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 os.environ['PATH'] = ':'.join([ | 303 os.environ['PATH'] = ':'.join([ |
| 303 os.environ['PATH'], android_sdk_tools, android_sdk_platform_tools]) | 304 os.environ['PATH'], android_sdk_tools, android_sdk_platform_tools]) |
| 304 # Remove any environment variables that would affect our build. | 305 # Remove any environment variables that would affect our build. |
| 305 for i in ['ANDROID_NDK_ROOT', 'ANDROID_SDK_ROOT', 'ANDROID_TOOLCHAIN', | 306 for i in ['ANDROID_NDK_ROOT', 'ANDROID_SDK_ROOT', 'ANDROID_TOOLCHAIN', |
| 306 'AR', 'BUILDTYPE', 'CC', 'CXX', 'GYP_DEFINES', | 307 'AR', 'BUILDTYPE', 'CC', 'CXX', 'GYP_DEFINES', |
| 307 'LD_LIBRARY_PATH', 'LINK', 'MAKEFLAGS', 'MAKELEVEL', | 308 'LD_LIBRARY_PATH', 'LINK', 'MAKEFLAGS', 'MAKELEVEL', |
| 308 'MAKEOVERRIDES', 'MFLAGS', 'NM']: | 309 'MAKEOVERRIDES', 'MFLAGS', 'NM']: |
| 309 if i in os.environ: | 310 if i in os.environ: |
| 310 del os.environ[i] | 311 del os.environ[i] |
| 311 | 312 |
| 313 |
| 312 def FindAndroid(abi, bootstrap): | 314 def FindAndroid(abi, bootstrap): |
| 313 if VERBOSE: | 315 if VERBOSE: |
| 314 sys.stderr.write('Looking for an Android device running abi %s...\n' % abi) | 316 sys.stderr.write('Looking for an Android device running abi %s...\n' % abi) |
| 315 AddSdkToolsToPath() | 317 AddSdkToolsToPath() |
| 316 device = FindAndroidRunning(abi) | 318 device = FindAndroidRunning(abi) |
| 317 if device is None: | 319 if not device: |
| 318 if bootstrap: | 320 if bootstrap: |
| 319 if VERBOSE: | 321 if VERBOSE: |
| 320 sys.stderr.write("No emulator found, try to create one.\n") | 322 sys.stderr.write("No emulator found, try to create one.\n") |
| 321 avdName = 'dart-build-%s' % abi | 323 avdName = 'dart-build-%s' % abi |
| 322 EnsureAvdExists(avdName, abi) | 324 EnsureAvdExists(avdName, abi) |
| 323 | 325 |
| 324 # It takes a while to start up an emulator. | 326 # It takes a while to start up an emulator. |
| 325 # Provide feedback while we wait. | 327 # Provide feedback while we wait. |
| 326 pollResult = [None] | 328 pollResult = [None] |
| 327 def pollFunction(): | 329 def pollFunction(): |
| (...skipping 22 matching lines...) Expand all Loading... |
| 350 | 352 |
| 351 try: | 353 try: |
| 352 device = FindAndroid(options.abi, options.bootstrap) | 354 device = FindAndroid(options.abi, options.bootstrap) |
| 353 if device != None: | 355 if device != None: |
| 354 sys.stdout.write("%s\n" % device) | 356 sys.stdout.write("%s\n" % device) |
| 355 return 0 | 357 return 0 |
| 356 else: | 358 else: |
| 357 if VERBOSE: | 359 if VERBOSE: |
| 358 sys.stderr.write('Could not find device\n') | 360 sys.stderr.write('Could not find device\n') |
| 359 return 2 | 361 return 2 |
| 360 except Exception as e: | 362 except utils.Error as e: |
| 361 sys.stderr.write("error: %s\n" % e) | 363 sys.stderr.write("error: %s\n" % e) |
| 362 if DEBUG: | 364 if DEBUG: |
| 363 traceback.print_exc(file=sys.stderr) | 365 traceback.print_exc(file=sys.stderr) |
| 364 return -1 | 366 return -1 |
| 365 | 367 |
| 366 | 368 |
| 367 if __name__ == '__main__': | 369 if __name__ == '__main__': |
| 368 sys.exit(Main()) | 370 sys.exit(Main()) |
| OLD | NEW |