# -*- 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]