Add a script for building just binutils.
In contrast to building binutils via the GCC build, each target here
takes only 1 minute instead of 10. Given that we've stopped
supporting GCC, it's also nice to not have to rebuild something that
shouldn't have changed anyway.
This also has the advantage of building to a specific location in the
out directory rather than /tmp, meaning it's easy to cd into the
build directory and just run make rather than rerunning the full
build script.
Test: ./build.py for all architectures, linux, win, win64
Bug: None
Change-Id: I067d84c5439c56004fc12de496f8f66fba7f7bf7
diff --git a/build.py b/build.py
new file mode 100755
index 0000000..0702465
--- /dev/null
+++ b/build.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+"""Builds binutils."""
+from __future__ import print_function
+
+import argparse
+import logging
+import multiprocessing
+import os
+import shutil
+import site
+import subprocess
+
+
+THIS_DIR = os.path.realpath(os.path.dirname(__file__))
+site.addsitedir(os.path.join(THIS_DIR, '../../ndk'))
+
+# pylint: disable=wrong-import-position
+import ndk.abis # pylint: disable=import-error
+import ndk.ext.shutil # pylint: disable=import-error
+import ndk.paths # pylint: disable=import-error
+import ndk.timer # pylint: disable=import-error
+# pylint: enable=wrong-import-position
+
+
+def logger():
+ """Returns the module level logger."""
+ return logging.getLogger(__name__)
+
+
+def makedirs(path):
+ """os.makedirs with logging."""
+ logger().info('makedirs ' + path)
+ os.makedirs(path)
+
+
+def rmtree(path):
+ """shutil.rmtree with logging."""
+ logger().info('rmtree ' + path)
+ shutil.rmtree(path)
+
+
+def chdir(path):
+ """os.chdir with logging."""
+ logger().info('chdir ' + path)
+ os.chdir(path)
+
+
+def check_call(cmd, *args, **kwargs):
+ """subprocess.check_call with logging."""
+ logger().info('check_call %s', subprocess.list2cmdline(cmd))
+ subprocess.check_call(cmd, *args, **kwargs)
+
+
+def configure(arch, host, install_dir, src_dir):
+ """Configures binutils."""
+ is_windows = host in ('win', 'win64')
+
+ configure_host = {
+ 'darwin': 'x86_64-apple-darwin',
+ 'linux': 'x86_64-linux-gnu',
+ 'win': 'i586-pc-mingw32msvc',
+ 'win64': 'x86_64-pc-mingw32msvc',
+ }[host]
+
+ sysroot = ndk.paths.sysroot_path(ndk.abis.arch_to_toolchain(arch))
+ configure_args = [
+ os.path.join(src_dir, 'configure'),
+ '--target={}'.format(ndk.abis.arch_to_triple(arch)),
+ '--host={}'.format(configure_host),
+ '--enable-gold=default',
+ '--enable-initfini-array',
+ '--enable-plugins',
+ '--with-sysroot={}'.format(sysroot),
+ '--prefix={}'.format(install_dir),
+ ]
+
+ if arch == 'arm64':
+ configure_args.append('--enable-fix-cortex-a53-835769')
+
+ if not is_windows:
+ # Multithreaded linking is implemented with pthreads, which we
+ # historically couldn't use on Windows.
+ # TODO: Try enabling this now that we have winpthreads in mingw.
+ configure_args.append('--enable-threads')
+
+ env = {}
+
+ m32 = False
+ if host == 'darwin':
+ toolchain = ndk.paths.android_path(
+ 'prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1')
+ toolchain_prefix = 'x86_64-linux'
+ env['MACOSX_DEPLOYMENT_TARGET'] = '10.6'
+ elif host == 'linux':
+ toolchain = ndk.paths.android_path(
+ 'prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8')
+ toolchain_prefix = 'x86_64-linux'
+ elif is_windows:
+ toolchain = ndk.paths.android_path(
+ 'prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8')
+ toolchain_prefix = 'x86_64-w64-mingw32'
+ if host == 'win':
+ m32 = True
+ else:
+ raise NotImplementedError
+
+ cc = os.path.join(toolchain, 'bin', '{}-gcc'.format(toolchain_prefix))
+ cxx = os.path.join(toolchain, 'bin', '{}-g++'.format(toolchain_prefix))
+ ar = os.path.join(toolchain, 'bin', '{}-ar'.format(toolchain_prefix))
+
+ env['AR'] = ar
+ env['CC'] = cc
+ env['CXX'] = cxx
+ if m32:
+ env['CFLAGS'] = '-m32'
+ env['CXXFLAGS'] = '-m32'
+ env['LDFLAGS'] = '-m32'
+
+ env_args = ['env'] + ['='.join([k, v]) for k, v in env.items()]
+ check_call(env_args + configure_args)
+
+
+def build(jobs):
+ """Builds binutils."""
+ check_call(['make', '-j', str(jobs)])
+
+
+def install(jobs):
+ """Installs binutils."""
+ check_call(['make', 'install', '-j', str(jobs)])
+
+
+def dist(dist_dir, base_dir, package_name):
+ """Packages binutils for distribution."""
+ has_pbzip2 = ndk.ext.shutil.which('pbzip2') is not None
+ if has_pbzip2:
+ compress_arg = '--use-compress-prog=pbzip2'
+ else:
+ compress_arg = '-j'
+
+ package_path = os.path.join(dist_dir, package_name + 'tar.bz2')
+ cmd = [
+ 'tar', compress_arg, '-cf', package_path, '-C', base_dir, package_name,
+ ]
+ subprocess.check_call(cmd)
+
+
+def parse_args():
+ """Parse command line arguments."""
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ '--arch', choices=ndk.abis.ALL_ARCHITECTURES, required=True)
+ parser.add_argument(
+ '--host', choices=('darwin', 'linux', 'win', 'win64'), required=True)
+
+ parser.add_argument(
+ '--clean', action='store_true',
+ help='Clean the out directory before building.')
+ parser.add_argument(
+ '-j', '--jobs', type=int, default=multiprocessing.cpu_count(),
+ help='Number of jobs to use when building.')
+
+ return parser.parse_args()
+
+
+def main():
+ """Program entry point."""
+ args = parse_args()
+ logging.basicConfig(level=logging.INFO)
+
+ total_timer = ndk.timer.Timer()
+ total_timer.start()
+
+ out_dir = ndk.paths.get_out_dir()
+ dist_dir = ndk.paths.get_dist_dir(out_dir)
+ base_build_dir = os.path.join(
+ out_dir, 'binutils', args.host, args.arch)
+ build_dir = os.path.join(base_build_dir, 'build')
+ package_name = 'binutils-{}-{}'.format(args.arch, args.host)
+ install_dir = os.path.join(base_build_dir, 'install', package_name)
+ binutils_path = os.path.join(THIS_DIR, 'binutils-2.27')
+
+ did_clean = False
+ clean_timer = ndk.timer.Timer()
+ if args.clean and os.path.exists(build_dir):
+ did_clean = True
+ with clean_timer:
+ rmtree(build_dir)
+
+ if not os.path.exists(build_dir):
+ makedirs(build_dir)
+
+ orig_dir = os.getcwd()
+ chdir(build_dir)
+ try:
+ configure_timer = ndk.timer.Timer()
+ with configure_timer:
+ configure(args.arch, args.host, install_dir, binutils_path)
+
+ build_timer = ndk.timer.Timer()
+ with build_timer:
+ build(args.jobs)
+
+ install_timer = ndk.timer.Timer()
+ with install_timer:
+ install(args.jobs)
+ finally:
+ chdir(orig_dir)
+
+ package_timer = ndk.timer.Timer()
+ with package_timer:
+ dist(dist_dir, os.path.dirname(install_dir), package_name)
+
+ total_timer.finish()
+
+ if did_clean:
+ print('Clean: {}'.format(clean_timer.duration))
+ print('Configure: {}'.format(configure_timer.duration))
+ print('Build: {}'.format(build_timer.duration))
+ print('Install: {}'.format(install_timer.duration))
+ print('Package: {}'.format(package_timer.duration))
+ print('Total: {}'.format(total_timer.duration))
+
+
+if __name__ == '__main__':
+ main()