1# -*- coding: utf-8 -*-
2
3import os
4import re
5import sys
6import shlex
7import subprocess
8import multiprocessing
9
10class NativeLib:
11	def __init__ (self, apiVersion, abiVersion):
12		self.apiVersion	= apiVersion
13		self.abiVersion	= abiVersion
14
15def getPlatform ():
16	if sys.platform.startswith('linux'):
17		return 'linux'
18	else:
19		return sys.platform
20
21def selectByOS (variants):
22	platform = getPlatform()
23	if platform in variants:
24		return variants[platform]
25	elif 'other' in variants:
26		return variants['other']
27	else:
28		raise Exception("No configuration for '%s'" % platform)
29
30def isExecutable (path):
31	return os.path.isfile(path) and os.access(path, os.X_OK)
32
33def which (binName):
34	for path in os.environ['PATH'].split(os.pathsep):
35		path = path.strip('"')
36		fullPath = os.path.join(path, binName)
37		if isExecutable(fullPath):
38			return fullPath
39
40	return None
41
42def isBinaryInPath (binName):
43	return which(binName) != None
44
45def selectFirstExistingBinary (filenames):
46	for filename in filenames:
47		if filename != None and isExecutable(filename):
48			return filename
49
50	return None
51
52def selectFirstExistingDir (paths):
53	for path in paths:
54		if path != None and os.path.isdir(path):
55			return path
56
57	return None
58
59def die (msg):
60	print msg
61	exit(-1)
62
63def shellquote(s):
64	return '"%s"' % s.replace('\\', '\\\\').replace('"', '\"').replace('$', '\$').replace('`', '\`')
65
66def execute (commandLine):
67	args	= shlex.split(commandLine)
68	retcode	= subprocess.call(args)
69	if retcode != 0:
70		raise Exception("Failed to execute '%s', got %d" % (commandLine, retcode))
71
72def execArgs (args):
73	retcode	= subprocess.call(args)
74	if retcode != 0:
75		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))
76
77class Device:
78	def __init__(self, serial, product, model, device):
79		self.serial		= serial
80		self.product	= product
81		self.model		= model
82		self.device		= device
83
84	def __str__ (self):
85		return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)
86
87def getDevices (adb):
88	proc = subprocess.Popen([adb, 'devices', '-l'], stdout=subprocess.PIPE)
89	(stdout, stderr) = proc.communicate()
90
91	if proc.returncode != 0:
92		raise Exception("adb devices -l failed, got %d" % retcode)
93
94	ptrn = re.compile(r'^([a-zA-Z0-9]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
95	devices = []
96	for line in stdout.splitlines()[1:]:
97		if len(line.strip()) == 0:
98			continue
99
100		m = ptrn.match(line)
101		if m == None:
102			print "WARNING: Failed to parse device info '%s'" % line
103			continue
104
105		devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))
106
107	return devices
108
109def getWin32Generator ():
110	if which("jom.exe") != None:
111		return "NMake Makefiles JOM"
112	else:
113		return "NMake Makefiles"
114
115def isNinjaSupported ():
116	return which("ninja") != None
117
118def getUnixGenerator ():
119	if isNinjaSupported():
120		return "Ninja"
121	else:
122		return "Unix Makefiles"
123
124def getExtraBuildArgs (generator):
125	if generator == "Unix Makefiles":
126		return ["--", "-j%d" % multiprocessing.cpu_count()]
127	else:
128		return []
129
130NDK_HOST_OS_NAMES = [
131	"windows",
132	"windows_x86-64",
133	"darwin-x86",
134	"darwin-x86-64",
135	"linux-x86",
136	"linux-x86_64"
137]
138
139def getNDKHostOsName (ndkPath):
140	for name in NDK_HOST_OS_NAMES:
141		if os.path.exists(os.path.join(ndkPath, "prebuilt", name)):
142			return name
143
144	raise Exception("Couldn't determine NDK host OS")
145
146# deqp/android path
147ANDROID_DIR				= os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
148
149# Build configuration
150NATIVE_LIBS				= [
151		#		  API		ABI
152		NativeLib(13,		"armeabi-v7a"),		# ARM v7a ABI
153		NativeLib(13,		"x86"),				# x86
154		NativeLib(21,		"arm64-v8a"),		# ARM64 v8a ABI
155	]
156ANDROID_JAVA_API		= "android-13"
157NATIVE_LIB_NAME			= "libdeqp.so"
158
159# NDK paths
160ANDROID_NDK_PATH		= selectFirstExistingDir([
161		os.path.expanduser("~/android-ndk-r10c"),
162		"C:/android/android-ndk-r10c",
163	])
164ANDROID_NDK_HOST_OS				= getNDKHostOsName(ANDROID_NDK_PATH)
165ANDROID_NDK_TOOLCHAIN_VERSION	= "r10c" # Toolchain file is selected based on this
166
167# Native code build settings
168CMAKE_GENERATOR			= selectByOS({
169		'win32':	getWin32Generator(),
170		'other':	getUnixGenerator()
171	})
172EXTRA_BUILD_ARGS		= getExtraBuildArgs(CMAKE_GENERATOR)
173
174# SDK paths
175ANDROID_SDK_PATH		= selectFirstExistingDir([
176		os.path.expanduser("~/android-sdk-linux"),
177		os.path.expanduser("~/android-sdk-mac_x86"),
178		"C:/android/android-sdk-windows",
179	])
180ANDROID_BIN				= selectFirstExistingBinary([
181		os.path.join(ANDROID_SDK_PATH, "tools", "android"),
182		os.path.join(ANDROID_SDK_PATH, "tools", "android.bat"),
183		which('android'),
184	])
185ADB_BIN					= selectFirstExistingBinary([
186		which('adb'), # \note Prefer adb in path to avoid version issues on dev machines
187		os.path.join(ANDROID_SDK_PATH, "platform-tools", "adb"),
188		os.path.join(ANDROID_SDK_PATH, "platform-tools", "adb.exe"),
189	])
190ZIPALIGN_BIN			= selectFirstExistingBinary([
191		os.path.join(ANDROID_SDK_PATH, "tools", "zipalign"),
192		os.path.join(ANDROID_SDK_PATH, "tools", "zipalign.exe"),
193		which('zipalign'),
194	])
195JARSIGNER_BIN			= which('jarsigner')
196
197# Apache ant
198ANT_BIN					= selectFirstExistingBinary([
199		which('ant'),
200		"C:/android/apache-ant-1.8.4/bin/ant.bat",
201		"C:/android/apache-ant-1.9.2/bin/ant.bat",
202		"C:/android/apache-ant-1.9.3/bin/ant.bat",
203		"C:/android/apache-ant-1.9.4/bin/ant.bat",
204	])
205