327 lines
8.6 KiB
Python
327 lines
8.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
#-------------------------------------------------------------------------
|
|
# drawElements Quality Program utilities
|
|
# --------------------------------------
|
|
#
|
|
# Copyright 2015 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 shlex
|
|
import subprocess
|
|
import multiprocessing
|
|
import string
|
|
|
|
try:
|
|
import threading
|
|
except ImportError:
|
|
import dummy_threading as threading
|
|
|
|
class NativeLib:
|
|
def __init__ (self, apiVersion, abiVersion, prebuiltDir):
|
|
self.apiVersion = apiVersion
|
|
self.abiVersion = abiVersion
|
|
self.prebuiltDir = prebuiltDir
|
|
|
|
def __str__ (self):
|
|
return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)
|
|
|
|
def __repr__ (self):
|
|
return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)
|
|
|
|
|
|
def getPlatform ():
|
|
if sys.platform.startswith('linux'):
|
|
return 'linux'
|
|
else:
|
|
return sys.platform
|
|
|
|
def selectByOS (variants):
|
|
platform = getPlatform()
|
|
if platform in variants:
|
|
return variants[platform]
|
|
elif 'other' in variants:
|
|
return variants['other']
|
|
else:
|
|
raise Exception("No configuration for '%s'" % platform)
|
|
|
|
def isExecutable (path):
|
|
return os.path.isfile(path) and os.access(path, os.X_OK)
|
|
|
|
def which (binName):
|
|
for path in os.environ['PATH'].split(os.pathsep):
|
|
path = path.strip('"')
|
|
fullPath = os.path.join(path, binName)
|
|
if isExecutable(fullPath):
|
|
return fullPath
|
|
|
|
return None
|
|
|
|
def isBinaryInPath (binName):
|
|
return which(binName) != None
|
|
|
|
def selectFirstExistingBinary (filenames):
|
|
for filename in filenames:
|
|
if filename != None and isExecutable(filename):
|
|
return filename
|
|
|
|
return None
|
|
|
|
def selectFirstExistingDir (paths):
|
|
for path in paths:
|
|
if path != None and os.path.isdir(path):
|
|
return path
|
|
|
|
return None
|
|
|
|
def die (msg):
|
|
print msg
|
|
exit(-1)
|
|
|
|
def shellquote(s):
|
|
return '"%s"' % s.replace('\\', '\\\\').replace('"', '\"').replace('$', '\$').replace('`', '\`')
|
|
|
|
def execute (commandLine):
|
|
args = shlex.split(commandLine)
|
|
retcode = subprocess.call(args)
|
|
if retcode != 0:
|
|
raise Exception("Failed to execute '%s', got %d" % (commandLine, retcode))
|
|
|
|
def execArgs (args):
|
|
# Make sure previous stdout prints have been written out.
|
|
sys.stdout.flush()
|
|
retcode = subprocess.call(args)
|
|
if retcode != 0:
|
|
raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
|
|
|
|
def execArgsInDirectory (args, cwd, linePrefix=""):
|
|
|
|
def readApplyPrefixAndPrint (source, prefix, sink):
|
|
while True:
|
|
line = source.readline()
|
|
if len(line) == 0: # EOF
|
|
break;
|
|
sink.write(prefix + line)
|
|
|
|
process = subprocess.Popen(args, cwd=cwd, 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 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], errorCode.error[2]
|
|
|
|
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 (adb):
|
|
proc = subprocess.Popen([adb, '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)
|
|
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 getWin32Generator ():
|
|
if which("jom.exe") != None:
|
|
return "NMake Makefiles JOM"
|
|
else:
|
|
return "NMake Makefiles"
|
|
|
|
def isNinjaSupported ():
|
|
return which("ninja") != None
|
|
|
|
def getUnixGenerator ():
|
|
if isNinjaSupported():
|
|
return "Ninja"
|
|
else:
|
|
return "Unix Makefiles"
|
|
|
|
def getExtraBuildArgs (generator):
|
|
if generator == "Unix Makefiles":
|
|
return ["--", "-j%d" % multiprocessing.cpu_count()]
|
|
else:
|
|
return []
|
|
|
|
NDK_HOST_OS_NAMES = [
|
|
"windows",
|
|
"windows-x86_64",
|
|
"darwin-x86",
|
|
"darwin-x86_64",
|
|
"linux-x86",
|
|
"linux-x86_64"
|
|
]
|
|
|
|
def getNDKHostOsName (ndkPath):
|
|
for name in NDK_HOST_OS_NAMES:
|
|
if os.path.exists(os.path.join(ndkPath, "prebuilt", name)):
|
|
return name
|
|
|
|
raise Exception("Couldn't determine NDK host OS")
|
|
|
|
# deqp/android path
|
|
ANDROID_DIR = os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
|
|
|
# Build configuration
|
|
NATIVE_LIBS = [
|
|
# API ABI prebuiltsDir
|
|
NativeLib(13, "armeabi-v7a", 'android-arm'), # ARM v7a ABI
|
|
NativeLib(13, "x86", 'android-x86'), # x86
|
|
NativeLib(21, "arm64-v8a", 'android-arm64'), # ARM64 v8a ABI
|
|
]
|
|
|
|
ANDROID_JAVA_API = "android-22"
|
|
NATIVE_LIB_NAME = "libdeqp.so"
|
|
|
|
def selectNDKPath ():
|
|
candidates = [
|
|
os.path.expanduser("~/android-ndk-r11"),
|
|
"C:/android/android-ndk-r11",
|
|
os.environ.get("ANDROID_NDK_PATH", None), # If not defined, return None
|
|
]
|
|
|
|
ndkPath = selectFirstExistingDir(candidates)
|
|
|
|
if ndkPath == None:
|
|
raise Exception("None of NDK directory candidates exist: %s. Check ANDROID_NDK_PATH in common.py" % candidates)
|
|
|
|
return ndkPath
|
|
|
|
def noneSafePathJoin (*components):
|
|
if None in components:
|
|
return None
|
|
return os.path.join(*components)
|
|
|
|
|
|
# NDK paths
|
|
ANDROID_NDK_PATH = selectNDKPath()
|
|
ANDROID_NDK_HOST_OS = getNDKHostOsName(ANDROID_NDK_PATH)
|
|
ANDROID_NDK_TOOLCHAIN_VERSION = "r11" # Toolchain file is selected based on this
|
|
|
|
# Native code build settings
|
|
CMAKE_GENERATOR = selectByOS({
|
|
'win32': getWin32Generator(),
|
|
'other': getUnixGenerator()
|
|
})
|
|
EXTRA_BUILD_ARGS = getExtraBuildArgs(CMAKE_GENERATOR)
|
|
|
|
# SDK paths
|
|
ANDROID_SDK_PATH = selectFirstExistingDir([
|
|
os.environ.get("ANDROID_SDK_PATH", None),
|
|
os.path.expanduser("~/android-sdk-linux"),
|
|
os.path.expanduser("~/android-sdk-mac_x86"),
|
|
"C:/android/android-sdk-windows",
|
|
])
|
|
ANDROID_BIN = selectFirstExistingBinary([
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android"),
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android.bat"),
|
|
which('android'),
|
|
])
|
|
ADB_BIN = selectFirstExistingBinary([
|
|
which('adb'), # \note Prefer adb in path to avoid version issues on dev machines
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb"),
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb.exe"),
|
|
])
|
|
ZIPALIGN_BIN = selectFirstExistingBinary([
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign"),
|
|
noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign.exe"),
|
|
which('zipalign'),
|
|
])
|
|
JARSIGNER_BIN = which('jarsigner')
|
|
|
|
# Apache ant
|
|
ANT_BIN = selectFirstExistingBinary([
|
|
which('ant'),
|
|
"C:/android/apache-ant-1.8.4/bin/ant.bat",
|
|
"C:/android/apache-ant-1.9.2/bin/ant.bat",
|
|
"C:/android/apache-ant-1.9.3/bin/ant.bat",
|
|
"C:/android/apache-ant-1.9.4/bin/ant.bat",
|
|
])
|
|
|
|
def makeNameValueTuple (name):
|
|
return (name, str(eval(name)))
|
|
|
|
CONFIG_VAR_NAMES = [
|
|
"ANDROID_DIR",
|
|
"NATIVE_LIBS",
|
|
"ANDROID_JAVA_API",
|
|
"NATIVE_LIB_NAME",
|
|
"ANDROID_NDK_PATH",
|
|
"ANDROID_NDK_HOST_OS",
|
|
"ANDROID_NDK_TOOLCHAIN_VERSION",
|
|
"CMAKE_GENERATOR",
|
|
"EXTRA_BUILD_ARGS",
|
|
"ANDROID_SDK_PATH",
|
|
"ANDROID_BIN",
|
|
"ADB_BIN",
|
|
"ZIPALIGN_BIN",
|
|
"JARSIGNER_BIN",
|
|
"ANT_BIN",
|
|
]
|
|
CONFIG_STRINGS = [makeNameValueTuple(x) for x in CONFIG_VAR_NAMES]
|