#!/usr/bin/env python from __future__ import print_function 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