blob: da47824e64e401445c823c5c926d37e84c4f97d2 [file] [log] [blame]
Dan Albert70a30b52017-07-25 14:49:43 -07001#!/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',
Ryan Prichard6fa214b2018-01-22 22:23:52 -080076 'win': 'i686-w64-mingw32',
77 'win64': 'x86_64-w64-mingw32',
Dan Albert70a30b52017-07-25 14:49:43 -070078 }[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 Albert70a30b52017-07-25 14:49:43 -070085 '--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 Albert745a0b62018-01-30 15:08:33 -080093 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 Albert70a30b52017-07-25 14:49:43 -070098
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 Albert62961252017-12-13 11:44:52 -0800111 toolchain_prefix = 'i686-apple-darwin10'
Dan Albert70a30b52017-07-25 14:49:43 -0700112 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.11-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))
Dan Albert10af2892017-12-15 12:22:55 -0800128
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 Albert70a30b52017-07-25 14:49:43 -0700137
138 env['AR'] = ar
139 env['CC'] = cc
140 env['CXX'] = cxx
Dan Albert08f5b702017-12-12 15:34:41 -0800141 env['STRIP'] = strip
Dan Albert70a30b52017-07-25 14:49:43 -0700142 if m32:
143 env['CFLAGS'] = '-m32'
144 env['CXXFLAGS'] = '-m32'
145 env['LDFLAGS'] = '-m32'
146
147 env_args = ['env'] + ['='.join([k, v]) for k, v in env.items()]
148 check_call(env_args + configure_args)
149
150
151def build(jobs):
152 """Builds binutils."""
153 check_call(['make', '-j', str(jobs)])
154
155
156def install(jobs):
157 """Installs binutils."""
Dan Albert08f5b702017-12-12 15:34:41 -0800158 check_call(['make', 'install-strip', '-j', str(jobs)])
Dan Albert70a30b52017-07-25 14:49:43 -0700159
160
161def dist(dist_dir, base_dir, package_name):
162 """Packages binutils for distribution."""
163 has_pbzip2 = ndk.ext.shutil.which('pbzip2') is not None
164 if has_pbzip2:
165 compress_arg = '--use-compress-prog=pbzip2'
166 else:
167 compress_arg = '-j'
168
Dan Albert45d25132018-01-29 23:31:43 -0800169 package_path = os.path.join(dist_dir, package_name + '.tar.bz2')
Dan Albert70a30b52017-07-25 14:49:43 -0700170 cmd = [
171 'tar', compress_arg, '-cf', package_path, '-C', base_dir, package_name,
172 ]
173 subprocess.check_call(cmd)
174
175
176def parse_args():
177 """Parse command line arguments."""
178 parser = argparse.ArgumentParser()
179
180 parser.add_argument(
181 '--arch', choices=ndk.abis.ALL_ARCHITECTURES, required=True)
182 parser.add_argument(
183 '--host', choices=('darwin', 'linux', 'win', 'win64'), required=True)
184
185 parser.add_argument(
186 '--clean', action='store_true',
187 help='Clean the out directory before building.')
188 parser.add_argument(
189 '-j', '--jobs', type=int, default=multiprocessing.cpu_count(),
190 help='Number of jobs to use when building.')
191
192 return parser.parse_args()
193
194
195def main():
196 """Program entry point."""
197 args = parse_args()
198 logging.basicConfig(level=logging.INFO)
199
200 total_timer = ndk.timer.Timer()
201 total_timer.start()
202
203 out_dir = ndk.paths.get_out_dir()
204 dist_dir = ndk.paths.get_dist_dir(out_dir)
205 base_build_dir = os.path.join(
206 out_dir, 'binutils', args.host, args.arch)
207 build_dir = os.path.join(base_build_dir, 'build')
208 package_name = 'binutils-{}-{}'.format(args.arch, args.host)
209 install_dir = os.path.join(base_build_dir, 'install', package_name)
210 binutils_path = os.path.join(THIS_DIR, 'binutils-2.27')
211
212 did_clean = False
213 clean_timer = ndk.timer.Timer()
214 if args.clean and os.path.exists(build_dir):
215 did_clean = True
216 with clean_timer:
217 rmtree(build_dir)
218
219 if not os.path.exists(build_dir):
220 makedirs(build_dir)
221
222 orig_dir = os.getcwd()
223 chdir(build_dir)
224 try:
225 configure_timer = ndk.timer.Timer()
226 with configure_timer:
227 configure(args.arch, args.host, install_dir, binutils_path)
228
229 build_timer = ndk.timer.Timer()
230 with build_timer:
231 build(args.jobs)
232
233 install_timer = ndk.timer.Timer()
234 with install_timer:
235 install(args.jobs)
236 finally:
237 chdir(orig_dir)
238
239 package_timer = ndk.timer.Timer()
240 with package_timer:
241 dist(dist_dir, os.path.dirname(install_dir), package_name)
242
243 total_timer.finish()
244
245 if did_clean:
246 print('Clean: {}'.format(clean_timer.duration))
247 print('Configure: {}'.format(configure_timer.duration))
248 print('Build: {}'.format(build_timer.duration))
249 print('Install: {}'.format(install_timer.duration))
250 print('Package: {}'.format(package_timer.duration))
251 print('Total: {}'.format(total_timer.duration))
252
253
254if __name__ == '__main__':
255 main()