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 + [
133 print(printPrefix + "Install complete\n",)
135 def installToDevice (device, adbPath, packageName, apkPath, printPrefix=""):
136 if len(printPrefix) == 0:
137 print("Installing to %s (%s)...\n" % (device.serial, device.model), end='')
139 print(printPrefix + "Installing to %s\n" % device.serial, end='')
141 uninstall(adbPath, packageName, ['-s', device.serial], printPrefix)
142 install(adbPath, apkPath, ['-s', device.serial], printPrefix)
144 def installToDevices (devices, doParallel, adbPath, packageName, apkPath):
145 padLen = max([len(device.model) for device in devices])+1
147 parallelApply(installToDevice, [(device, adbPath, packageName, apkPath, ("(%s):%s" % (device.model, ' ' * (padLen - len(device.model))))) for device in devices]);
149 serialApply(installToDevice, [(device, adbPath, packageName, apkPath) for device in devices]);
151 def installToAllDevices (doParallel, adbPath, packageName, apkPath):
152 devices = getDevices(adbPath)
153 installToDevices(devices, doParallel, adbPath, packageName, apkPath)
155 def getAPKPath (buildRootPath, target):
156 package = getPackageAndLibrariesForTarget(target)[0]
157 return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package))
159 def getPackageName (target):
160 package = getPackageAndLibrariesForTarget(target)[0]
161 manifestPath = os.path.join(DEQP_DIR, "android", package.appDirName, "AndroidManifest.xml")
163 return parsePackageName(manifestPath)
166 adbInPath = which("adb")
167 if adbInPath != None:
172 adbInSDK = os.path.join(sdkPath, "platform-tools", "adb")
173 if os.path.isfile(adbInSDK):
179 defaultADBPath = findADB()
180 defaultBuildRoot = getDefaultBuildRoot()
182 parser = argparse.ArgumentParser(os.path.basename(__file__),
183 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
184 parser.add_argument('--build-root',
186 default=defaultBuildRoot,
187 help="Root build directory")
188 parser.add_argument('--adb',
190 default=defaultADBPath,
191 help="ADB binary path",
192 required=(True if defaultADBPath == None else False))
193 parser.add_argument('--target',
196 choices=['deqp', 'openglcts'],
198 parser.add_argument('-p', '--parallel',
201 help="Install package in parallel")
202 parser.add_argument('-s', '--serial',
206 help="Install package to device with serial number")
207 parser.add_argument('-a', '--all',
210 help="Install to all devices")
212 return parser.parse_args()
214 if __name__ == "__main__":
216 packageName = getPackageName(args.target)
217 apkPath = getAPKPath(args.buildRoot, args.target)
219 if not os.path.isfile(apkPath):
220 die("%s does not exist" % apkPath)
223 installToAllDevices(args.doParallel, args.adbPath, packageName, apkPath)
225 if args.serial == None:
226 devices = getDevices(args.adbPath)
227 if len(devices) == 0:
228 die('No devices connected')
229 elif len(devices) == 1:
230 installToDevice(devices[0], args.adbPath, packageName, apkPath)
232 print("More than one device connected:")
233 for i in range(0, len(devices)):
234 print("%3d: %16s %s" % ((i+1), devices[i].serial, devices[i].model))
236 deviceNdx = int(input("Choose device (1-%d): " % len(devices)))
237 installToDevice(devices[deviceNdx-1], args.adbPath, packageName, apkPath)
239 devices = getDevices(args.adbPath)
241 devices = [dev for dev in devices if dev.serial in args.serial]
242 devSerials = [dev.serial for dev in devices]
243 notFounds = [serial for serial in args.serial if not serial in devSerials]
245 for notFound in notFounds:
246 print("Couldn't find device matching serial '%s'" % notFound)
248 installToDevices(devices, args.doParallel, args.adbPath, packageName, apkPath)