Home | History | Annotate | Download | only in lib
      1 #
      2 # Copyright (C) 2015 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 import argparse
     17 import multiprocessing
     18 import os
     19 import shutil
     20 import subprocess
     21 import sys
     22 import tempfile
     23 import zipfile
     24 
     25 
     26 THIS_DIR = os.path.realpath(os.path.dirname(__file__))
     27 
     28 
     29 # TODO: Make the x86 toolchain names just be the triple.
     30 ALL_TOOLCHAINS = (
     31     'arm-linux-androideabi',
     32     'aarch64-linux-android',
     33     'mipsel-linux-android',
     34     'mips64el-linux-android',
     35     'x86',
     36     'x86_64',
     37 )
     38 
     39 
     40 ALL_TRIPLES = (
     41     'arm-linux-androideabi',
     42     'aarch64-linux-android',
     43     'mipsel-linux-android',
     44     'mips64el-linux-android',
     45     'i686-linux-android',
     46     'x86_64-linux-android',
     47 )
     48 
     49 
     50 ALL_ARCHITECTURES = (
     51     'arm',
     52     'arm64',
     53     'mips',
     54     'mips64',
     55     'x86',
     56     'x86_64',
     57 )
     58 
     59 
     60 ALL_ABIS = (
     61     'armeabi',
     62     'armeabi-v7a',
     63     'armeabi-v7a-hard',
     64     'arm64-v8a',
     65     'mips',
     66     'mips64',
     67     'x86',
     68     'x86_64',
     69 )
     70 
     71 
     72 def arch_to_toolchain(arch):
     73     return dict(zip(ALL_ARCHITECTURES, ALL_TOOLCHAINS))[arch]
     74 
     75 
     76 def arch_to_triple(arch):
     77     return dict(zip(ALL_ARCHITECTURES, ALL_TRIPLES))[arch]
     78 
     79 
     80 def toolchain_to_arch(toolchain):
     81     return dict(zip(ALL_TOOLCHAINS, ALL_ARCHITECTURES))[toolchain]
     82 
     83 
     84 def arch_to_abis(arch):
     85     return {
     86         'arm': ['armeabi', 'armeabi-v7a', 'armeabi-v7a-hard'],
     87         'arm64': ['arm64-v8a'],
     88         'mips': ['mips'],
     89         'mips64': ['mips64'],
     90         'x86': ['x86'],
     91         'x86_64': ['x86_64'],
     92     }[arch]
     93 
     94 
     95 def abi_to_arch(arch):
     96     return {
     97         'armeabi': 'arm',
     98         'armeabi-v7a': 'arm',
     99         'armeabi-v7a-hard': 'arm',
    100         'arm64-v8a': 'arm64',
    101         'mips': 'mips',
    102         'mips64': 'mips64',
    103         'x86': 'x86',
    104         'x86_64': 'x86_64',
    105     }[arch]
    106 
    107 
    108 def android_path(*args):
    109     top = os.path.realpath(os.path.join(THIS_DIR, '../../..'))
    110     return os.path.normpath(os.path.join(top, *args))
    111 
    112 
    113 def sysroot_path(toolchain):
    114     arch = toolchain_to_arch(toolchain)
    115     version = default_api_level(arch)
    116 
    117     prebuilt_ndk = 'prebuilts/ndk/current'
    118     sysroot_subpath = 'platforms/android-{}/arch-{}'.format(version, arch)
    119     return android_path(prebuilt_ndk, sysroot_subpath)
    120 
    121 
    122 def ndk_path(*args):
    123     return android_path('ndk', *args)
    124 
    125 
    126 def toolchain_path(*args):
    127     return android_path('toolchain', *args)
    128 
    129 
    130 def default_api_level(arch):
    131     if '64' in arch:
    132         return 21
    133     else:
    134         return 9
    135 
    136 
    137 def jobs_arg():
    138     return '-j{}'.format(multiprocessing.cpu_count() * 2)
    139 
    140 
    141 def build(cmd, args, intermediate_package=False):
    142     package_dir = args.out_dir if intermediate_package else args.dist_dir
    143     common_args = [
    144         '--verbose',
    145         '--package-dir={}'.format(package_dir),
    146     ]
    147 
    148     build_env = dict(os.environ)
    149     build_env['NDK_BUILDTOOLS_PATH'] = android_path('ndk/build/tools')
    150     build_env['ANDROID_NDK_ROOT'] = ndk_path()
    151     subprocess.check_call(cmd + common_args, env=build_env)
    152 
    153 
    154 def _get_dir_from_env(default, env_var):
    155     path = os.path.realpath(os.getenv(env_var, default))
    156     if not os.path.isdir(path):
    157         os.makedirs(path)
    158     return path
    159 
    160 
    161 def get_out_dir():
    162     return _get_dir_from_env(android_path('out'), 'OUT_DIR')
    163 
    164 
    165 def get_dist_dir(out_dir):
    166     return _get_dir_from_env(os.path.join(out_dir, 'dist'), 'DIST_DIR')
    167 
    168 
    169 def get_default_host():
    170     if sys.platform in ('linux', 'linux2'):
    171         return 'linux'
    172     elif sys.platform == 'darwin':
    173         return 'darwin'
    174     else:
    175         raise RuntimeError('Unsupported host: {}'.format(sys.platform))
    176 
    177 
    178 def host_to_tag(host):
    179     if host in ['darwin', 'linux']:
    180         return host + '-x86_64'
    181     elif host == 'windows':
    182         return 'windows'
    183     elif host == 'windows64':
    184         return 'windows-x86_64'
    185     else:
    186         raise RuntimeError('Unsupported host: {}'.format(host))
    187 
    188 
    189 def make_repo_prop(out_dir):
    190     file_name = 'repo.prop'
    191 
    192     dist_dir = os.environ.get('DIST_DIR')
    193     if dist_dir is not None:
    194         dist_repo_prop = os.path.join(dist_dir, file_name)
    195         shutil.copy(dist_repo_prop, out_dir)
    196     else:
    197         out_file = os.path.join(out_dir, file_name)
    198         with open(out_file, 'w') as prop_file:
    199             cmd = [
    200                 'repo', 'forall', '-c',
    201                 'echo $REPO_PROJECT $(git rev-parse HEAD)',
    202             ]
    203             subprocess.check_call(cmd, stdout=prop_file)
    204 
    205 
    206 def make_package(name, directory, out_dir):
    207     """Pacakges an NDK module for release.
    208 
    209     Makes a zipfile of the single NDK module that can be released in the SDK
    210     manager. This will handle the details of creating the repo.prop file for
    211     the package.
    212 
    213     Args:
    214         name: Name of the final package, excluding extension.
    215         directory: Directory to be packaged.
    216         out_dir: Directory to place package.
    217     """
    218     if not os.path.isdir(directory):
    219         raise ValueError('directory must be a directory: ' + directory)
    220 
    221     path = os.path.join(out_dir, name + '.zip')
    222     if os.path.exists(path):
    223         os.unlink(path)
    224 
    225     cwd = os.getcwd()
    226     os.chdir(os.path.dirname(directory))
    227     basename = os.path.basename(directory)
    228     try:
    229         subprocess.check_call(
    230             ['zip', '-x', '*.pyc', '-x', '*.pyo', '-x', '*.swp',
    231              '-x', '*.git*', '-9qr', path, basename])
    232     finally:
    233         os.chdir(cwd)
    234 
    235     with zipfile.ZipFile(path, 'a', zipfile.ZIP_DEFLATED) as zip_file:
    236         tmpdir = tempfile.mkdtemp()
    237         try:
    238             make_repo_prop(tmpdir)
    239             arcname = os.path.join(basename, 'repo.prop')
    240             zip_file.write(os.path.join(tmpdir, 'repo.prop'), arcname)
    241         finally:
    242             shutil.rmtree(tmpdir)
    243 
    244 
    245 class ArgParser(argparse.ArgumentParser):
    246     def __init__(self):
    247         super(ArgParser, self).__init__()
    248 
    249         self.add_argument(
    250             '--host', choices=('darwin', 'linux', 'windows', 'windows64'),
    251             default=get_default_host(),
    252             help='Build binaries for given OS (e.g. linux).')
    253 
    254         self.add_argument(
    255             '--out-dir', help='Directory to place temporary build files.',
    256             type=os.path.realpath, default=get_out_dir())
    257 
    258         # The default for --dist-dir has to be handled after parsing all
    259         # arguments because the default is derived from --out-dir. This is
    260         # handled in run().
    261         self.add_argument(
    262             '--dist-dir', help='Directory to place the packaged artifact.',
    263             type=os.path.realpath)
    264 
    265 
    266 def run(main_func, arg_parser=ArgParser):
    267     if 'ANDROID_BUILD_TOP' not in os.environ:
    268         top = os.path.join(os.path.dirname(__file__), '../../..')
    269         os.environ['ANDROID_BUILD_TOP'] = os.path.realpath(top)
    270 
    271     args = arg_parser().parse_args()
    272 
    273     if args.dist_dir is None:
    274         args.dist_dir = get_dist_dir(args.out_dir)
    275 
    276     # We want any paths to be relative to the invoked build script.
    277     main_filename = os.path.realpath(sys.modules['__main__'].__file__)
    278     os.chdir(os.path.dirname(main_filename))
    279 
    280     main_func(args)
    281