| #!/usr/bin/env python |
| # |
| # 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. |
| # |
| """Verifies that the build is sane. |
| |
| Cleans old build artifacts, configures the required environment, determines |
| build goals, and invokes the build scripts. |
| """ |
| from __future__ import print_function |
| |
| import argparse |
| import collections |
| import inspect |
| import os |
| import shutil |
| import site |
| import subprocess |
| import sys |
| import tempfile |
| |
| site.addsitedir(os.path.join(os.path.dirname(__file__), 'build/lib')) |
| |
| import build_support # pylint: disable=import-error |
| |
| |
| ALL_MODULES = { |
| 'binutils', |
| 'build', |
| 'clang', |
| 'cpufeatures', |
| 'gcc', |
| 'gcclibs', |
| 'gdbserver', |
| 'gnustl', |
| 'gtest', |
| 'host-tools', |
| 'libc++', |
| 'native_app_glue', |
| 'ndk_helper', |
| 'platforms', |
| 'python-packages', |
| 'stlport', |
| } |
| |
| |
| class ArgParser(argparse.ArgumentParser): |
| def __init__(self): |
| super(ArgParser, self).__init__( |
| description=inspect.getdoc(sys.modules[__name__])) |
| |
| self.add_argument( |
| '--arch', |
| choices=('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'), |
| help='Build for the given architecture. Build all by default.') |
| |
| self.add_argument( |
| '--package', action='store_true', dest='package', default=True, |
| help='Package the NDK when done building (default).') |
| self.add_argument( |
| '--no-package', action='store_false', dest='package', |
| help='Do not package the NDK when done building.') |
| |
| self.add_argument( |
| '--release', |
| help='Release name. Package will be named android-ndk-RELEASE.') |
| |
| self.add_argument( |
| '--system', choices=('darwin', 'linux', 'windows', 'windows64'), |
| default=build_support.get_default_host(), |
| help='Build for the given OS.') |
| |
| module_group = self.add_mutually_exclusive_group() |
| |
| module_group.add_argument( |
| '--module', choices=sorted(ALL_MODULES), |
| help='NDK modules to build.') |
| |
| module_group.add_argument( |
| '--host-only', action='store_true', |
| help='Skip building target components.') |
| |
| |
| def _invoke_build(script, args): |
| if args is None: |
| args = [] |
| subprocess.check_call([build_support.android_path(script)] + args) |
| |
| |
| def invoke_build(script, args=None): |
| script_path = os.path.join('build/tools', script) |
| _invoke_build(build_support.ndk_path(script_path), args) |
| |
| |
| def invoke_external_build(script, args=None): |
| _invoke_build(build_support.android_path(script), args) |
| |
| |
| def package_ndk(out_dir, args): |
| package_args = common_build_args(out_dir, args) |
| package_args.append(out_dir) |
| |
| if args.release is not None: |
| package_args.append('--release={}'.format(args.release)) |
| |
| if args.arch is not None: |
| package_args.append('--arch={}'.format(args.arch)) |
| |
| invoke_build('package.py', package_args) |
| |
| |
| def common_build_args(out_dir, args): |
| build_args = ['--package-dir={}'.format(out_dir)] |
| build_args.append('--host={}'.format(args.system)) |
| return build_args |
| |
| |
| def fixup_toolchain_triple(toolchain): |
| """Maps toolchain names to their proper triple. |
| |
| The x86 toolchains are named stupidly and aren't a proper triple. |
| """ |
| return { |
| 'x86': 'i686-linux-android', |
| 'x86_64': 'x86_64-linux-android', |
| }.get(toolchain, toolchain) |
| |
| |
| def get_binutils_files(triple, has_gold, is_windows): |
| files = [ |
| 'ld.bfd', |
| 'nm', |
| 'as', |
| 'objcopy', |
| 'strip', |
| 'objdump', |
| 'ld', |
| 'ar', |
| 'ranlib', |
| ] |
| |
| if has_gold: |
| files.append('ld.gold') |
| |
| if is_windows: |
| files = [f + '.exe' for f in files] |
| |
| # binutils programs get installed to two locations: |
| # 1: $INSTALL_DIR/bin/$TRIPLE-$PROGRAM |
| # 2: $INSTALL_DIR/$TRIPLE/bin/$PROGRAM |
| # |
| # We need to copy both. |
| |
| prefixed_files = [] |
| for file_name in files: |
| prefixed_name = '-'.join([triple, file_name]) |
| prefixed_files.append(os.path.join('bin', prefixed_name)) |
| |
| dir_prefixed_files = [] |
| for file_name in files: |
| dir_prefixed_files.append(os.path.join(triple, 'bin', file_name)) |
| |
| ldscripts_dir = os.path.join(triple, 'lib/ldscripts') |
| return prefixed_files + dir_prefixed_files + [ldscripts_dir] |
| |
| |
| def install_file(file_name, src_dir, dst_dir): |
| src_file = os.path.join(src_dir, file_name) |
| dst_file = os.path.join(dst_dir, file_name) |
| |
| print('Copying {} to {}...'.format(src_file, dst_file)) |
| if os.path.isdir(src_file): |
| _install_dir(src_file, dst_file) |
| elif os.path.islink(src_file): |
| _install_symlink(src_file, dst_file) |
| else: |
| _install_file(src_file, dst_file) |
| |
| |
| def _install_dir(src_dir, dst_dir): |
| parent_dir = os.path.normpath(os.path.join(dst_dir, '..')) |
| if not os.path.exists(parent_dir): |
| os.makedirs(parent_dir) |
| shutil.copytree(src_dir, dst_dir, symlinks=True) |
| |
| |
| def _install_symlink(src_file, dst_file): |
| dirname = os.path.dirname(dst_file) |
| if not os.path.exists(dirname): |
| os.makedirs(dirname) |
| link_target = os.readlink(src_file) |
| os.symlink(link_target, dst_file) |
| |
| |
| def _install_file(src_file, dst_file): |
| dirname = os.path.dirname(dst_file) |
| if not os.path.exists(dirname): |
| os.makedirs(dirname) |
| # copy2 is just copy followed by copystat (preserves file metadata). |
| shutil.copy2(src_file, dst_file) |
| |
| |
| def pack_binutils(arch, host_tag, out_dir, root_dir, binutils_path): |
| archive_name = '-'.join(['binutils', arch, host_tag]) |
| binutils_relpath = os.path.relpath(binutils_path, root_dir) |
| files = [binutils_relpath] |
| build_support.make_package(archive_name, files, out_dir, root_dir, |
| repo_prop_dir=binutils_relpath) |
| |
| |
| def get_prebuilt_gcc(host, arch): |
| tag = build_support.host_to_tag(host) |
| system_subdir = 'prebuilts/ndk/current/toolchains/{}'.format(tag) |
| system_path = build_support.android_path(system_subdir) |
| toolchain = build_support.arch_to_toolchain(arch) |
| toolchain_dir = toolchain + '-4.9' |
| return os.path.join(system_path, toolchain_dir) |
| |
| |
| def build_binutils(out_dir, args): |
| print('Extracting binutils package from GCC...') |
| |
| arches = build_support.ALL_ARCHITECTURES |
| if args.arch is not None: |
| arches = [args.arch] |
| |
| host_tag = build_support.host_to_tag(args.system) |
| |
| for arch in arches: |
| toolchain = build_support.arch_to_toolchain(arch) |
| toolchain_path = get_prebuilt_gcc(args.system, arch) |
| |
| triple = fixup_toolchain_triple(toolchain) |
| tmpdir = tempfile.mkdtemp() |
| try: |
| install_dir = os.path.join(tmpdir, 'binutils', triple) |
| os.makedirs(install_dir) |
| is_windows = host_tag.startswith('windows') |
| has_gold = not triple.startswith('mips') and not is_windows |
| for file_name in get_binutils_files(triple, has_gold, is_windows): |
| install_file(file_name, toolchain_path, install_dir) |
| pack_binutils(arch, host_tag, out_dir, tmpdir, install_dir) |
| finally: |
| shutil.rmtree(tmpdir) |
| |
| |
| def build_clang(out_dir, args): |
| print('Building Clang...') |
| invoke_build('build-llvm.py', common_build_args(out_dir, args)) |
| |
| |
| def build_gcc(out_dir, args): |
| print('Building GCC...') |
| build_args = common_build_args(out_dir, args) |
| if args.arch is not None: |
| build_args.append('--arch={}'.format(args.arch)) |
| invoke_build('build-gcc.py', build_args) |
| |
| |
| def build_gcc_libs(out_dir, args): |
| print('Packaging GCC libs...') |
| |
| arches = build_support.ALL_ARCHITECTURES |
| if args.arch is not None: |
| arches = [args.arch] |
| |
| for arch in arches: |
| toolchain = build_support.arch_to_toolchain(arch) |
| triple = fixup_toolchain_triple(toolchain) |
| libgcc_subdir = 'lib/gcc/{}/4.9'.format(triple) |
| is64 = arch.endswith('64') |
| libatomic_subdir = '{}/lib{}'.format(triple, '64' if is64 else '') |
| |
| lib_names = [ |
| (libatomic_subdir, 'libatomic.a'), |
| (libgcc_subdir, 'libgcc.a'), |
| ] |
| |
| lib_dirs = [''] |
| if arch == 'arm': |
| lib_dirs += [ |
| 'armv7-a', |
| 'armv7-a/hard', |
| 'armv7-a/thumb', |
| 'armv7-a/thumb/hard', |
| 'thumb', |
| ] |
| |
| libs = [] |
| for lib_dir in lib_dirs: |
| for subdir, lib in lib_names: |
| libs.append((subdir, os.path.join(lib_dir, lib))) |
| |
| tmpdir = tempfile.mkdtemp() |
| try: |
| install_dir = os.path.join(tmpdir, 'gcclibs', triple) |
| os.makedirs(install_dir) |
| |
| # These are target libraries, so the OS we use here is not |
| # important. We explicitly use Linux because for whatever reason |
| # the Windows aarch64 toolchain doesn't include libatomic. |
| gcc_path = get_prebuilt_gcc('linux', arch) |
| for gcc_subdir, lib in libs: |
| src = os.path.join(gcc_path, gcc_subdir, lib) |
| dst = os.path.join(install_dir, lib) |
| dst_dir = os.path.dirname(dst) |
| if not os.path.exists(dst_dir): |
| os.makedirs(dst_dir) |
| shutil.copy2(src, dst) |
| |
| archive_name = os.path.join(out_dir, 'gcclibs-' + arch) |
| gcclibs_relpath = os.path.relpath(install_dir, tmpdir) |
| files = [gcclibs_relpath] |
| root_dir = os.path.realpath(tmpdir) |
| build_support.make_package(archive_name, files, out_dir, |
| root_dir, |
| repo_prop_dir=gcclibs_relpath) |
| finally: |
| shutil.rmtree(tmpdir) |
| |
| |
| def build_host_tools(out_dir, args): |
| build_args = common_build_args(out_dir, args) |
| |
| print('Building ndk-stack...') |
| invoke_external_build( |
| 'ndk/sources/host-tools/ndk-stack/build.py', build_args) |
| |
| print('Building ndk-depends...') |
| invoke_external_build( |
| 'ndk/sources/host-tools/ndk-depends/build.py', build_args) |
| |
| print('Building awk...') |
| invoke_external_build( |
| 'ndk/sources/host-tools/nawk-20071023/build.py', build_args) |
| |
| print('Building make...') |
| invoke_external_build( |
| 'ndk/sources/host-tools/make-3.81/build.py', build_args) |
| |
| if args.system in ('windows', 'windows64'): |
| print('Building toolbox...') |
| invoke_external_build( |
| 'ndk/sources/host-tools/toolbox/build.py', build_args) |
| |
| print('Building Python...') |
| invoke_external_build('toolchain/python/build.py', build_args) |
| |
| print('Building GDB...') |
| invoke_external_build('toolchain/gdb/build.py', build_args) |
| |
| print('Building YASM...') |
| invoke_external_build('toolchain/yasm/build.py', build_args) |
| |
| package_host_tools(out_dir, args.system) |
| |
| |
| def package_host_tools(out_dir, host): |
| packages = [ |
| 'gdb-multiarch-7.10', |
| 'ndk-awk', |
| 'ndk-depends', |
| 'ndk-make', |
| 'ndk-python', |
| 'ndk-stack', |
| 'ndk-yasm', |
| ] |
| |
| files = [ |
| 'ndk-gdb', |
| 'ndk-gdb.cmd', |
| 'ndk-gdb.py', |
| ] |
| |
| if host in ('windows', 'windows64'): |
| packages.append('toolbox') |
| |
| host_tag = build_support.host_to_tag(host) |
| |
| package_names = [p + '-' + host_tag + '.tar.bz2' for p in packages] |
| temp_dir = tempfile.mkdtemp() |
| try: |
| for package_name in package_names: |
| package_path = os.path.join(out_dir, package_name) |
| subprocess.check_call(['tar', 'xf', package_path, '-C', temp_dir]) |
| |
| for f in files: |
| shutil.copy2(f, os.path.join(temp_dir, 'host-tools/bin')) |
| |
| package_name = 'host-tools-' + host_tag |
| build_support.make_package(package_name, ['host-tools'], out_dir, |
| temp_dir, repo_prop_dir='host-tools') |
| finally: |
| shutil.rmtree(temp_dir) |
| |
| |
| def build_gdbserver(out_dir, args): |
| print('Building gdbserver...') |
| build_args = common_build_args(out_dir, args) |
| if args.arch is not None: |
| build_args.append('--arch={}'.format(args.arch)) |
| invoke_build('build-gdbserver.py', build_args) |
| |
| |
| def _build_stl(out_dir, args, stl): |
| build_args = common_build_args(out_dir, args) |
| if args.arch is not None: |
| build_args.append('--arch={}'.format(args.arch)) |
| script = 'ndk/sources/cxx-stl/{}/build.py'.format(stl) |
| invoke_external_build(script, build_args) |
| |
| |
| def build_gnustl(out_dir, args): |
| print('Building gnustl...') |
| _build_stl(out_dir, args, 'gnu-libstdc++') |
| |
| |
| def build_libcxx(out_dir, args): |
| print('Building libc++...') |
| _build_stl(out_dir, args, 'llvm-libc++') |
| |
| |
| def build_stlport(out_dir, args): |
| print('Building stlport...') |
| _build_stl(out_dir, args, 'stlport') |
| |
| |
| def build_platforms(out_dir, args): |
| print('Building platforms...') |
| invoke_build('build-platforms.py', common_build_args(out_dir, args)) |
| |
| |
| def build_cpufeatures(out_dir, _): |
| root_dir = build_support.ndk_path() |
| path = 'sources/android/cpufeatures' |
| build_support.make_package('cpufeatures', [path], out_dir, root_dir, |
| repo_prop_dir=path) |
| |
| |
| def build_native_app_glue(out_dir, _): |
| root_dir = build_support.android_path('development/ndk') |
| path = 'sources/android/native_app_glue' |
| build_support.make_package('native_app_glue', [path], out_dir, root_dir, |
| repo_prop_dir=path) |
| |
| |
| def build_ndk_helper(out_dir, _): |
| root_dir = build_support.android_path('development/ndk') |
| path = 'sources/android/ndk_helper' |
| build_support.make_package('ndk_helper', [path], out_dir, root_dir, |
| repo_prop_dir=path) |
| |
| |
| def build_gtest(out_dir, _): |
| root_dir = build_support.ndk_path() |
| path = 'sources/third_party/googletest' |
| build_support.make_package('gtest', [path], out_dir, root_dir, |
| repo_prop_dir=path) |
| |
| |
| def build_build(out_dir, _): |
| root_dir = build_support.ndk_path() |
| build_support.make_package('build', ['build'], out_dir, root_dir, |
| repo_prop_dir='build') |
| |
| |
| def build_python_packages(out_dir, _): |
| # Stage the files in a temporary directory to make things easier. |
| temp_dir = tempfile.mkdtemp() |
| try: |
| shutil.copytree( |
| build_support.android_path('development/python-packages'), |
| os.path.join(temp_dir, 'python-packages')) |
| build_support.make_package('python-packages', ['python-packages'], |
| out_dir, temp_dir, |
| repo_prop_dir='python-packages') |
| finally: |
| shutil.rmtree(temp_dir) |
| |
| |
| def main(): |
| args = ArgParser().parse_args() |
| |
| # Disable buffering on stdout so the build output doesn't hide all of our |
| # "Building..." messages. |
| sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) |
| |
| os.chdir(os.path.dirname(os.path.realpath(__file__))) |
| |
| # Set ANDROID_BUILD_TOP. |
| if 'ANDROID_BUILD_TOP' not in os.environ: |
| os.environ['ANDROID_BUILD_TOP'] = os.path.realpath('..') |
| build_top = os.getenv('ANDROID_BUILD_TOP') |
| |
| DEFAULT_OUT_DIR = os.path.join(build_top, 'out/ndk') |
| out_dir = os.path.realpath(os.getenv('DIST_DIR', DEFAULT_OUT_DIR)) |
| if not os.path.isdir(out_dir): |
| os.makedirs(out_dir) |
| |
| if args.module is None: |
| modules = ALL_MODULES |
| else: |
| modules = {args.module} |
| |
| if args.host_only: |
| modules = { |
| 'clang', |
| 'gcc', |
| 'host-tools', |
| } |
| |
| print('Cleaning up...') |
| invoke_build('dev-cleanup.sh') |
| |
| module_builds = collections.OrderedDict([ |
| ('binutils', build_binutils), |
| ('build', build_build), |
| ('clang', build_clang), |
| ('cpufeatures', build_cpufeatures), |
| ('gcc', build_gcc), |
| ('gcclibs', build_gcc_libs), |
| ('gdbserver', build_gdbserver), |
| ('gnustl', build_gnustl), |
| ('gtest', build_gtest), |
| ('host-tools', build_host_tools), |
| ('libc++', build_libcxx), |
| ('native_app_glue', build_native_app_glue), |
| ('ndk_helper', build_ndk_helper), |
| ('platforms', build_platforms), |
| ('python-packages', build_python_packages), |
| ('stlport', build_stlport), |
| ]) |
| |
| print('Building modules: {}'.format(' '.join(modules))) |
| for module in modules: |
| module_builds[module](out_dir, args) |
| |
| required_package_modules = ALL_MODULES |
| if args.package and required_package_modules <= modules: |
| package_ndk(out_dir, args) |
| |
| |
| if __name__ == '__main__': |
| main() |