Home | History | Annotate | Download | only in scripts
      1 # -*- coding: utf-8 -*-
      2 
      3 #-------------------------------------------------------------------------
      4 # drawElements Quality Program utilities
      5 # --------------------------------------
      6 #
      7 # Copyright 2015 The Android Open Source Project
      8 #
      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
     12 #
     13 #      http://www.apache.org/licenses/LICENSE-2.0
     14 #
     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.
     20 #
     21 #-------------------------------------------------------------------------
     22 
     23 import os
     24 import re
     25 import sys
     26 import shlex
     27 import subprocess
     28 import multiprocessing
     29 import string
     30 
     31 try:
     32 	import threading
     33 except ImportError:
     34 	import dummy_threading as threading
     35 
     36 class NativeLib:
     37 	def __init__ (self, apiVersion, abiVersion, prebuiltDir):
     38 		self.apiVersion		= apiVersion
     39 		self.abiVersion		= abiVersion
     40 		self.prebuiltDir	= prebuiltDir
     41 
     42 	def __str__ (self):
     43 		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)
     44 
     45 	def __repr__ (self):
     46 		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)
     47 
     48 
     49 def getPlatform ():
     50 	if sys.platform.startswith('linux'):
     51 		return 'linux'
     52 	else:
     53 		return sys.platform
     54 
     55 def selectByOS (variants):
     56 	platform = getPlatform()
     57 	if platform in variants:
     58 		return variants[platform]
     59 	elif 'other' in variants:
     60 		return variants['other']
     61 	else:
     62 		raise Exception("No configuration for '%s'" % platform)
     63 
     64 def isExecutable (path):
     65 	return os.path.isfile(path) and os.access(path, os.X_OK)
     66 
     67 def which (binName):
     68 	for path in os.environ['PATH'].split(os.pathsep):
     69 		path = path.strip('"')
     70 		fullPath = os.path.join(path, binName)
     71 		if isExecutable(fullPath):
     72 			return fullPath
     73 
     74 	return None
     75 
     76 def isBinaryInPath (binName):
     77 	return which(binName) != None
     78 
     79 def selectFirstExistingBinary (filenames):
     80 	for filename in filenames:
     81 		if filename != None and isExecutable(filename):
     82 			return filename
     83 
     84 	return None
     85 
     86 def selectFirstExistingDir (paths):
     87 	for path in paths:
     88 		if path != None and os.path.isdir(path):
     89 			return path
     90 
     91 	return None
     92 
     93 def die (msg):
     94 	print msg
     95 	exit(-1)
     96 
     97 def shellquote(s):
     98 	return '"%s"' % s.replace('\\', '\\\\').replace('"', '\"').replace('$', '\$').replace('`', '\`')
     99 
    100 def execute (commandLine):
    101 	args	= shlex.split(commandLine)
    102 	retcode	= subprocess.call(args)
    103 	if retcode != 0:
    104 		raise Exception("Failed to execute '%s', got %d" % (commandLine, retcode))
    105 
    106 def execArgs (args):
    107 	# Make sure previous stdout prints have been written out.
    108 	sys.stdout.flush()
    109 	retcode	= subprocess.call(args)
    110 	if retcode != 0:
    111 		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
    112 
    113 def execArgsInDirectory (args, cwd, linePrefix=""):
    114 
    115 	def readApplyPrefixAndPrint (source, prefix, sink):
    116 		while True:
    117 			line = source.readline()
    118 			if len(line) == 0: # EOF
    119 				break;
    120 			sink.write(prefix + line)
    121 
    122 	process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    123 	stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout))
    124 	stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr))
    125 	stdoutJob.start()
    126 	stderrJob.start()
    127 	retcode = process.wait()
    128 	if retcode != 0:
    129 		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
    130 
    131 def serialApply(f, argsList):
    132 	for args in argsList:
    133 		f(*args)
    134 
    135 def parallelApply(f, argsList):
    136 	class ErrorCode:
    137 		def __init__ (self):
    138 			self.error = None;
    139 
    140 	def applyAndCaptureError (func, args, errorCode):
    141 		try:
    142 			func(*args)
    143 		except:
    144 			errorCode.error = sys.exc_info()
    145 
    146 	errorCode = ErrorCode()
    147 	jobs = []
    148 	for args in argsList:
    149 		job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode))
    150 		job.start()
    151 		jobs.append(job)
    152 
    153 	for job in jobs:
    154 		job.join()
    155 
    156 	if errorCode.error:
    157 		raise errorCode.error[0], errorCode.error[1], errorCode.error[2]
    158 
    159 class Device:
    160 	def __init__(self, serial, product, model, device):
    161 		self.serial		= serial
    162 		self.product	= product
    163 		self.model		= model
    164 		self.device		= device
    165 
    166 	def __str__ (self):
    167 		return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)
    168 
    169 def getDevices (adb):
    170 	proc = subprocess.Popen([adb, 'devices', '-l'], stdout=subprocess.PIPE)
    171 	(stdout, stderr) = proc.communicate()
    172 
    173 	if proc.returncode != 0:
    174 		raise Exception("adb devices -l failed, got %d" % proc.returncode)
    175 
    176 	ptrn = re.compile(r'^([a-zA-Z0-9]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
    177 	devices = []
    178 	for line in stdout.splitlines()[1:]:
    179 		if len(line.strip()) == 0:
    180 			continue
    181 
    182 		m = ptrn.match(line)
    183 		if m == None:
    184 			print "WARNING: Failed to parse device info '%s'" % line
    185 			continue
    186 
    187 		devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))
    188 
    189 	return devices
    190 
    191 def getWin32Generator ():
    192 	if which("jom.exe") != None:
    193 		return "NMake Makefiles JOM"
    194 	else:
    195 		return "NMake Makefiles"
    196 
    197 def isNinjaSupported ():
    198 	return which("ninja") != None
    199 
    200 def getUnixGenerator ():
    201 	if isNinjaSupported():
    202 		return "Ninja"
    203 	else:
    204 		return "Unix Makefiles"
    205 
    206 def getExtraBuildArgs (generator):
    207 	if generator == "Unix Makefiles":
    208 		return ["--", "-j%d" % multiprocessing.cpu_count()]
    209 	else:
    210 		return []
    211 
    212 NDK_HOST_OS_NAMES = [
    213 	"windows",
    214 	"windows-x86_64",
    215 	"darwin-x86",
    216 	"darwin-x86_64",
    217 	"linux-x86",
    218 	"linux-x86_64"
    219 ]
    220 
    221 def getNDKHostOsName (ndkPath):
    222 	for name in NDK_HOST_OS_NAMES:
    223 		if os.path.exists(os.path.join(ndkPath, "prebuilt", name)):
    224 			return name
    225 
    226 	raise Exception("Couldn't determine NDK host OS")
    227 
    228 # deqp/android path
    229 ANDROID_DIR				= os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
    230 
    231 # Build configuration
    232 NATIVE_LIBS				= [
    233 		#		  API		ABI				prebuiltsDir
    234 		NativeLib(13,		"armeabi-v7a",	'android-arm'),		# ARM v7a ABI
    235 		NativeLib(13,		"x86",			'android-x86'),		# x86
    236 		NativeLib(21,		"arm64-v8a",	'android-arm64'),	# ARM64 v8a ABI
    237 	]
    238 
    239 ANDROID_JAVA_API		= "android-22"
    240 NATIVE_LIB_NAME			= "libdeqp.so"
    241 
    242 def selectNDKPath ():
    243 	candidates =  [
    244 		os.path.expanduser("~/android-ndk-r11"),
    245 		"C:/android/android-ndk-r11",
    246 		os.environ.get("ANDROID_NDK_PATH", None), # If not defined, return None
    247 	]
    248 
    249 	ndkPath = selectFirstExistingDir(candidates)
    250 
    251 	if ndkPath == None:
    252 		raise Exception("None of NDK directory candidates exist: %s. Check ANDROID_NDK_PATH in common.py" % candidates)
    253 
    254 	return ndkPath
    255 
    256 def noneSafePathJoin (*components):
    257 	if None in components:
    258 		return None
    259 	return os.path.join(*components)
    260 
    261 
    262 # NDK paths
    263 ANDROID_NDK_PATH				= selectNDKPath()
    264 ANDROID_NDK_HOST_OS				= getNDKHostOsName(ANDROID_NDK_PATH)
    265 ANDROID_NDK_TOOLCHAIN_VERSION	= "r11" # Toolchain file is selected based on this
    266 
    267 # Native code build settings
    268 CMAKE_GENERATOR			= selectByOS({
    269 		'win32':	getWin32Generator(),
    270 		'other':	getUnixGenerator()
    271 	})
    272 EXTRA_BUILD_ARGS		= getExtraBuildArgs(CMAKE_GENERATOR)
    273 
    274 # SDK paths
    275 ANDROID_SDK_PATH		= selectFirstExistingDir([
    276 		os.environ.get("ANDROID_SDK_PATH", None),
    277 		os.path.expanduser("~/android-sdk-linux"),
    278 		os.path.expanduser("~/android-sdk-mac_x86"),
    279 		"C:/android/android-sdk-windows",
    280 	])
    281 ANDROID_BIN				= selectFirstExistingBinary([
    282 		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android"),
    283 		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android.bat"),
    284 		which('android'),
    285 	])
    286 ADB_BIN					= selectFirstExistingBinary([
    287 		which('adb'), # \note Prefer adb in path to avoid version issues on dev machines
    288 		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb"),
    289 		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb.exe"),
    290 	])
    291 ZIPALIGN_BIN			= selectFirstExistingBinary([
    292 		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign"),
    293 		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign.exe"),
    294 		which('zipalign'),
    295 	])
    296 JARSIGNER_BIN			= which('jarsigner')
    297 
    298 # Apache ant
    299 ANT_BIN					= selectFirstExistingBinary([
    300 		which('ant'),
    301 		"C:/android/apache-ant-1.8.4/bin/ant.bat",
    302 		"C:/android/apache-ant-1.9.2/bin/ant.bat",
    303 		"C:/android/apache-ant-1.9.3/bin/ant.bat",
    304 		"C:/android/apache-ant-1.9.4/bin/ant.bat",
    305 	])
    306 
    307 def makeNameValueTuple (name):
    308 	return (name, str(eval(name)))
    309 
    310 CONFIG_VAR_NAMES = [
    311 		"ANDROID_DIR",
    312 		"NATIVE_LIBS",
    313 		"ANDROID_JAVA_API",
    314 		"NATIVE_LIB_NAME",
    315 		"ANDROID_NDK_PATH",
    316 		"ANDROID_NDK_HOST_OS",
    317 		"ANDROID_NDK_TOOLCHAIN_VERSION",
    318 		"CMAKE_GENERATOR",
    319 		"EXTRA_BUILD_ARGS",
    320 		"ANDROID_SDK_PATH",
    321 		"ANDROID_BIN",
    322 		"ADB_BIN",
    323 		"ZIPALIGN_BIN",
    324 		"JARSIGNER_BIN",
    325 		"ANT_BIN",
    326 	]
    327 CONFIG_STRINGS = [makeNameValueTuple(x) for x in CONFIG_VAR_NAMES]
    328