blob: 7d0a1fa488282b218638b5ae6a7664e2247a2ccf [file] [log] [blame]
#!/usr/bin/env python
"""Ninja toolchain abstraction for Android platform"""
import os
import subprocess
import toolchain
def make_target(toolchain, host, target):
return Android(toolchain, host, target)
class Android(object):
def __init__(self, toolchain, host, target):
self.host = host
if host.is_windows():
self.exe_suffix = '.exe'
else:
self.exe_suffix = ''
self.javaccmd = toolchain.mkdircmd('$outpath') + ' && $javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in'
self.dexcmd = '$dex --dex --output $out $in'
self.aaptcmd = toolchain.cdcmd('$apkbuildpath') + ' && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S res --debug-mode --no-crunch -J gen $aaptflags'
self.aaptdeploycmd = toolchain.cdcmd('$apkbuildpath') + ' && $aapt c -S res -C bin/res && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S bin/res -S res -J gen $aaptflags'
self.aaptaddcmd = toolchain.cdcmd('$apkbuildpath') + ' && ' + toolchain.copycmd('$apksource', '$apk' ) + ' && $aapt a $apk $apkaddfiles'
self.zipcmd = '$zip -r -9 $out $in $implicitin'
self.zipaligncmd = '$zipalign -f 4 $in $out'
self.codesigncmd = 'build/ninja/codesign.py --target $target --prefs codesign.json --zipfile $in --config $config --jarsigner $jarsigner $out'
if host.is_windows():
self.codesigncmd = 'python ' + self.codesigncmd
def initialize_toolchain(self):
self.ndkpath = os.getenv('NDK_HOME', '')
self.sdkpath = os.getenv('ANDROID_HOME', '')
self.sysroot = ''
self.platformversion = '21'
self.gcc_toolchainversion = '4.9'
self.javasdk = ''
self.archname = dict()
self.archname['x86'] = 'x86'
self.archname['x86-64'] = 'x86_64'
self.archname['arm6'] = 'arm'
self.archname['arm7'] = 'arm'
self.archname['arm64'] = 'arm64'
self.archname['mips'] = 'mips'
self.archname['mips64'] = 'mips64'
self.archpath = dict()
self.archpath['x86'] = 'x86'
self.archpath['x86-64'] = 'x86-64'
self.archpath['arm6'] = 'armeabi'
self.archpath['arm7'] = 'armeabi-v7a'
self.archpath['arm64'] = 'arm64-v8a'
self.archpath['mips'] = 'mips'
self.archpath['mips64'] = 'mips64'
self.gcc_toolchainname = dict()
self.gcc_toolchainname['x86'] = 'x86-' + self.gcc_toolchainversion
self.gcc_toolchainname['x86-64'] = 'x86_64-' + self.gcc_toolchainversion
self.gcc_toolchainname['arm6'] = 'arm-linux-androideabi-' + self.gcc_toolchainversion
self.gcc_toolchainname['arm7'] = 'arm-linux-androideabi-' + self.gcc_toolchainversion
self.gcc_toolchainname['arm64'] = 'aarch64-linux-android-' + self.gcc_toolchainversion
self.gcc_toolchainname['mips'] = 'mipsel-linux-android-' + self.gcc_toolchainversion
self.gcc_toolchainname['mips64'] = 'mips64el-linux-android-' + self.gcc_toolchainversion
self.gcc_toolchainprefix = dict()
self.gcc_toolchainprefix['x86'] = 'i686-linux-android-'
self.gcc_toolchainprefix['x86-64'] = 'x86_64-linux-android-'
self.gcc_toolchainprefix['arm6'] = 'arm-linux-androideabi-'
self.gcc_toolchainprefix['arm7'] = 'arm-linux-androideabi-'
self.gcc_toolchainprefix['arm64'] = 'aarch64-linux-android-'
self.gcc_toolchainprefix['mips'] = 'mipsel-linux-android-'
self.gcc_toolchainprefix['mips64'] = 'mips64el-linux-android-'
if self.host.is_windows():
if os.getenv('PROCESSOR_ARCHITECTURE', 'AMD64').find('64') != -1:
self.hostarchname = 'windows-x86_64'
else:
self.hostarchname = 'windows-x86'
elif self.host.is_linux():
localarch = toolchain.check_output(['uname', '-m'])
if localarch == 'x86_64':
self.hostarchname = 'linux-x86_64'
else:
self.hostarchname = 'linux-x86'
elif self.host.is_macos():
self.hostarchname = 'darwin-x86_64'
def build_toolchain(self):
buildtools_path = os.path.join(self.sdkpath, 'build-tools')
buildtools_list = [item for item in os.listdir(buildtools_path) if os.path.isdir(os.path.join(buildtools_path, item))]
buildtools_list.sort(key = lambda s: map(int, s.split('-')[0].split('.')))
self.buildtools_path = os.path.join(self.sdkpath, 'build-tools', buildtools_list[-1])
self.android_jar = os.path.join(self.sdkpath, 'platforms', 'android-' + self.platformversion, 'android.jar')
self.javac = 'javac'
self.jarsigner = 'jarsigner'
if self.javasdk != '':
self.javac = os.path.join(self.javasdk, 'bin', self.javac)
self.jarsigner = os.path.join(self.javasdk, 'bin', self.jarsigner)
if self.host.is_windows():
self.dex = os.path.join(self.buildtools_path, 'dx.bat')
else:
self.dex = os.path.join(self.buildtools_path, 'dx' + self.exe_suffix)
if not os.path.isfile(self.dex):
self.dex = os.path.join(self.sdkpath, 'tools', 'dx' + self.exe_suffix)
self.aapt = os.path.join(self.buildtools_path, 'aapt' + self.exe_suffix)
self.zipalign = os.path.join(self.buildtools_path, 'zipalign' + self.exe_suffix)
if not os.path.isfile( self.zipalign ):
self.zipalign = os.path.join(self.sdkpath, 'tools', 'zipalign' + self.exe_suffix)
def parse_prefs(self, prefs):
if 'android' in prefs:
androidprefs = prefs['android']
if 'ndkpath' in androidprefs:
self.ndkpath = os.path.expanduser(androidprefs['ndkpath'])
if 'sdkpath' in androidprefs:
self.sdkpath = os.path.expanduser(androidprefs['sdkpath'])
if 'platformversion' in androidprefs:
self.platformversion = androidprefs['platformversion']
if 'gccversion' in androidprefs:
self.gcc_toolchainversion = androidprefs['gccversion']
if 'javasdk' in androidprefs:
self.javasdk = androidprefs['javasdk']
def write_variables(self, writer):
writer.variable('ndk', self.ndkpath)
writer.variable('sdk', self.sdkpath)
writer.variable('sysroot', self.sysroot)
writer.variable('androidjar', self.android_jar )
writer.variable('apkbuildpath', '')
writer.variable('apk', '')
writer.variable('apksource', '')
writer.variable('apkaddfiles', '')
writer.variable('javac', self.javac)
writer.variable('dex', self.dex)
writer.variable('aapt', self.aapt)
writer.variable('zipalign', self.zipalign)
writer.variable('jarsigner', self.jarsigner)
writer.variable('aaptflags', '')
def write_rules(self, writer):
writer.rule('aapt', command = self.aaptcmd, description = 'AAPT $out')
writer.rule('aaptdeploy', command = self.aaptdeploycmd, description = 'AAPT $out')
writer.rule('aaptadd', command = self.aaptaddcmd, description = 'AAPT $out')
writer.rule('javac', command = self.javaccmd, description = 'JAVAC $in')
writer.rule('dex', command = self.dexcmd, description = 'DEX $out')
writer.rule('zip', command = self.zipcmd, description = 'ZIP $out')
writer.rule('zipalign', command = self.zipaligncmd, description = 'ZIPALIGN $out')
writer.rule('codesign', command = self.codesigncmd, description = 'CODESIGN $out')
def make_sysroot_path(self, arch):
return os.path.join(self.ndkpath, 'platforms', 'android-' + self.platformversion, 'arch-' + self.archname[arch])
def make_gcc_toolchain_path(self, arch):
return os.path.join(self.ndkpath, 'toolchains', self.gcc_toolchainname[arch], 'prebuilt', self.hostarchname)
def make_gcc_bin_path(self, arch):
return os.path.join(self.make_gcc_toolchain_path(arch), 'bin', self.gcc_toolchainprefix[arch])
def archname(self):
return self.archname
def archpath(self):
return self.archpath
def hostarchname(self):
return self.hostarchname
def apk(self, toolchain, writer, module, archbins, javasources, outpath, binname, basepath, config, implicit_deps, resources):
buildpath = os.path.join('$buildpath', config, 'apk', binname)
baseapkname = binname + ".base.apk"
unsignedapkname = binname + ".unsigned.apk"
unalignedapkname = binname + ".unaligned.apk"
apkname = binname + ".apk"
apkfiles = []
libfiles = []
locallibs = []
resfiles = []
manifestfile = []
writer.comment('Make APK')
for _, value in archbins.iteritems():
for archbin in value:
archpair = os.path.split(archbin)
libname = archpair[1]
arch = os.path.split(archpair[0])[1]
locallibpath = os.path.join('lib', self.archpath[arch], libname)
archpath = os.path.join(buildpath, locallibpath)
locallibs += [locallibpath + ' ']
libfiles += toolchain.copy(writer, archbin, archpath)
for resource in resources:
filename = os.path.split(resource)[1]
if filename == 'AndroidManifest.xml':
manifestfile = toolchain.copy(writer, os.path.join(basepath, module, resource), os.path.join(buildpath, 'AndroidManifest.xml'))
else:
restype = os.path.split(os.path.split(resource)[0])[1]
if restype == 'asset':
pass #todo: implement
else:
resfiles += toolchain.copy(writer, os.path.join(basepath, module, resource), os.path.join(buildpath, 'res', restype, filename))
#Make directories
gendir = toolchain.mkdir(writer, os.path.join(buildpath, 'gen'))
bindir = toolchain.mkdir(writer, os.path.join(buildpath, 'bin'))
binresdir = toolchain.mkdir(writer, os.path.join(buildpath, 'bin', 'res'), order_only = bindir)
alldirs = gendir + bindir + binresdir
aaptvars = [('apkbuildpath', buildpath), ('apk', baseapkname)]
aaptout = os.path.join(buildpath, baseapkname)
if config == 'deploy':
baseapkfile = writer.build(aaptout, 'aaptdeploy', manifestfile, variables = aaptvars, implicit = manifestfile + resfiles, order_only = alldirs)
else:
baseapkfile = writer.build(aaptout, 'aapt', manifestfile, variables = aaptvars, implicit = manifestfile + resfiles, order_only = alldirs)
#Compile java code
javafiles = []
localjava = []
if javasources != []:
#self.javaccmd = '$javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in'
#self.dexcmd = '$dex --dex --output $out $in'
javasourcepath = '.'
if self.host.is_windows():
javasourcepath += ';'
else:
javasourcepath += ':'
javasourcepath += os.path.join(buildpath, 'gen')
classpath = os.path.join(buildpath, 'classes')
javavars = [('outpath', classpath), ('sourcepath', javasourcepath)]
javaclasses = writer.build(classpath, 'javac', javasources, variables = javavars, implicit = baseapkfile)
localjava += ['classes.dex']
javafiles += writer.build(os.path.join(buildpath, 'classes.dex'), 'dex', classpath)
#Add native libraries and java classes to apk
aaptvars = [('apkbuildpath', buildpath), ('apk', unsignedapkname), ('apksource', baseapkname), ('apkaddfiles', toolchain.paths_forward_slash(locallibs + localjava))]
unsignedapkfile = writer.build(os.path.join(buildpath, unsignedapkname), 'aaptadd', baseapkfile, variables = aaptvars, implicit = libfiles + javafiles, order_only = alldirs)
#Sign the APK
codesignvars = [('config', config)]
unalignedapkfile = writer.build(os.path.join(buildpath, unalignedapkname), 'codesign', unsignedapkfile, variables = codesignvars)
#Run zipalign
outfile = writer.build(os.path.join(outpath, config, apkname), 'zipalign', unalignedapkfile)
return outfile