普通文本  |  281行  |  7.27 KB

#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import multiprocessing
import os
import shutil
import subprocess
import sys
import tempfile
import zipfile


THIS_DIR = os.path.realpath(os.path.dirname(__file__))


# TODO: Make the x86 toolchain names just be the triple.
ALL_TOOLCHAINS = (
    'arm-linux-androideabi',
    'aarch64-linux-android',
    'mipsel-linux-android',
    'mips64el-linux-android',
    'x86',
    'x86_64',
)


ALL_TRIPLES = (
    'arm-linux-androideabi',
    'aarch64-linux-android',
    'mipsel-linux-android',
    'mips64el-linux-android',
    'i686-linux-android',
    'x86_64-linux-android',
)


ALL_ARCHITECTURES = (
    'arm',
    'arm64',
    'mips',
    'mips64',
    'x86',
    'x86_64',
)


ALL_ABIS = (
    'armeabi',
    'armeabi-v7a',
    'armeabi-v7a-hard',
    'arm64-v8a',
    'mips',
    'mips64',
    'x86',
    'x86_64',
)


def arch_to_toolchain(arch):
    return dict(zip(ALL_ARCHITECTURES, ALL_TOOLCHAINS))[arch]


def arch_to_triple(arch):
    return dict(zip(ALL_ARCHITECTURES, ALL_TRIPLES))[arch]


def toolchain_to_arch(toolchain):
    return dict(zip(ALL_TOOLCHAINS, ALL_ARCHITECTURES))[toolchain]


def arch_to_abis(arch):
    return {
        'arm': ['armeabi', 'armeabi-v7a', 'armeabi-v7a-hard'],
        'arm64': ['arm64-v8a'],
        'mips': ['mips'],
        'mips64': ['mips64'],
        'x86': ['x86'],
        'x86_64': ['x86_64'],
    }[arch]


def abi_to_arch(arch):
    return {
        'armeabi': 'arm',
        'armeabi-v7a': 'arm',
        'armeabi-v7a-hard': 'arm',
        'arm64-v8a': 'arm64',
        'mips': 'mips',
        'mips64': 'mips64',
        'x86': 'x86',
        'x86_64': 'x86_64',
    }[arch]


def android_path(*args):
    top = os.path.realpath(os.path.join(THIS_DIR, '../../..'))
    return os.path.normpath(os.path.join(top, *args))


def sysroot_path(toolchain):
    arch = toolchain_to_arch(toolchain)
    version = default_api_level(arch)

    prebuilt_ndk = 'prebuilts/ndk/current'
    sysroot_subpath = 'platforms/android-{}/arch-{}'.format(version, arch)
    return android_path(prebuilt_ndk, sysroot_subpath)


def ndk_path(*args):
    return android_path('ndk', *args)


def toolchain_path(*args):
    return android_path('toolchain', *args)


def default_api_level(arch):
    if '64' in arch:
        return 21
    else:
        return 9


def jobs_arg():
    return '-j{}'.format(multiprocessing.cpu_count() * 2)


def build(cmd, args, intermediate_package=False):
    package_dir = args.out_dir if intermediate_package else args.dist_dir
    common_args = [
        '--verbose',
        '--package-dir={}'.format(package_dir),
    ]

    build_env = dict(os.environ)
    build_env['NDK_BUILDTOOLS_PATH'] = android_path('ndk/build/tools')
    build_env['ANDROID_NDK_ROOT'] = ndk_path()
    subprocess.check_call(cmd + common_args, env=build_env)


def _get_dir_from_env(default, env_var):
    path = os.path.realpath(os.getenv(env_var, default))
    if not os.path.isdir(path):
        os.makedirs(path)
    return path


def get_out_dir():
    return _get_dir_from_env(android_path('out'), 'OUT_DIR')


def get_dist_dir(out_dir):
    return _get_dir_from_env(os.path.join(out_dir, 'dist'), 'DIST_DIR')


def get_default_host():
    if sys.platform in ('linux', 'linux2'):
        return 'linux'
    elif sys.platform == 'darwin':
        return 'darwin'
    else:
        raise RuntimeError('Unsupported host: {}'.format(sys.platform))


def host_to_tag(host):
    if host in ['darwin', 'linux']:
        return host + '-x86_64'
    elif host == 'windows':
        return 'windows'
    elif host == 'windows64':
        return 'windows-x86_64'
    else:
        raise RuntimeError('Unsupported host: {}'.format(host))


def make_repo_prop(out_dir):
    file_name = 'repo.prop'

    dist_dir = os.environ.get('DIST_DIR')
    if dist_dir is not None:
        dist_repo_prop = os.path.join(dist_dir, file_name)
        shutil.copy(dist_repo_prop, out_dir)
    else:
        out_file = os.path.join(out_dir, file_name)
        with open(out_file, 'w') as prop_file:
            cmd = [
                'repo', 'forall', '-c',
                'echo $REPO_PROJECT $(git rev-parse HEAD)',
            ]
            subprocess.check_call(cmd, stdout=prop_file)


def make_package(name, directory, out_dir):
    """Pacakges an NDK module for release.

    Makes a zipfile of the single NDK module that can be released in the SDK
    manager. This will handle the details of creating the repo.prop file for
    the package.

    Args:
        name: Name of the final package, excluding extension.
        directory: Directory to be packaged.
        out_dir: Directory to place package.
    """
    if not os.path.isdir(directory):
        raise ValueError('directory must be a directory: ' + directory)

    path = os.path.join(out_dir, name + '.zip')
    if os.path.exists(path):
        os.unlink(path)

    cwd = os.getcwd()
    os.chdir(os.path.dirname(directory))
    basename = os.path.basename(directory)
    try:
        subprocess.check_call(
            ['zip', '-x', '*.pyc', '-x', '*.pyo', '-x', '*.swp',
             '-x', '*.git*', '-9qr', path, basename])
    finally:
        os.chdir(cwd)

    with zipfile.ZipFile(path, 'a', zipfile.ZIP_DEFLATED) as zip_file:
        tmpdir = tempfile.mkdtemp()
        try:
            make_repo_prop(tmpdir)
            arcname = os.path.join(basename, 'repo.prop')
            zip_file.write(os.path.join(tmpdir, 'repo.prop'), arcname)
        finally:
            shutil.rmtree(tmpdir)


class ArgParser(argparse.ArgumentParser):
    def __init__(self):
        super(ArgParser, self).__init__()

        self.add_argument(
            '--host', choices=('darwin', 'linux', 'windows', 'windows64'),
            default=get_default_host(),
            help='Build binaries for given OS (e.g. linux).')

        self.add_argument(
            '--out-dir', help='Directory to place temporary build files.',
            type=os.path.realpath, default=get_out_dir())

        # The default for --dist-dir has to be handled after parsing all
        # arguments because the default is derived from --out-dir. This is
        # handled in run().
        self.add_argument(
            '--dist-dir', help='Directory to place the packaged artifact.',
            type=os.path.realpath)


def run(main_func, arg_parser=ArgParser):
    if 'ANDROID_BUILD_TOP' not in os.environ:
        top = os.path.join(os.path.dirname(__file__), '../../..')
        os.environ['ANDROID_BUILD_TOP'] = os.path.realpath(top)

    args = arg_parser().parse_args()

    if args.dist_dir is None:
        args.dist_dir = get_dist_dir(args.out_dir)

    # We want any paths to be relative to the invoked build script.
    main_filename = os.path.realpath(sys.modules['__main__'].__file__)
    os.chdir(os.path.dirname(main_filename))

    main_func(args)