| #!/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 |