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