blob: a1583670e49d7071f16a13618b5478ef6f5cd350 [file] [log] [blame]
# -*- 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="", failOnNonZeroExit=True):
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 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], 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
NativeLib(21, "x86_64", 'android-x86_64'), # x86_64
]
ANDROID_JAVA_API = "android-22"
NATIVE_LIB_NAME = "libdeqp.so"
def makeNdkVersionString (version):
minorVersionString = (chr(ord('a') + version[1]) if version[1] > 0 else "")
return "r%d%s" % (version[0], minorVersionString)
ANDROID_NDK_VERSION = (11,0)
ANDROID_NDK_VERSION_STRING = makeNdkVersionString(ANDROID_NDK_VERSION)
def selectNDKPath ():
candidates = [
os.path.expanduser("~/android-ndk-" + ANDROID_NDK_VERSION_STRING),
"C:/android/android-ndk-" + ANDROID_NDK_VERSION_STRING,
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]