1#!/usr/bin/env python 2 3from __future__ import print_function 4 5import collections 6import os 7import re 8import subprocess 9import sys 10 11 12def detect_ndk_dir(): 13 ndk_dir = os.getenv('NDK') 14 if not ndk_dir: 15 error_msg = '''error: NDK toolchain is required for this test case. 16error: 17error: Steps: 18error: 1. Download NDK from https://developer.android.com/ndk/downloads/ 19error: 2. Unzip the archive (android-ndk-r15c-linux-x86_64.zip) 20error: 3. Set environment variable NDK to the extracted directory 21error: (export NDK=android-ndk-r15c) 22error:''' 23 print(error_msg, file=sys.stderr) 24 raise ValueError('NDK toolchain not specified') 25 26 if not os.path.exists(ndk_dir): 27 raise ValueError('NDK toolchain not found') 28 29 return ndk_dir 30 31 32def detect_api_level(ndk_dir): 33 try: 34 apis = [] 35 pattern = re.compile('android-(\\d+)') 36 for name in os.listdir(os.path.join(ndk_dir, 'platforms')): 37 match = pattern.match(name) 38 if match: 39 apis.append(int(match.group(1))) 40 if not apis: 41 raise ValueError('failed to find latest api') 42 return 'android-{}'.format(max(apis)) 43 except IOError: 44 raise ValueError('failed to find latest api') 45 46 47def detect_host(): 48 if sys.platform.startswith('linux'): 49 return 'linux-x86_64' 50 if sys.platform.startswith('darwin'): 51 return 'darwin-x86_64' 52 raise NotImplementedError('unknown host platform') 53 54 55def get_gcc_dir(ndk_dir, arch, host): 56 return os.path.join(ndk_dir, 'toolchains', arch, 'prebuilt', host) 57 58 59def get_clang_dir(ndk_dir, host): 60 return os.path.join(ndk_dir, 'toolchains', 'llvm', 'prebuilt', host) 61 62 63def get_platform_dir(ndk_dir, api, subdirs): 64 return os.path.join(ndk_dir, 'platforms', api, *subdirs) 65 66 67class Target(object): 68 def __init__(self, name, triple, cflags, ldflags, gcc_toolchain_dir, 69 clang_dir, ndk_include, ndk_lib): 70 self.name = name 71 self.target_triple = triple 72 self.target_cflags = cflags 73 self.target_ldflags = ldflags 74 75 self.gcc_toolchain_dir = gcc_toolchain_dir 76 self.clang_dir = clang_dir 77 self.ndk_include = ndk_include 78 self.ndk_lib = ndk_lib 79 80 81 def check_paths(self): 82 def check_path(path): 83 if os.path.exists(path): 84 return True 85 print('error: File not found:', path, file=sys.stderr) 86 return False 87 88 ld_exeutable = os.path.join( 89 self.gcc_toolchain_dir, 'bin', self.target_triple + '-ld') 90 91 success = check_path(self.gcc_toolchain_dir) 92 success &= check_path(ld_exeutable) 93 success &= check_path(self.clang_dir) 94 success &= check_path(self.ndk_include) 95 success &= check_path(self.ndk_lib) 96 return success 97 98 99 def compile(self, obj_file, src_file, cflags): 100 clang = os.path.join(self.clang_dir, 'bin', 'clang') 101 102 cmd = [clang, '-o', obj_file, '-c', src_file] 103 cmd.extend(['-fPIE', '-fPIC']) 104 cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir]) 105 cmd.extend(['-target', self.target_triple]) 106 cmd.extend(['-isystem', self.ndk_include]) 107 cmd.extend(cflags) 108 cmd.extend(self.target_cflags) 109 subprocess.check_call(cmd) 110 111 112 def link(self, out_file, obj_files, ldflags): 113 if '-shared' in ldflags: 114 crtbegin = os.path.join(self.ndk_lib, 'crtbegin_so.o') 115 crtend = os.path.join(self.ndk_lib, 'crtend_so.o') 116 else: 117 crtbegin = os.path.join(self.ndk_lib, 'crtbegin_static.o') 118 crtend = os.path.join(self.ndk_lib, 'crtend_android.o') 119 120 clang = os.path.join(self.clang_dir, 'bin', 'clang') 121 122 cmd = [clang, '-o', out_file] 123 cmd.extend(['-fPIE', '-fPIC', '-Wl,--no-undefined', '-nostdlib']) 124 cmd.append('-L' + self.ndk_lib) 125 cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir]) 126 cmd.extend(['-target', self.target_triple]) 127 cmd.append(crtbegin) 128 cmd.extend(obj_files) 129 cmd.append(crtend) 130 cmd.extend(ldflags) 131 cmd.extend(self.target_ldflags) 132 if '-shared' not in ldflags: 133 cmd.append('-Wl,-pie') 134 subprocess.check_call(cmd) 135 136 137def create_targets(ndk_dir=None, api=None, host=None): 138 if ndk_dir is None: 139 ndk_dir = detect_ndk_dir() 140 if api is None: 141 api = detect_api_level(ndk_dir) 142 if host is None: 143 host = detect_host() 144 145 targets = collections.OrderedDict() 146 147 targets['arm'] = Target( 148 'arm', 'arm-linux-androideabi', [], [], 149 get_gcc_dir(ndk_dir, 'arm-linux-androideabi-4.9', host), 150 get_clang_dir(ndk_dir, host), 151 get_platform_dir(ndk_dir, api, ['arch-arm', 'usr', 'include']), 152 get_platform_dir(ndk_dir, api, ['arch-arm', 'usr', 'lib'])) 153 154 targets['arm64'] = Target( 155 'arm64', 'aarch64-linux-android', [], [], 156 get_gcc_dir(ndk_dir, 'aarch64-linux-android-4.9', host), 157 get_clang_dir(ndk_dir, host), 158 get_platform_dir(ndk_dir, api, ['arch-arm64', 'usr', 'include']), 159 get_platform_dir(ndk_dir, api, ['arch-arm64', 'usr', 'lib'])) 160 161 targets['x86'] = Target( 162 'x86', 'i686-linux-android', ['-m32'], ['-m32'], 163 get_gcc_dir(ndk_dir, 'x86-4.9', host), 164 get_clang_dir(ndk_dir, host), 165 get_platform_dir(ndk_dir, api, ['arch-x86', 'usr', 'include']), 166 get_platform_dir(ndk_dir, api, ['arch-x86', 'usr', 'lib'])) 167 168 targets['x86_64'] = Target( 169 'x86_64', 'x86_64-linux-android', ['-m64'], ['-m64'], 170 get_gcc_dir(ndk_dir, 'x86_64-4.9', host), 171 get_clang_dir(ndk_dir, host), 172 get_platform_dir(ndk_dir, api, ['arch-x86_64', 'usr', 'include']), 173 get_platform_dir(ndk_dir, api, ['arch-x86_64', 'usr', 'lib64'])) 174 175 targets['mips'] = Target( 176 'mips', 'mipsel-linux-android', [], [], 177 get_gcc_dir(ndk_dir, 'mipsel-linux-android-4.9', host), 178 get_clang_dir(ndk_dir, host), 179 get_platform_dir(ndk_dir, api, ['arch-mips', 'usr', 'include']), 180 get_platform_dir(ndk_dir, api, ['arch-mips', 'usr', 'lib'])) 181 182 targets['mips64'] = Target( 183 'mips64', 'mips64el-linux-android', 184 ['-march=mips64el', '-mcpu=mips64r6'], 185 ['-march=mips64el', '-mcpu=mips64r6'], 186 get_gcc_dir(ndk_dir, 'mips64el-linux-android-4.9', host), 187 get_clang_dir(ndk_dir, host), 188 get_platform_dir(ndk_dir, api, ['arch-mips64', 'usr', 'include']), 189 get_platform_dir(ndk_dir, api, ['arch-mips64', 'usr', 'lib64'])) 190 191 return targets 192