You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
7.8 KiB

# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------
import os
import re
import sys
import argparse
import threading
import subprocess
from build_apk import findSDK
from build_apk import getDefaultBuildRoot
from build_apk import getPackageAndLibrariesForTarget
from build_apk import getBuildRootRelativeAPKPath
from build_apk import parsePackageName
# Import from <root>/scripts
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
from build.common import *
class Device:
def __init__(self, serial, product, model, device):
self.serial = serial
self.product = product
self.model = model
self.device = device
def __str__ (self):
return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)
def getDevices (adbPath):
proc = subprocess.Popen([adbPath, 'devices', '-l'], stdout=subprocess.PIPE)
(stdout, stderr) = proc.communicate()
if proc.returncode != 0:
raise Exception("adb devices -l failed, got %d" % proc.returncode)
ptrn = re.compile(r'^([a-zA-Z0-9\.\-:]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
devices = []
for line in stdout.splitlines()[1:]:
if len(line.strip()) == 0:
continue
m = ptrn.match(line.decode('utf-8'))
if m == None:
print("WARNING: Failed to parse device info '%s'" % line)
continue
devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))
return devices
def execWithPrintPrefix (args, linePrefix="", failOnNonZeroExit=True):
def readApplyPrefixAndPrint (source, prefix, sink):
while True:
line = source.readline()
if len(line) == 0: # EOF
break;
sink.write(prefix + line.decode('utf-8'))
process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout))
stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr))
stdoutJob.start()
stderrJob.start()
retcode = process.wait()
if failOnNonZeroExit and retcode != 0:
raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
def serialApply (f, argsList):
for args in argsList:
f(*args)
def parallelApply (f, argsList):
class ErrorCode:
def __init__ (self):
self.error = None;
def applyAndCaptureError (func, args, errorCode):
try:
func(*args)
except:
errorCode.error = sys.exc_info()
errorCode = ErrorCode()
jobs = []
for args in argsList:
job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode))
job.start()
jobs.append(job)
for job in jobs:
job.join()
if errorCode.error:
raise errorCode.error[0](errorCode.error[1]).with_traceback(errorCode.error[2])
def uninstall (adbPath, packageName, extraArgs = [], printPrefix=""):
print(printPrefix + "Removing existing %s...\n" % packageName,)
execWithPrintPrefix([adbPath] + extraArgs + [
'uninstall',
packageName
], printPrefix, failOnNonZeroExit=False)
print(printPrefix + "Remove complete\n",)
def install (adbPath, apkPath, extraArgs = [], printPrefix=""):
print(printPrefix + "Installing %s...\n" % apkPath,)
execWithPrintPrefix([adbPath] + extraArgs + [
'install',
'-g',
apkPath
], printPrefix)
print(printPrefix + "Install complete\n",)
def installToDevice (device, adbPath, packageName, apkPath, printPrefix=""):
if len(printPrefix) == 0:
print("Installing to %s (%s)...\n" % (device.serial, device.model), end='')
else:
print(printPrefix + "Installing to %s\n" % device.serial, end='')
uninstall(adbPath, packageName, ['-s', device.serial], printPrefix)
install(adbPath, apkPath, ['-s', device.serial], printPrefix)
def installToDevices (devices, doParallel, adbPath, packageName, apkPath):
padLen = max([len(device.model) for device in devices])+1
if doParallel:
parallelApply(installToDevice, [(device, adbPath, packageName, apkPath, ("(%s):%s" % (device.model, ' ' * (padLen - len(device.model))))) for device in devices]);
else:
serialApply(installToDevice, [(device, adbPath, packageName, apkPath) for device in devices]);
def installToAllDevices (doParallel, adbPath, packageName, apkPath):
devices = getDevices(adbPath)
installToDevices(devices, doParallel, adbPath, packageName, apkPath)
def getAPKPath (buildRootPath, target):
package = getPackageAndLibrariesForTarget(target)[0]
return os.path.join(buildRootPath, getBuildRootRelativeAPKPath(package))
def getPackageName (target):
package = getPackageAndLibrariesForTarget(target)[0]
manifestPath = os.path.join(DEQP_DIR, "android", package.appDirName, "AndroidManifest.xml")
return parsePackageName(manifestPath)
def findADB ():
adbInPath = which("adb")
if adbInPath != None:
return adbInPath
sdkPath = findSDK()
if sdkPath != None:
adbInSDK = os.path.join(sdkPath, "platform-tools", "adb")
if os.path.isfile(adbInSDK):
return adbInSDK
return None
def parseArgs ():
defaultADBPath = findADB()
defaultBuildRoot = getDefaultBuildRoot()
parser = argparse.ArgumentParser(os.path.basename(__file__),
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--build-root',
dest='buildRoot',
default=defaultBuildRoot,
help="Root build directory")
parser.add_argument('--adb',
dest='adbPath',
default=defaultADBPath,
help="ADB binary path",
required=(True if defaultADBPath == None else False))
parser.add_argument('--target',
dest='target',
help='Build target',
choices=['deqp', 'openglcts'],
default='deqp')
parser.add_argument('-p', '--parallel',
dest='doParallel',
action="store_true",
help="Install package in parallel")
parser.add_argument('-s', '--serial',
dest='serial',
type=str,
nargs='+',
help="Install package to device with serial number")
parser.add_argument('-a', '--all',
dest='all',
action="store_true",
help="Install to all devices")
return parser.parse_args()
if __name__ == "__main__":
args = parseArgs()
packageName = getPackageName(args.target)
apkPath = getAPKPath(args.buildRoot, args.target)
if not os.path.isfile(apkPath):
die("%s does not exist" % apkPath)
if args.all:
installToAllDevices(args.doParallel, args.adbPath, packageName, apkPath)
else:
if args.serial == None:
devices = getDevices(args.adbPath)
if len(devices) == 0:
die('No devices connected')
elif len(devices) == 1:
installToDevice(devices[0], args.adbPath, packageName, apkPath)
else:
print("More than one device connected:")
for i in range(0, len(devices)):
print("%3d: %16s %s" % ((i+1), devices[i].serial, devices[i].model))
deviceNdx = int(input("Choose device (1-%d): " % len(devices)))
installToDevice(devices[deviceNdx-1], args.adbPath, packageName, apkPath)
else:
devices = getDevices(args.adbPath)
devices = [dev for dev in devices if dev.serial in args.serial]
devSerials = [dev.serial for dev in devices]
notFounds = [serial for serial in args.serial if not serial in devSerials]
for notFound in notFounds:
print("Couldn't find device matching serial '%s'" % notFound)
installToDevices(devices, args.doParallel, args.adbPath, packageName, apkPath)