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
23import os
24import re
25import sys
26import shutil
27import argparse
28
29import common
30
31def getStoreKeyPasswords (filename):
32	f			= open(filename)
33	storepass	= None
34	keypass		= None
35	for line in f:
36		m = re.search('([a-z]+)\s*\=\s*"([^"]+)"', line)
37		if m != None:
38			if m.group(1) == "storepass":
39				storepass = m.group(2)
40			elif m.group(1) == "keypass":
41				keypass = m.group(2)
42	f.close()
43	if storepass == None or keypass == None:
44		common.die("Could not read signing key passwords")
45	return (storepass, keypass)
46
47def getNativeBuildDir (buildRoot, nativeLib, buildType):
48	buildName = "%s-%d-%s" % (buildType.lower(), nativeLib.apiVersion, nativeLib.abiVersion)
49	return os.path.normpath(os.path.join(buildRoot, "native", buildName))
50
51def getAssetsDir (buildRoot, nativeLib, buildType):
52	return os.path.join(getNativeBuildDir(buildRoot, nativeLib, buildType), "assets")
53
54def buildNative (buildRoot, libTargetDir, nativeLib, buildType):
55	deqpDir		= os.path.normpath(os.path.join(common.ANDROID_DIR, ".."))
56	buildDir	= getNativeBuildDir(buildRoot, nativeLib, buildType)
57	libsDir		= os.path.join(libTargetDir, nativeLib.abiVersion)
58	srcLibFile	= os.path.join(buildDir, common.NATIVE_LIB_NAME)
59	dstLibFile	= os.path.join(libsDir, common.NATIVE_LIB_NAME)
60
61	# Make build directory if necessary
62	if not os.path.exists(buildDir):
63		os.makedirs(buildDir)
64		toolchainFile = '%s/framework/delibs/cmake/toolchain-android-%s.cmake' % (deqpDir, common.ANDROID_NDK_TOOLCHAIN_VERSION)
65		common.execArgsInDirectory([
66				'cmake',
67				'-G%s' % common.CMAKE_GENERATOR,
68				'-DCMAKE_TOOLCHAIN_FILE=%s' % toolchainFile,
69				'-DANDROID_NDK_HOST_OS=%s' % common.ANDROID_NDK_HOST_OS,
70				'-DANDROID_NDK_PATH=%s' % common.ANDROID_NDK_PATH,
71				'-DANDROID_ABI=%s' % nativeLib.abiVersion,
72				'-DDE_ANDROID_API=%s' % nativeLib.apiVersion,
73				'-DCMAKE_BUILD_TYPE=%s' % buildType,
74				'-DDEQP_TARGET=android',
75				deqpDir
76			], buildDir)
77
78	common.execArgsInDirectory(['cmake', '--build', '.'] + common.EXTRA_BUILD_ARGS, buildDir)
79
80	if not os.path.exists(libsDir):
81		os.makedirs(libsDir)
82
83	shutil.copyfile(srcLibFile, dstLibFile)
84
85	# Copy gdbserver for debugging
86	if buildType.lower() == "debug":
87		srcGdbserverPath = os.path.join(common.ANDROID_NDK_PATH,
88										'prebuilt',
89										nativeLib.prebuiltDir,
90										'gdbserver',
91										'gdbserver')
92		dstGdbserverPath = os.path.join(libsDir, 'gdbserver')
93		shutil.copyfile(srcGdbserverPath, dstGdbserverPath)
94	else:
95		assert not os.path.exists(os.path.join(libsDir, "gdbserver"))
96
97def buildApp (buildRoot, androidBuildType, javaApi):
98	appDir	= os.path.join(buildRoot, "package")
99
100	# Set up app
101	os.chdir(appDir)
102
103	manifestSrcPath = os.path.normpath(os.path.join(common.ANDROID_DIR, "package", "AndroidManifest.xml"))
104	manifestDstPath = os.path.normpath(os.path.join(appDir, "AndroidManifest.xml"))
105
106	# Build dir can be the Android dir, in which case the copy is not needed.
107	if manifestSrcPath != manifestDstPath:
108		shutil.copy(manifestSrcPath, manifestDstPath)
109
110	common.execArgs([
111			common.ANDROID_BIN,
112			'update', 'project',
113			'--name', 'dEQP',
114			'--path', '.',
115			'--target', javaApi,
116		])
117
118	# Build
119	common.execArgs([
120			common.ANT_BIN,
121			androidBuildType,
122			"-Dsource.dir=" + os.path.join(common.ANDROID_DIR, "package", "src"),
123			"-Dresource.absolute.dir=" + os.path.join(common.ANDROID_DIR, "package", "res")
124		])
125
126def signApp (keystore, keyname, storepass, keypass):
127	os.chdir(os.path.join(common.ANDROID_DIR, "package"))
128	common.execArgs([
129			common.JARSIGNER_BIN,
130			'-keystore', keystore,
131			'-storepass', storepass,
132			'-keypass', keypass,
133			'-sigfile', 'CERT',
134			'-digestalg', 'SHA1',
135			'-sigalg', 'MD5withRSA',
136			'-signedjar', 'bin/dEQP-unaligned.apk',
137			'bin/dEQP-release-unsigned.apk',
138			keyname
139		])
140	common.execArgs([
141			common.ZIPALIGN_BIN,
142			'-f', '4',
143			'bin/dEQP-unaligned.apk',
144			'bin/dEQP-release.apk'
145		])
146
147def build (buildRoot=common.ANDROID_DIR, androidBuildType='debug', nativeBuildType="Release", javaApi=common.ANDROID_JAVA_API, doParallelBuild=False):
148	curDir = os.getcwd()
149
150	try:
151		assetsSrcDir = getAssetsDir(buildRoot, common.NATIVE_LIBS[0], nativeBuildType)
152		assetsDstDir = os.path.join(buildRoot, "package", "assets")
153
154		# Remove assets from the first build dir where we copy assets from
155		# to avoid collecting cruft there.
156		if os.path.exists(assetsSrcDir):
157			shutil.rmtree(assetsSrcDir)
158		if os.path.exists(assetsDstDir):
159			shutil.rmtree(assetsDstDir)
160
161		# Remove old libs dir to avoid collecting out-of-date versions
162		# of libs for ABIs not built this time.
163		libTargetDir = os.path.join(buildRoot, "package", "libs")
164		if os.path.exists(libTargetDir):
165			shutil.rmtree(libTargetDir)
166
167		# Build native code
168		nativeBuildArgs = [(buildRoot, libTargetDir, nativeLib, nativeBuildType) for nativeLib in common.NATIVE_LIBS]
169		if doParallelBuild:
170			common.parallelApply(buildNative, nativeBuildArgs)
171		else:
172			common.serialApply(buildNative, nativeBuildArgs)
173
174		# Copy assets
175		if os.path.exists(assetsSrcDir):
176			shutil.copytree(assetsSrcDir, assetsDstDir)
177
178		# Build java code and .apk
179		buildApp(buildRoot, androidBuildType, javaApi)
180
181	finally:
182		# Restore working dir
183		os.chdir(curDir)
184
185def dumpConfig ():
186	print " "
187	for entry in common.CONFIG_STRINGS:
188		print "%-30s : %s" % (entry[0], entry[1])
189	print " "
190
191if __name__ == "__main__":
192	nativeBuildTypes = ['Release', 'Debug', 'MinSizeRel', 'RelWithAsserts', 'RelWithDebInfo']
193	androidBuildTypes = ['debug', 'release']
194
195	parser = argparse.ArgumentParser()
196	parser.add_argument('--android-build-type', dest='androidBuildType', choices=androidBuildTypes, default='debug', help="Build type for android project..")
197	parser.add_argument('--native-build-type', dest='nativeBuildType', default="RelWithAsserts", choices=nativeBuildTypes, help="Build type passed to cmake when building native code.")
198	parser.add_argument('--build-root', dest='buildRoot', default=common.ANDROID_DIR, help="Root directory for storing build results.")
199	parser.add_argument('--dump-config', dest='dumpConfig', action='store_true', help="Print out all configurations variables")
200	parser.add_argument('--java-api', dest='javaApi', default=common.ANDROID_JAVA_API, help="Set the API signature for the java build.")
201	parser.add_argument('-p', '--parallel-build', dest='parallelBuild', action="store_true", help="Build native libraries in parallel.")
202
203	args = parser.parse_args()
204
205	if args.dumpConfig:
206		dumpConfig()
207
208	build(buildRoot=os.path.abspath(args.buildRoot), androidBuildType=args.androidBuildType, nativeBuildType=args.nativeBuildType, javaApi=args.javaApi, doParallelBuild=args.parallelBuild)
209