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 |