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.""" 18from __future__ import print_function 19 20import argparse 21import logging 22import multiprocessing 23import os 24import shutil 25import site 26import subprocess 27 28 29THIS_DIR = os.path.realpath(os.path.dirname(__file__)) 30site.addsitedir(os.path.join(THIS_DIR, '../../ndk')) 31 32# pylint: disable=wrong-import-position 33import ndk.abis # pylint: disable=import-error 34import ndk.ext.shutil # pylint: disable=import-error 35import ndk.paths # pylint: disable=import-error 36import ndk.timer # pylint: disable=import-error 37# pylint: enable=wrong-import-position 38 39 40def logger(): 41 """Returns the module level logger.""" 42 return logging.getLogger(__name__) 43 44 45def makedirs(path): 46 """os.makedirs with logging.""" 47 logger().info('makedirs ' + path) 48 os.makedirs(path) 49 50 51def rmtree(path): 52 """shutil.rmtree with logging.""" 53 logger().info('rmtree ' + path) 54 shutil.rmtree(path) 55 56 57def chdir(path): 58 """os.chdir with logging.""" 59 logger().info('chdir ' + path) 60 os.chdir(path) 61 62 63def 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 69def 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', 76 'win': 'i686-w64-mingw32', 77 'win64': 'x86_64-w64-mingw32', 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), 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') 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') 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') 111 toolchain_prefix = 'i686-apple-darwin10' 112 env['MACOSX_DEPLOYMENT_TARGET'] = '10.6' 113 elif host == 'linux': 114 toolchain = ndk.paths.android_path( 115 'prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8') 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)) 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)) 137 138 env['AR'] = ar 139 env['CC'] = cc 140 env['CXX'] = cxx 141 env['STRIP'] = strip 142 if m32: 143 env['CFLAGS'] = '-m32' 144 env['CXXFLAGS'] = '-m32' 145 env['LDFLAGS'] = '-m32' 146 else: 147 env['CFLAGS'] = '-m64' 148 env['CXXFLAGS'] = '-m64' 149 env['LDFLAGS'] = '-m64' 150 151 env_args = ['env'] + ['='.join([k, v]) for k, v in env.items()] 152 check_call(env_args + configure_args) 153 154 155def build(jobs): 156 """Builds binutils.""" 157 check_call(['make', '-j', str(jobs)]) 158 159 160def install(jobs): 161 """Installs binutils.""" 162 check_call(['make', 'install-strip', '-j', str(jobs)]) 163 164 165def 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 173 package_path = os.path.join(dist_dir, package_name + '.tar.bz2') 174 cmd = [ 175 'tar', compress_arg, '-cf', package_path, '-C', base_dir, package_name, 176 ] 177 subprocess.check_call(cmd) 178 179 180def 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 199def 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 258if __name__ == '__main__': 259 main() 260