Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2017 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | # |
| 17 | """Builds binutils.""" |
| 18 | from __future__ import print_function |
| 19 | |
| 20 | import argparse |
| 21 | import logging |
| 22 | import multiprocessing |
| 23 | import os |
| 24 | import shutil |
| 25 | import site |
| 26 | import subprocess |
| 27 | |
| 28 | |
| 29 | THIS_DIR = os.path.realpath(os.path.dirname(__file__)) |
| 30 | site.addsitedir(os.path.join(THIS_DIR, '../../ndk')) |
| 31 | |
| 32 | # pylint: disable=wrong-import-position |
| 33 | import ndk.abis # pylint: disable=import-error |
| 34 | import ndk.ext.shutil # pylint: disable=import-error |
| 35 | import ndk.paths # pylint: disable=import-error |
| 36 | import ndk.timer # pylint: disable=import-error |
| 37 | # pylint: enable=wrong-import-position |
| 38 | |
| 39 | |
| 40 | def logger(): |
| 41 | """Returns the module level logger.""" |
| 42 | return logging.getLogger(__name__) |
| 43 | |
| 44 | |
| 45 | def makedirs(path): |
| 46 | """os.makedirs with logging.""" |
| 47 | logger().info('makedirs ' + path) |
| 48 | os.makedirs(path) |
| 49 | |
| 50 | |
| 51 | def rmtree(path): |
| 52 | """shutil.rmtree with logging.""" |
| 53 | logger().info('rmtree ' + path) |
| 54 | shutil.rmtree(path) |
| 55 | |
| 56 | |
| 57 | def chdir(path): |
| 58 | """os.chdir with logging.""" |
| 59 | logger().info('chdir ' + path) |
| 60 | os.chdir(path) |
| 61 | |
| 62 | |
| 63 | def check_call(cmd, *args, **kwargs): |
| 64 | """subprocess.check_call with logging.""" |
| 65 | logger().info('check_call %s', subprocess.list2cmdline(cmd)) |
| 66 | subprocess.check_call(cmd, *args, **kwargs) |
| 67 | |
| 68 | |
| 69 | def configure(arch, host, install_dir, src_dir): |
| 70 | """Configures binutils.""" |
| 71 | is_windows = host in ('win', 'win64') |
| 72 | |
| 73 | configure_host = { |
| 74 | 'darwin': 'x86_64-apple-darwin', |
| 75 | 'linux': 'x86_64-linux-gnu', |
Ryan Prichard | 6fa214b | 2018-01-22 22:23:52 -0800 | [diff] [blame] | 76 | 'win': 'i686-w64-mingw32', |
| 77 | 'win64': 'x86_64-w64-mingw32', |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 78 | }[host] |
| 79 | |
| 80 | sysroot = ndk.paths.sysroot_path(ndk.abis.arch_to_toolchain(arch)) |
| 81 | configure_args = [ |
| 82 | os.path.join(src_dir, 'configure'), |
| 83 | '--target={}'.format(ndk.abis.arch_to_triple(arch)), |
| 84 | '--host={}'.format(configure_host), |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 85 | '--enable-initfini-array', |
| 86 | '--enable-plugins', |
| 87 | '--with-sysroot={}'.format(sysroot), |
| 88 | '--prefix={}'.format(install_dir), |
| 89 | ] |
| 90 | |
| 91 | if arch == 'arm64': |
| 92 | configure_args.append('--enable-fix-cortex-a53-835769') |
Dan Albert | 745a0b6 | 2018-01-30 15:08:33 -0800 | [diff] [blame] | 93 | configure_args.append('--enable-gold') |
| 94 | else: |
| 95 | # Gold for aarch64 currently emits broken debug info. |
| 96 | # https://issuetracker.google.com/70838247 |
| 97 | configure_args.append('--enable-gold=default') |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 98 | |
| 99 | if not is_windows: |
| 100 | # Multithreaded linking is implemented with pthreads, which we |
| 101 | # historically couldn't use on Windows. |
| 102 | # TODO: Try enabling this now that we have winpthreads in mingw. |
| 103 | configure_args.append('--enable-threads') |
| 104 | |
| 105 | env = {} |
| 106 | |
| 107 | m32 = False |
| 108 | if host == 'darwin': |
| 109 | toolchain = ndk.paths.android_path( |
| 110 | 'prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1') |
Dan Albert | 6296125 | 2017-12-13 11:44:52 -0800 | [diff] [blame] | 111 | toolchain_prefix = 'i686-apple-darwin10' |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 112 | env['MACOSX_DEPLOYMENT_TARGET'] = '10.6' |
| 113 | elif host == 'linux': |
| 114 | toolchain = ndk.paths.android_path( |
Ryan Prichard | 1702a21 | 2018-02-26 20:33:58 -0800 | [diff] [blame] | 115 | 'prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8') |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 116 | toolchain_prefix = 'x86_64-linux' |
| 117 | elif is_windows: |
| 118 | toolchain = ndk.paths.android_path( |
| 119 | 'prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8') |
| 120 | toolchain_prefix = 'x86_64-w64-mingw32' |
| 121 | if host == 'win': |
| 122 | m32 = True |
| 123 | else: |
| 124 | raise NotImplementedError |
| 125 | |
| 126 | cc = os.path.join(toolchain, 'bin', '{}-gcc'.format(toolchain_prefix)) |
| 127 | cxx = os.path.join(toolchain, 'bin', '{}-g++'.format(toolchain_prefix)) |
Dan Albert | 10af289 | 2017-12-15 12:22:55 -0800 | [diff] [blame] | 128 | |
| 129 | # Our darwin prebuilts are gcc *only*. No binutils. |
| 130 | if host == 'darwin': |
| 131 | ar = 'ar' |
| 132 | strip = 'strip' |
| 133 | else: |
| 134 | ar = os.path.join(toolchain, 'bin', '{}-ar'.format(toolchain_prefix)) |
| 135 | strip = os.path.join( |
| 136 | toolchain, 'bin', '{}-strip'.format(toolchain_prefix)) |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 137 | |
| 138 | env['AR'] = ar |
| 139 | env['CC'] = cc |
| 140 | env['CXX'] = cxx |
Dan Albert | 08f5b70 | 2017-12-12 15:34:41 -0800 | [diff] [blame] | 141 | env['STRIP'] = strip |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 142 | if m32: |
| 143 | env['CFLAGS'] = '-m32' |
| 144 | env['CXXFLAGS'] = '-m32' |
| 145 | env['LDFLAGS'] = '-m32' |
Dan Albert | 21352c8 | 2018-01-30 23:11:13 -0800 | [diff] [blame] | 146 | else: |
| 147 | env['CFLAGS'] = '-m64' |
| 148 | env['CXXFLAGS'] = '-m64' |
| 149 | env['LDFLAGS'] = '-m64' |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 150 | |
| 151 | env_args = ['env'] + ['='.join([k, v]) for k, v in env.items()] |
| 152 | check_call(env_args + configure_args) |
| 153 | |
| 154 | |
| 155 | def build(jobs): |
| 156 | """Builds binutils.""" |
| 157 | check_call(['make', '-j', str(jobs)]) |
| 158 | |
| 159 | |
| 160 | def install(jobs): |
| 161 | """Installs binutils.""" |
Dan Albert | 08f5b70 | 2017-12-12 15:34:41 -0800 | [diff] [blame] | 162 | check_call(['make', 'install-strip', '-j', str(jobs)]) |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 163 | |
| 164 | |
| 165 | def dist(dist_dir, base_dir, package_name): |
| 166 | """Packages binutils for distribution.""" |
| 167 | has_pbzip2 = ndk.ext.shutil.which('pbzip2') is not None |
| 168 | if has_pbzip2: |
| 169 | compress_arg = '--use-compress-prog=pbzip2' |
| 170 | else: |
| 171 | compress_arg = '-j' |
| 172 | |
Dan Albert | 45d2513 | 2018-01-29 23:31:43 -0800 | [diff] [blame] | 173 | package_path = os.path.join(dist_dir, package_name + '.tar.bz2') |
Dan Albert | 70a30b5 | 2017-07-25 14:49:43 -0700 | [diff] [blame] | 174 | cmd = [ |
| 175 | 'tar', compress_arg, '-cf', package_path, '-C', base_dir, package_name, |
| 176 | ] |
| 177 | subprocess.check_call(cmd) |
| 178 | |
| 179 | |
| 180 | def parse_args(): |
| 181 | """Parse command line arguments.""" |
| 182 | parser = argparse.ArgumentParser() |
| 183 | |
| 184 | parser.add_argument( |
| 185 | '--arch', choices=ndk.abis.ALL_ARCHITECTURES, required=True) |
| 186 | parser.add_argument( |
| 187 | '--host', choices=('darwin', 'linux', 'win', 'win64'), required=True) |
| 188 | |
| 189 | parser.add_argument( |
| 190 | '--clean', action='store_true', |
| 191 | help='Clean the out directory before building.') |
| 192 | parser.add_argument( |
| 193 | '-j', '--jobs', type=int, default=multiprocessing.cpu_count(), |
| 194 | help='Number of jobs to use when building.') |
| 195 | |
| 196 | return parser.parse_args() |
| 197 | |
| 198 | |
| 199 | def main(): |
| 200 | """Program entry point.""" |
| 201 | args = parse_args() |
| 202 | logging.basicConfig(level=logging.INFO) |
| 203 | |
| 204 | total_timer = ndk.timer.Timer() |
| 205 | total_timer.start() |
| 206 | |
| 207 | out_dir = ndk.paths.get_out_dir() |
| 208 | dist_dir = ndk.paths.get_dist_dir(out_dir) |
| 209 | base_build_dir = os.path.join( |
| 210 | out_dir, 'binutils', args.host, args.arch) |
| 211 | build_dir = os.path.join(base_build_dir, 'build') |
| 212 | package_name = 'binutils-{}-{}'.format(args.arch, args.host) |
| 213 | install_dir = os.path.join(base_build_dir, 'install', package_name) |
| 214 | binutils_path = os.path.join(THIS_DIR, 'binutils-2.27') |
| 215 | |
| 216 | did_clean = False |
| 217 | clean_timer = ndk.timer.Timer() |
| 218 | if args.clean and os.path.exists(build_dir): |
| 219 | did_clean = True |
| 220 | with clean_timer: |
| 221 | rmtree(build_dir) |
| 222 | |
| 223 | if not os.path.exists(build_dir): |
| 224 | makedirs(build_dir) |
| 225 | |
| 226 | orig_dir = os.getcwd() |
| 227 | chdir(build_dir) |
| 228 | try: |
| 229 | configure_timer = ndk.timer.Timer() |
| 230 | with configure_timer: |
| 231 | configure(args.arch, args.host, install_dir, binutils_path) |
| 232 | |
| 233 | build_timer = ndk.timer.Timer() |
| 234 | with build_timer: |
| 235 | build(args.jobs) |
| 236 | |
| 237 | install_timer = ndk.timer.Timer() |
| 238 | with install_timer: |
| 239 | install(args.jobs) |
| 240 | finally: |
| 241 | chdir(orig_dir) |
| 242 | |
| 243 | package_timer = ndk.timer.Timer() |
| 244 | with package_timer: |
| 245 | dist(dist_dir, os.path.dirname(install_dir), package_name) |
| 246 | |
| 247 | total_timer.finish() |
| 248 | |
| 249 | if did_clean: |
| 250 | print('Clean: {}'.format(clean_timer.duration)) |
| 251 | print('Configure: {}'.format(configure_timer.duration)) |
| 252 | print('Build: {}'.format(build_timer.duration)) |
| 253 | print('Install: {}'.format(install_timer.duration)) |
| 254 | print('Package: {}'.format(package_timer.duration)) |
| 255 | print('Total: {}'.format(total_timer.duration)) |
| 256 | |
| 257 | |
| 258 | if __name__ == '__main__': |
| 259 | main() |