#!/usr/bin/env python
from __future__ import print_function
import argparse
import collections
import os
import re
import subprocess
import sys
def detect_ndk_dir():
ndk_dir = os.getenv('NDK')
if not ndk_dir:
error_msg = '''error: NDK toolchain is required for this test case.
error:
error: Steps:
error: 1. Download NDK from https://developer.android.com/ndk/downloads/
error: 2. Unzip the archive (android-ndk-r15c-linux-x86_64.zip)
error: 3. Set environment variable NDK to the extracted directory
error: (export NDK=android-ndk-r15c)
error:'''
print(error_msg, file=sys.stderr)
raise ValueError('NDK toolchain not specified')
if not os.path.exists(ndk_dir):
raise ValueError('NDK toolchain not found')
return ndk_dir
def detect_api_level(ndk_dir):
try:
apis = []
pattern = re.compile('android-(\\d+)')
for name in os.listdir(os.path.join(ndk_dir, 'platforms')):
match = pattern.match(name)
if match:
apis.append(int(match.group(1)))
if not apis:
raise ValueError('failed to find latest api')
return 'android-{}'.format(max(apis))
except IOError:
raise ValueError('failed to find latest api')
def detect_host():
if sys.platform.startswith('linux'):
return 'linux-x86_64'
if sys.platform.startswith('darwin'):
return 'darwin-x86_64'
raise NotImplementedError('unknown host platform')
def get_gcc_dir(ndk_dir, arch, host):
return os.path.join(ndk_dir, 'toolchains', arch, 'prebuilt', host)
def get_clang_dir(ndk_dir, host):
return os.path.join(ndk_dir, 'toolchains', 'llvm', 'prebuilt', host)
def get_platform_dir(ndk_dir, api, subdirs):
return os.path.join(ndk_dir, 'platforms', api, *subdirs)
class Target(object):
def __init__(self, name, triple, cflags, ldflags, gcc_toolchain_dir,
clang_dir, ndk_include, ndk_lib):
self.name = name
self.target_triple = triple
self.target_cflags = cflags
self.target_ldflags = ldflags
self.gcc_toolchain_dir = gcc_toolchain_dir
self.clang_dir = clang_dir
self.ndk_include = ndk_include
self.ndk_lib = ndk_lib
def check_paths(self):
def check_path(path):
if os.path.exists(path):
return True
print('error: File not found:', path, file=sys.stderr)
return False
ld_exeutable = os.path.join(
self.gcc_toolchain_dir, 'bin', self.target_triple + '-ld')
success = check_path(self.gcc_toolchain_dir)
success &= check_path(ld_exeutable)
success &= check_path(self.clang_dir)
success &= check_path(self.ndk_include)
success &= check_path(self.ndk_lib)
return success
def compile(self, obj_file, src_file, cflags):
clang = os.path.join(self.clang_dir, 'bin', 'clang')
cmd = [clang, '-o', obj_file, '-c', src_file]
cmd.extend(['-fPIE', '-fPIC'])
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
cmd.extend(['-target', self.target_triple])
cmd.extend(['-isystem', self.ndk_include])
cmd.extend(cflags)
cmd.extend(self.target_cflags)
subprocess.check_call(cmd)
def link(self, out_file, obj_files, ldflags):
if '-shared' in ldflags:
crtbegin = os.path.join(self.ndk_lib, 'crtbegin_so.o')
crtend = os.path.join(self.ndk_lib, 'crtend_so.o')
else:
crtbegin = os.path.join(self.ndk_lib, 'crtbegin_static.o')
crtend = os.path.join(self.ndk_lib, 'crtend_android.o')
clang = os.path.join(self.clang_dir, 'bin', 'clang')
cmd = [clang, '-o', out_file]
cmd.extend(['-fPIE', '-fPIC', '-Wl,--no-undefined', '-nostdlib'])
cmd.append('-L' + self.ndk_lib)
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
cmd.extend(['-target', self.target_triple])
cmd.append(crtbegin)
cmd.extend(obj_files)
cmd.append(crtend)
cmd.extend(ldflags)
cmd.extend(self.target_ldflags)
if '-shared' not in ldflags:
cmd.append('-Wl,-pie')
subprocess.check_call(cmd)
def create_targets(ndk_dir=None, api=None, host=None):
if ndk_dir is None:
ndk_dir = detect_ndk_dir()
if api is None:
api = detect_api_level(ndk_dir)
if host is None:
host = detect_host()
targets = collections.OrderedDict()
targets['arm'] = Target(
'arm', 'arm-linux-androideabi', [],[],
get_gcc_dir(ndk_dir, 'arm-linux-androideabi-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-arm', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-arm', 'usr', 'lib']))
targets['arm64'] = Target(
'arm64', 'aarch64-linux-android', [], [],
get_gcc_dir(ndk_dir, 'aarch64-linux-android-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-arm64', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-arm64', 'usr', 'lib']))
targets['x86'] = Target(
'x86', 'i686-linux-android', ['-m32'], ['-m32'],
get_gcc_dir(ndk_dir, 'x86-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-x86', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-x86', 'usr', 'lib']))
targets['x86_64'] = Target(
'x86_64', 'x86_64-linux-android', ['-m64'], ['-m64'],
get_gcc_dir(ndk_dir, 'x86_64-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-x86_64', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-x86_64', 'usr', 'lib64']))
targets['mips'] = Target(
'mips', 'mipsel-linux-android', [], [],
get_gcc_dir(ndk_dir, 'mipsel-linux-android-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-mips', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-mips', 'usr', 'lib']))
targets['mips64'] = Target(
'mips64', 'mips64el-linux-android',
['-march=mips64el', '-mcpu=mips64r6'],
['-march=mips64el', '-mcpu=mips64r6'],
get_gcc_dir(ndk_dir, 'mips64el-linux-android-4.9', host),
get_clang_dir(ndk_dir, host),
get_platform_dir(ndk_dir, api, ['arch-mips64', 'usr', 'include']),
get_platform_dir(ndk_dir, api, ['arch-mips64', 'usr', 'lib64']))
return targets
def main():
parser = argparse.ArgumentParser(
description='Dry-run NDK toolchain detection')
parser.add_argument('--ndk-dir')
parser.add_argument('--api-level')
parser.add_argument('--host')
args = parser.parse_args()
targets = create_targets(args.ndk_dir, args.api_level, args.host)
success = True
for name, target in targets.items():
success &= target.check_paths()
if not success:
sys.exit(1)
print('succeed')
if __name__ == '__main__':
main()