1 # -*- coding: utf-8 -*-
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
7 # Copyright 2017 The Android Open Source Project
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
21 #-------------------------------------------------------------------------
30 from build_apk import findSDK
31 from build_apk import getDefaultBuildRoot
32 from build_apk import getPackageAndLibrariesForTarget
33 from build_apk import getBuildRootRelativeAPKPath
34 from build_apk import parsePackageName
36 # Import from <root>/scripts
37 sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
39 from build.common import *
42 def __init__(self, serial, product, model, device):
44 self.product = product
49 return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)
51 def getDevices (adbPath):
52 proc = subprocess.Popen([adbPath, 'devices', '-l'], stdout=subprocess.PIPE)
53 (stdout, stderr) = proc.communicate()
55 if proc.returncode != 0:
56 raise Exception("adb devices -l failed, got %d" % proc.returncode)
58 ptrn = re.compile(r'^([a-zA-Z0-9\.\-:]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
60 for line in stdout.splitlines()[1:]:
61 if len(line.strip()) == 0:
64 m = ptrn.match(line.decode('utf-8'))
66 print("WARNING: Failed to parse device info '%s'" % line)
69 devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))
73 def execWithPrintPrefix (args, linePrefix="", failOnNonZeroExit=True):
75 def readApplyPrefixAndPrint (source, prefix, sink):
77 line = source.readline()
78 if len(line) == 0: # EOF
80 sink.write(prefix + line.decode('utf-8'))
82 process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
83 stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout))
84 stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr))
87 retcode = process.wait()
88 if failOnNonZeroExit and retcode != 0:
89 raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
91 def serialApply (f, argsList):
95 def parallelApply (f, argsList):
100 def applyAndCaptureError (func, args, errorCode):
104 errorCode.error = sys.exc_info()
106 errorCode = ErrorCode()
108 for args in argsList:
109 job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode))
117 raise errorCode.error[0](errorCode.error[1]).with_traceback(errorCode.error[2])
119 def uninstall (adbPath, packageName, extraArgs = [], printPrefix=""):
120 print(printPrefix + "Removing existing %s...\n" % packageName,)
121 execWithPrintPrefix([adbPath] + extraArgs + [
124 ], printPrefix, failOnNonZeroExit=False)
125 print(printPrefix + "Remove complete\n",)
127 def install (adbPath, apkPath, extraArgs = [], printPrefix=""):
128 print(printPrefix + "Installing %s...\n" % apkPath,)
129 execWithPrintPrefix([adbPath] + extraArgs + [
134 print(printPrefix + "Install complete\n",)
136 def installToDevice (device, adbPath, packageName, apkPath, printPrefix=""):
137 if len(printPrefix) == 0:
138 print("Installing to %s (%s)...\n" % (device.serial, device.model), end='')
140 print(printPrefix + "Installing to %s\n" % device.serial, end='')
142 uninstall(adbPath, packageName, ['-s', device.serial], printPrefix)
143 install(adbPath, apkPath, ['-s', device.serial], printPrefix)
145 def installToDevices (devices, doParallel, adbPath, packageName, apkPath):
146 padLen = max([len(device.model) for device in devices])+1
148 parallelApply(installToDevice, [(device, adbPath, packageName, apkPath, ("(%s):%s" % (device.model, ' ' * (padLen - len(device.model))))) for device in devices]);
150 serialApply(installToDevice, [(device, adbPath, packageName, apkPath) for device in devices]);
152 def installToAllDevices (doParallel, adbPath, packageName, apkPath):
153 devices = getDevices(adbPath)
154 installToDevices(devices, doParallel, adbPath, packageName, apkPath)
156 def getAPKPath (buildRootPath, target):
157 package = getPackageAndLibrariesForTarget(target)[0]
158 return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package))
160 def getPackageName (target):
161 package = getPackageAndLibrariesForTarget(target)[0]
162 manifestPath = os.path.join(DEQP_DIR, "android", package.appDirName, "AndroidManifest.xml")
164 return parsePackageName(manifestPath)
167 adbInPath = which("adb")
168 if adbInPath != None:
173 adbInSDK = os.path.join(sdkPath, "platform-tools", "adb")
174 if os.path.isfile(adbInSDK):
180 defaultADBPath = findADB()
181 defaultBuildRoot = getDefaultBuildRoot()
183 parser = argparse.ArgumentParser(os.path.basename(__file__),
184 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
185 parser.add_argument('--build-root',
187 default=defaultBuildRoot,
188 help="Root build directory")
189 parser.add_argument('--adb',
191 default=defaultADBPath,
192 help="ADB binary path",
193 required=(True if defaultADBPath == None else False))
194 parser.add_argument('--target',
197 choices=['deqp', 'openglcts'],
199 parser.add_argument('-p', '--parallel',
202 help="Install package in parallel")
203 parser.add_argument('-s', '--serial',
207 help="Install package to device with serial number")
208 parser.add_argument('-a', '--all',
211 help="Install to all devices")
213 return parser.parse_args()
215 if __name__ == "__main__":
217 packageName = getPackageName(args.target)
218 apkPath = getAPKPath(args.buildRoot, args.target)
220 if not os.path.isfile(apkPath):
221 die("%s does not exist" % apkPath)
224 installToAllDevices(args.doParallel, args.adbPath, packageName, apkPath)
226 if args.serial == None:
227 devices = getDevices(args.adbPath)
228 if len(devices) == 0:
229 die('No devices connected')
230 elif len(devices) == 1:
231 installToDevice(devices[0], args.adbPath, packageName, apkPath)
233 print("More than one device connected:")
234 for i in range(0, len(devices)):
235 print("%3d: %16s %s" % ((i+1), devices[i].serial, devices[i].model))
237 deviceNdx = int(input("Choose device (1-%d): " % len(devices)))
238 installToDevice(devices[deviceNdx-1], args.adbPath, packageName, apkPath)
240 devices = getDevices(args.adbPath)
242 devices = [dev for dev in devices if dev.serial in args.serial]
243 devSerials = [dev.serial for dev in devices]
244 notFounds = [serial for serial in args.serial if not serial in devSerials]
246 for notFound in notFounds:
247 print("Couldn't find device matching serial '%s'" % notFound)
249 installToDevices(devices, args.doParallel, args.adbPath, packageName, apkPath)