1 # -*- coding: utf-8 -*-
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
7 # Copyright 2017 The Android Open Source Project
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 #-------------------------------------------------------------------------
23 # \todo [2017-04-10 pyry]
24 # * Use smarter asset copy in main build
25 # * cmake -E copy_directory doesn't copy timestamps which will cause
26 # assets to be always re-packaged
27 # * Consider adding an option for downloading SDK & NDK
36 import xml.etree.ElementTree
38 # Import from <root>/scripts
39 sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
41 from build.common import *
42 from build.config import *
43 from build.build import *
46 def __init__(self, path):
48 self.buildToolsVersion = SDKEnv.selectBuildToolsVersion(self.path)
51 def getBuildToolsVersions (path):
52 buildToolsPath = os.path.join(path, "build-tools")
55 if os.path.exists(buildToolsPath):
56 for item in os.listdir(buildToolsPath):
57 m = re.match(r'^([0-9]+)\.([0-9]+)\.([0-9]+)$', item)
59 versions.append((int(m.group(1)), int(m.group(2)), int(m.group(3))))
64 def selectBuildToolsVersion (path):
65 preferred = [(25, 0, 2)]
66 versions = SDKEnv.getBuildToolsVersions(path)
68 if len(versions) == 0:
71 for candidate in preferred:
72 if candidate in versions:
79 def getPlatformLibrary (self, apiVersion):
80 return os.path.join(self.path, "platforms", "android-%d" % apiVersion, "android.jar")
82 def getBuildToolsPath (self):
83 return os.path.join(self.path, "build-tools", "%d.%d.%d" % self.buildToolsVersion)
86 def __init__(self, path):
88 self.version = NDKEnv.detectVersion(self.path)
89 self.hostOsName = NDKEnv.detectHostOsName(self.path)
93 return ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
96 def getAbiPrebuiltsName (abiName):
98 "armeabi-v7a": 'android-arm',
99 "arm64-v8a": 'android-arm64',
100 "x86": 'android-x86',
101 "x86_64": 'android-x86_64',
104 if not abiName in prebuilts:
105 raise Exception("Unknown ABI: " + abiName)
107 return prebuilts[abiName]
110 def detectVersion (path):
111 propFilePath = os.path.join(path, "source.properties")
113 with open(propFilePath) as propFile:
114 for line in propFile:
115 keyValue = map(lambda x: string.strip(x), line.split("="))
116 if keyValue[0] == "Pkg.Revision":
117 versionParts = keyValue[1].split(".")
118 return tuple(map(int, versionParts[0:2]))
119 except Exception as e:
120 raise Exception("Failed to read source prop file '%s': %s" % (propFilePath, str(e)))
122 raise Exception("Failed to read source prop file '%s': unkown error")
124 raise Exception("Failed to detect NDK version (does %s/source.properties have Pkg.Revision?)" % path)
127 def isHostOsSupported (hostOsName):
128 os = HostInfo.getOs()
129 bits = HostInfo.getArchBits()
130 hostOsParts = hostOsName.split('-')
132 if len(hostOsParts) > 1:
133 assert(len(hostOsParts) == 2)
134 assert(hostOsParts[1] == "x86_64")
139 if os == HostInfo.OS_WINDOWS:
140 return hostOsParts[0] == 'windows'
141 elif os == HostInfo.OS_LINUX:
142 return hostOsParts[0] == 'linux'
143 elif os == HostInfo.OS_OSX:
144 return hostOsParts[0] == 'darwin'
146 raise Exception("Unhandled HostInfo.getOs() '%d'" % os)
149 def detectHostOsName (path):
159 for name in hostOsNames:
160 if os.path.exists(os.path.join(path, "prebuilt", name)):
163 raise Exception("Failed to determine NDK host OS")
166 def __init__(self, sdk, ndk):
171 def __init__(self, env, buildPath, abis, nativeBuildType, gtfTarget, verbose):
173 self.sourcePath = DEQP_DIR
174 self.buildPath = buildPath
178 self.nativeBuildType = nativeBuildType
179 self.gtfTarget = gtfTarget
180 self.verbose = verbose
181 self.cmakeGenerator = selectFirstAvailableGenerator([NINJA_GENERATOR, MAKEFILE_GENERATOR, NMAKE_GENERATOR])
184 if self.cmakeGenerator == None:
185 raise Exception("Failed to find build tools for CMake")
187 if not os.path.exists(self.env.ndk.path):
188 raise Exception("Android NDK not found at %s" % self.env.ndk.path)
190 if not NDKEnv.isHostOsSupported(self.env.ndk.hostOsName):
191 raise Exception("NDK '%s' is not supported on this machine" % self.env.ndk.hostOsName)
193 supportedNDKVersion = 11
194 if self.env.ndk.version[0] != supportedNDKVersion:
195 raise Exception("Android NDK version %d is not supported; build requires NDK version %d" % (self.env.ndk.version[0], supportedNDKVersion))
197 if self.env.sdk.buildToolsVersion == (0,0,0):
198 raise Exception("No build tools directory found at %s" % os.path.join(self.env.sdk.path, "build-tools"))
200 androidBuildTools = ["aapt", "zipalign", "dx"]
201 for tool in androidBuildTools:
202 if which(tool, [self.env.sdk.getBuildToolsPath()]) == None:
203 raise Exception("Missing Android build tool: %s" % toolPath)
205 requiredToolsInPath = ["javac", "jar", "jarsigner", "keytool"]
206 for tool in requiredToolsInPath:
207 if which(tool) == None:
208 raise Exception("%s not in PATH" % tool)
210 def log (config, msg):
214 def executeAndLog (config, args):
221 class ResolvablePathComponent:
225 class SourceRoot (ResolvablePathComponent):
226 def resolve (self, config):
227 return config.sourcePath
229 class BuildRoot (ResolvablePathComponent):
230 def resolve (self, config):
231 return config.buildPath
233 class NativeBuildPath (ResolvablePathComponent):
234 def __init__ (self, abiName):
235 self.abiName = abiName
237 def resolve (self, config):
238 return getNativeBuildPath(config, self.abiName)
240 class GeneratedResSourcePath (ResolvablePathComponent):
241 def __init__ (self, package):
242 self.package = package
244 def resolve (self, config):
245 packageComps = self.package.getPackageName(config).split('.')
246 packageDir = os.path.join(*packageComps)
248 return os.path.join(config.buildPath, self.package.getAppDirName(), "src", packageDir, "R.java")
250 def resolvePath (config, path):
253 for component in path:
254 if isinstance(component, ResolvablePathComponent):
255 resolvedComps.append(component.resolve(config))
257 resolvedComps.append(str(component))
259 return os.path.join(*resolvedComps)
261 def resolvePaths (config, paths):
262 return list(map(lambda p: resolvePath(config, p), paths))
268 def getInputs (self):
271 def getOutputs (self):
275 def expandPathsToFiles (paths):
277 Expand mixed list of file and directory paths into a flattened list
278 of files. Any non-existent input paths are preserved as is.
281 def getFiles (dirPath):
282 for root, dirs, files in os.walk(dirPath):
284 yield os.path.join(root, file)
288 if os.path.isdir(path):
289 files += list(getFiles(path))
295 def isUpToDate (self, config):
296 inputs = resolvePaths(config, self.getInputs())
297 outputs = resolvePaths(config, self.getOutputs())
299 assert len(inputs) > 0 and len(outputs) > 0
301 expandedInputs = BuildStep.expandPathsToFiles(inputs)
302 expandedOutputs = BuildStep.expandPathsToFiles(outputs)
304 existingInputs = filter(os.path.exists, expandedInputs)
305 existingOutputs = filter(os.path.exists, expandedOutputs)
307 if len(existingInputs) != len(expandedInputs):
308 for file in expandedInputs:
309 if file not in existingInputs:
310 print "ERROR: Missing input file: %s" % file
311 die("Missing input files")
313 if len(existingOutputs) != len(expandedOutputs):
314 return False # One or more output files are missing
316 lastInputChange = max(map(os.path.getmtime, existingInputs))
317 firstOutputChange = min(map(os.path.getmtime, existingOutputs))
319 return lastInputChange <= firstOutputChange
322 die("BuildStep.update() not implemented")
324 def getNativeBuildPath (config, abiName):
325 return os.path.join(config.buildPath, "%s-%s-%d" % (abiName, config.nativeBuildType, config.nativeApi))
327 def buildNativeLibrary (config, abiName):
328 def makeNDKVersionString (version):
329 minorVersionString = (chr(ord('a') + version[1]) if version[1] > 0 else "")
330 return "r%d%s" % (version[0], minorVersionString)
332 def getBuildArgs (config, abiName):
333 toolchain = 'ndk-%s' % makeNDKVersionString((config.env.ndk.version[0], 0))
334 return ['-DDEQP_TARGET=android',
335 '-DDEQP_TARGET_TOOLCHAIN=%s' % toolchain,
336 '-DCMAKE_C_FLAGS=-Werror',
337 '-DCMAKE_CXX_FLAGS=-Werror',
338 '-DANDROID_NDK_HOST_OS=%s' % config.env.ndk.hostOsName,
339 '-DANDROID_NDK_PATH=%s' % config.env.ndk.path,
340 '-DANDROID_ABI=%s' % abiName,
341 '-DDE_ANDROID_API=%s' % config.nativeApi,
342 '-DGLCTS_GTF_TARGET=%s' % config.gtfTarget]
344 nativeBuildPath = getNativeBuildPath(config, abiName)
345 buildConfig = BuildConfig(nativeBuildPath, config.nativeBuildType, getBuildArgs(config, abiName))
347 build(buildConfig, config.cmakeGenerator, ["deqp"])
349 def executeSteps (config, steps):
351 if not step.isUpToDate(config):
354 def parsePackageName (manifestPath):
355 tree = xml.etree.ElementTree.parse(manifestPath)
357 if not 'package' in tree.getroot().attrib:
358 raise Exception("'package' attribute missing from root element in %s" % manifestPath)
360 return tree.getroot().attrib['package']
362 class PackageDescription:
363 def __init__ (self, appDirName, appName, hasResources = True):
364 self.appDirName = appDirName
365 self.appName = appName
366 self.hasResources = hasResources
368 def getAppName (self):
371 def getAppDirName (self):
372 return self.appDirName
374 def getPackageName (self, config):
375 manifestPath = resolvePath(config, self.getManifestPath())
377 return parsePackageName(manifestPath)
379 def getManifestPath (self):
380 return [SourceRoot(), "android", self.appDirName, "AndroidManifest.xml"]
382 def getResPath (self):
383 return [SourceRoot(), "android", self.appDirName, "res"]
385 def getSourcePaths (self):
387 [SourceRoot(), "android", self.appDirName, "src"]
390 def getAssetsPath (self):
391 return [BuildRoot(), self.appDirName, "assets"]
393 def getClassesJarPath (self):
394 return [BuildRoot(), self.appDirName, "bin", "classes.jar"]
396 def getClassesDexPath (self):
397 return [BuildRoot(), self.appDirName, "bin", "classes.dex"]
399 def getAPKPath (self):
400 return [BuildRoot(), self.appDirName, "bin", self.appName + ".apk"]
402 # Build step implementations
404 class BuildNativeLibrary (BuildStep):
405 def __init__ (self, abi):
408 def isUpToDate (self, config):
411 def update (self, config):
412 log(config, "BuildNativeLibrary: %s" % self.abi)
413 buildNativeLibrary(config, self.abi)
415 class GenResourcesSrc (BuildStep):
416 def __init__ (self, package):
417 self.package = package
419 def getInputs (self):
420 return [self.package.getResPath(), self.package.getManifestPath()]
422 def getOutputs (self):
423 return [[GeneratedResSourcePath(self.package)]]
425 def update (self, config):
426 aaptPath = which("aapt", [config.env.sdk.getBuildToolsPath()])
427 dstDir = os.path.dirname(resolvePath(config, [GeneratedResSourcePath(self.package)]))
429 if not os.path.exists(dstDir):
432 executeAndLog(config, [
437 "-S", resolvePath(config, self.package.getResPath()),
438 "-M", resolvePath(config, self.package.getManifestPath()),
439 "-J", resolvePath(config, [BuildRoot(), self.package.getAppDirName(), "src"]),
440 "-I", config.env.sdk.getPlatformLibrary(config.javaApi)
443 # Builds classes.jar from *.java files
444 class BuildJavaSource (BuildStep):
445 def __init__ (self, package, libraries = []):
446 self.package = package
447 self.libraries = libraries
449 def getSourcePaths (self):
450 srcPaths = self.package.getSourcePaths()
452 if self.package.hasResources:
453 srcPaths.append([BuildRoot(), self.package.getAppDirName(), "src"]) # Generated sources
457 def getInputs (self):
458 inputs = self.getSourcePaths()
460 for lib in self.libraries:
461 inputs.append(lib.getClassesJarPath())
465 def getOutputs (self):
466 return [self.package.getClassesJarPath()]
468 def update (self, config):
469 srcPaths = resolvePaths(config, self.getSourcePaths())
470 srcFiles = BuildStep.expandPathsToFiles(srcPaths)
471 jarPath = resolvePath(config, self.package.getClassesJarPath())
472 objPath = resolvePath(config, [BuildRoot(), self.package.getAppDirName(), "obj"])
473 classPaths = [objPath] + [resolvePath(config, lib.getClassesJarPath()) for lib in self.libraries]
474 pathSep = ";" if HostInfo.getOs() == HostInfo.OS_WINDOWS else ":"
476 if os.path.exists(objPath):
477 shutil.rmtree(objPath)
481 for srcFile in srcFiles:
482 executeAndLog(config, [
487 "-bootclasspath", config.env.sdk.getPlatformLibrary(config.javaApi),
488 "-classpath", pathSep.join(classPaths),
489 "-sourcepath", pathSep.join(srcPaths),
493 if not os.path.exists(os.path.dirname(jarPath)):
494 os.makedirs(os.path.dirname(jarPath))
497 pushWorkingDir(objPath)
498 executeAndLog(config, [
507 class BuildDex (BuildStep):
508 def __init__ (self, package, libraries):
509 self.package = package
510 self.libraries = libraries
512 def getInputs (self):
513 return [self.package.getClassesJarPath()] + [lib.getClassesJarPath() for lib in self.libraries]
515 def getOutputs (self):
516 return [self.package.getClassesDexPath()]
518 def update (self, config):
519 dxPath = which("dx", [config.env.sdk.getBuildToolsPath()])
520 srcPaths = resolvePaths(config, self.getInputs())
521 dexPath = resolvePath(config, self.package.getClassesDexPath())
522 jarPaths = [resolvePath(config, self.package.getClassesJarPath())]
524 for lib in self.libraries:
525 jarPaths.append(resolvePath(config, lib.getClassesJarPath()))
527 executeAndLog(config, [
533 class CreateKeystore (BuildStep):
535 self.keystorePath = [BuildRoot(), "debug.keystore"]
537 def getOutputs (self):
538 return [self.keystorePath]
540 def isUpToDate (self, config):
541 return os.path.exists(resolvePath(config, self.keystorePath))
543 def update (self, config):
544 executeAndLog(config, [
547 "-keystore", resolvePath(config, self.keystorePath),
548 "-storepass", "android",
549 "-alias", "androiddebugkey",
550 "-keypass", "android",
553 "-validity", "10000",
554 "-dname", "CN=, OU=, O=, L=, S=, C=",
557 # Builds APK without code
558 class BuildBaseAPK (BuildStep):
559 def __init__ (self, package, libraries = []):
560 self.package = package
561 self.libraries = libraries
562 self.dstPath = [BuildRoot(), self.package.getAppDirName(), "tmp", "base.apk"]
564 def getResPaths (self):
566 for pkg in [self.package] + self.libraries:
568 paths.append(pkg.getResPath())
571 def getInputs (self):
572 return [self.package.getManifestPath()] + self.getResPaths()
574 def getOutputs (self):
575 return [self.dstPath]
577 def update (self, config):
578 aaptPath = which("aapt", [config.env.sdk.getBuildToolsPath()])
579 dstPath = resolvePath(config, self.dstPath)
581 if not os.path.exists(os.path.dirname(dstPath)):
582 os.makedirs(os.path.dirname(dstPath))
588 "-M", resolvePath(config, self.package.getManifestPath()),
589 "-I", config.env.sdk.getPlatformLibrary(config.javaApi),
593 for resPath in self.getResPaths():
594 args += ["-S", resolvePath(config, resPath)]
599 executeAndLog(config, args)
601 def addFilesToAPK (config, apkPath, baseDir, relFilePaths):
602 aaptPath = which("aapt", [config.env.sdk.getBuildToolsPath()])
605 pushWorkingDir(baseDir)
607 workQueue = list(relFilePaths)
609 while len(workQueue) > 0:
610 batchSize = min(len(workQueue), maxBatchSize)
611 items = workQueue[0:batchSize]
613 executeAndLog(config, [
619 del workQueue[0:batchSize]
623 def addFileToAPK (config, apkPath, baseDir, relFilePath):
624 addFilesToAPK(config, apkPath, baseDir, [relFilePath])
626 class AddJavaToAPK (BuildStep):
627 def __init__ (self, package):
628 self.package = package
629 self.srcPath = BuildBaseAPK(self.package).getOutputs()[0]
630 self.dstPath = [BuildRoot(), self.package.getAppDirName(), "tmp", "with-java.apk"]
632 def getInputs (self):
635 self.package.getClassesDexPath(),
638 def getOutputs (self):
639 return [self.dstPath]
641 def update (self, config):
642 srcPath = resolvePath(config, self.srcPath)
643 dstPath = resolvePath(config, self.getOutputs()[0])
644 dexPath = resolvePath(config, self.package.getClassesDexPath())
646 shutil.copyfile(srcPath, dstPath)
647 addFileToAPK(config, dstPath, os.path.dirname(dexPath), os.path.basename(dexPath))
649 class AddAssetsToAPK (BuildStep):
650 def __init__ (self, package, abi):
651 self.package = package
652 self.buildPath = [NativeBuildPath(abi)]
653 self.srcPath = AddJavaToAPK(self.package).getOutputs()[0]
654 self.dstPath = [BuildRoot(), self.package.getAppDirName(), "tmp", "with-assets.apk"]
656 def getInputs (self):
659 self.buildPath + ["assets"]
662 def getOutputs (self):
663 return [self.dstPath]
666 def getAssetFiles (buildPath):
667 allFiles = BuildStep.expandPathsToFiles([os.path.join(buildPath, "assets")])
668 return [os.path.relpath(p, buildPath) for p in allFiles]
670 def update (self, config):
671 srcPath = resolvePath(config, self.srcPath)
672 dstPath = resolvePath(config, self.getOutputs()[0])
673 buildPath = resolvePath(config, self.buildPath)
674 assetFiles = AddAssetsToAPK.getAssetFiles(buildPath)
676 shutil.copyfile(srcPath, dstPath)
678 addFilesToAPK(config, dstPath, buildPath, assetFiles)
680 class AddNativeLibsToAPK (BuildStep):
681 def __init__ (self, package, abis):
682 self.package = package
684 self.srcPath = AddAssetsToAPK(self.package, "").getOutputs()[0]
685 self.dstPath = [BuildRoot(), self.package.getAppDirName(), "tmp", "with-native-libs.apk"]
687 def getInputs (self):
688 paths = [self.srcPath]
689 for abi in self.abis:
690 paths.append([NativeBuildPath(abi), "libdeqp.so"])
693 def getOutputs (self):
694 return [self.dstPath]
696 def update (self, config):
697 srcPath = resolvePath(config, self.srcPath)
698 dstPath = resolvePath(config, self.getOutputs()[0])
699 pkgPath = resolvePath(config, [BuildRoot(), self.package.getAppDirName()])
702 # Create right directory structure first
703 for abi in self.abis:
704 libSrcPath = resolvePath(config, [NativeBuildPath(abi), "libdeqp.so"])
705 libRelPath = os.path.join("lib", abi, "libdeqp.so")
706 libAbsPath = os.path.join(pkgPath, libRelPath)
708 if not os.path.exists(os.path.dirname(libAbsPath)):
709 os.makedirs(os.path.dirname(libAbsPath))
711 shutil.copyfile(libSrcPath, libAbsPath)
712 libFiles.append(libRelPath)
714 shutil.copyfile(srcPath, dstPath)
715 addFilesToAPK(config, dstPath, pkgPath, libFiles)
717 class SignAPK (BuildStep):
718 def __init__ (self, package):
719 self.package = package
720 self.srcPath = AddNativeLibsToAPK(self.package, []).getOutputs()[0]
721 self.dstPath = [BuildRoot(), self.package.getAppDirName(), "tmp", "signed.apk"]
722 self.keystorePath = CreateKeystore().getOutputs()[0]
724 def getInputs (self):
725 return [self.srcPath, self.keystorePath]
727 def getOutputs (self):
728 return [self.dstPath]
730 def update (self, config):
731 srcPath = resolvePath(config, self.srcPath)
732 dstPath = resolvePath(config, self.dstPath)
734 executeAndLog(config, [
736 "-keystore", resolvePath(config, self.keystorePath),
737 "-storepass", "android",
738 "-keypass", "android",
739 "-signedjar", dstPath,
744 def getBuildRootRelativeAPKPath (package):
745 return os.path.join(package.getAppDirName(), package.getAppName() + ".apk")
747 class FinalizeAPK (BuildStep):
748 def __init__ (self, package):
749 self.package = package
750 self.srcPath = SignAPK(self.package).getOutputs()[0]
751 self.dstPath = [BuildRoot(), getBuildRootRelativeAPKPath(self.package)]
752 self.keystorePath = CreateKeystore().getOutputs()[0]
754 def getInputs (self):
755 return [self.srcPath]
757 def getOutputs (self):
758 return [self.dstPath]
760 def update (self, config):
761 srcPath = resolvePath(config, self.srcPath)
762 dstPath = resolvePath(config, self.dstPath)
763 zipalignPath = os.path.join(config.env.sdk.getBuildToolsPath(), "zipalign")
765 executeAndLog(config, [
772 def getBuildStepsForPackage (abis, package, libraries = []):
777 # Build native code first
779 steps += [BuildNativeLibrary(abi)]
781 # Build library packages
782 for library in libraries:
783 if library.hasResources:
784 steps.append(GenResourcesSrc(library))
785 steps.append(BuildJavaSource(library))
787 # Build main package .java sources
788 if package.hasResources:
789 steps.append(GenResourcesSrc(package))
790 steps.append(BuildJavaSource(package, libraries))
791 steps.append(BuildDex(package, libraries))
794 steps.append(BuildBaseAPK(package, libraries))
795 steps.append(AddJavaToAPK(package))
797 # Add assets from first ABI
798 steps.append(AddAssetsToAPK(package, abis[0]))
800 # Add native libs to APK
801 steps.append(AddNativeLibsToAPK(package, abis))
804 steps.append(CreateKeystore())
805 steps.append(SignAPK(package))
806 steps.append(FinalizeAPK(package))
810 def getPackageAndLibrariesForTarget (target):
811 deqpPackage = PackageDescription("package", "dEQP")
812 ctsPackage = PackageDescription("openglcts", "Khronos-CTS", hasResources = False)
815 return (deqpPackage, [])
816 elif target == 'openglcts':
817 return (ctsPackage, [deqpPackage])
819 raise Exception("Uknown target '%s'" % target)
822 ndkBuildPath = which('ndk-build')
823 if ndkBuildPath != None:
824 return os.path.dirname(ndkBuildPath)
829 sdkBuildPath = which('android')
830 if sdkBuildPath != None:
831 return os.path.dirname(os.path.dirname(sdkBuildPath))
835 def getDefaultBuildRoot ():
836 return os.path.join(tempfile.gettempdir(), "deqp-android-build")
839 nativeBuildTypes = ['Release', 'Debug', 'MinSizeRel', 'RelWithAsserts', 'RelWithDebInfo']
840 defaultNDKPath = findNDK()
841 defaultSDKPath = findSDK()
842 defaultBuildRoot = getDefaultBuildRoot()
844 parser = argparse.ArgumentParser(os.path.basename(__file__),
845 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
846 parser.add_argument('--native-build-type',
847 dest='nativeBuildType',
848 default="RelWithAsserts",
849 choices=nativeBuildTypes,
850 help="Native code build type")
851 parser.add_argument('--build-root',
853 default=defaultBuildRoot,
854 help="Root build directory")
855 parser.add_argument('--abis',
857 default=",".join(NDKEnv.getKnownAbis()),
858 help="ABIs to build")
859 parser.add_argument('--sdk',
861 default=defaultSDKPath,
862 help="Android SDK path",
863 required=(True if defaultSDKPath == None else False))
864 parser.add_argument('--ndk',
866 default=defaultNDKPath,
867 help="Android NDK path",
868 required=(True if defaultNDKPath == None else False))
869 parser.add_argument('-v', '--verbose',
871 help="Verbose output",
874 parser.add_argument('--target',
877 choices=['deqp', 'openglcts'],
879 parser.add_argument('--kc-cts-target',
882 choices=['gles32', 'gles31', 'gles3', 'gles2', 'gl'],
883 help="KC-CTS (GTF) target API (only used in openglcts target)")
885 args = parser.parse_args()
887 def parseAbis (abisStr):
888 knownAbis = set(NDKEnv.getKnownAbis())
891 for abi in abisStr.split(','):
893 if not abi in knownAbis:
894 raise Exception("Unknown ABI: %s" % abi)
899 # Custom parsing & checks
901 args.abis = parseAbis(args.abis)
902 if len(args.abis) == 0:
903 raise Exception("--abis can't be empty")
904 except Exception as e:
905 print "ERROR: %s" % str(e)
911 if __name__ == "__main__":
914 ndk = NDKEnv(os.path.realpath(args.ndkPath))
915 sdk = SDKEnv(os.path.realpath(args.sdkPath))
916 buildPath = os.path.realpath(args.buildRoot)
917 env = Environment(sdk, ndk)
918 config = Configuration(env, buildPath, abis=args.abis, nativeBuildType=args.nativeBuildType, gtfTarget=args.gtfTarget, verbose=args.verbose)
922 except Exception as e:
923 print "ERROR: %s" % str(e)
925 print "Please check your configuration:"
926 print " --sdk=%s" % args.sdkPath
927 print " --ndk=%s" % args.ndkPath
930 pkg, libs = getPackageAndLibrariesForTarget(args.target)
931 steps = getBuildStepsForPackage(config.abis, pkg, libs)
933 executeSteps(config, steps)
936 print "Built %s" % os.path.join(buildPath, getBuildRootRelativeAPKPath(pkg))