352252bd1c50eeeeb04981d2bb57ffa7b61f69eb
[platform/framework/web/crosswalk.git] / src / native_client / toolchain_build / toolchain_build.py
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Recipes for NativeClient toolchain packages.
7
8 The real entry plumbing is in toolchain_main.py.
9 """
10
11 import fnmatch
12 import platform
13 import os
14 import re
15 import sys
16
17 import command
18 import gsd_storage
19 import toolchain_main
20
21 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
22 NACL_DIR = os.path.dirname(SCRIPT_DIR)
23
24 GIT_REVISIONS = {
25     'binutils': '38dbda270a4248ab5b7facc012b9c8d8527f6fb2',
26     'gcc': '145a627d95b98f54915d037dddbcbb0f7b283494',
27     'newlib': 'cc9ce45891a45ecfbc671f4a6f1b06ba60a55ad9',
28     }
29
30 TAR_FILES = {
31     'gmp': command.path.join('gmp', 'gmp-5.1.3.tar.bz2'),
32     'mpfr': command.path.join('mpfr', 'mpfr-3.1.2.tar.bz2'),
33     'mpc': command.path.join('mpc', 'mpc-1.0.2.tar.gz'),
34     'isl': command.path.join('cloog', 'isl-0.12.1.tar.bz2'),
35     'cloog': command.path.join('cloog', 'cloog-0.18.1.tar.gz'),
36     }
37
38 GIT_BASE_URL = 'https://chromium.googlesource.com/native_client'
39
40
41 def CollectSources():
42   sources = {}
43
44   for package in TAR_FILES:
45     tar_file = TAR_FILES[package]
46     if fnmatch.fnmatch(tar_file, '*.bz2'):
47       extract = EXTRACT_STRIP_TBZ2
48     elif fnmatch.fnmatch(tar_file, '*.gz'):
49       extract = EXTRACT_STRIP_TGZ
50     else:
51       raise Exception('unexpected file name pattern in TAR_FILES[%r]' % package)
52     sources[package] = {
53         'type': 'source',
54         'commands': [
55             command.Command(extract + [command.path.join('%(abs_top_srcdir)s',
56                                                          '..', 'third_party',
57                                                          tar_file)],
58                             cwd='%(output)s'),
59             ],
60         }
61
62   for package in GIT_REVISIONS:
63     sources[package] = {
64         'type': 'source',
65         'commands': [
66             command.SyncGitRepo('%s/nacl-%s.git' % (GIT_BASE_URL, package),
67                                 '%(output)s', GIT_REVISIONS[package])
68             ]
69         }
70
71   # The gcc_libs component gets the whole GCC source tree.
72   sources['gcc_libs'] = sources['gcc']
73
74   # The gcc component omits all the source directories that are used solely
75   # for building target libraries.  We don't want those included in the
76   # input hash calculation so that we don't rebuild the compiler when the
77   # the only things that have changed are target libraries.
78   sources['gcc'] = {
79         'type': 'source',
80         'dependencies': ['gcc_libs'],
81         'commands': [command.CopyTree('%(gcc_libs)s', '%(output)s', [
82             'boehm-gc',
83             'libada',
84             'libatomic',
85             'libffi',
86             'libgcc',
87             'libgfortran',
88             'libgo',
89             'libgomp',
90             'libitm',
91             'libjava',
92             'libmudflap',
93             'libobjc',
94             'libquadmath',
95             'libsanitizer',
96             'libssp',
97             'libstdc++-v3',
98             ])]
99       }
100
101   # We have to populate the newlib source tree with the "exported" form of
102   # some headers from the native_client source tree.  The newlib build
103   # needs these to be in the expected place.  By doing this in the source
104   # target, these files will be part of the input hash and so we don't need
105   # to do anything else to keep track of when they might have changed in
106   # the native_client source tree.
107   newlib_sys_nacl = command.path.join('%(output)s',
108                                       'newlib', 'libc', 'sys', 'nacl')
109   newlib_unpack = [command.RemoveDirectory(command.path.join(newlib_sys_nacl,
110                                                              dirname))
111                    for dirname in ['bits', 'sys', 'machine']]
112   newlib_unpack.append(command.Command([
113       'python',
114       command.path.join('%(top_srcdir)s', 'src',
115                         'trusted', 'service_runtime', 'export_header.py'),
116       command.path.join('%(top_srcdir)s', 'src',
117                         'trusted', 'service_runtime', 'include'),
118       newlib_sys_nacl,
119       ]))
120   sources['newlib']['commands'] += newlib_unpack
121
122   return sources
123
124
125 # Map of native host tuple to extra tuples that it cross-builds for.
126 EXTRA_HOSTS_MAP = {
127     'i686-linux': [
128         'arm-linux-gnueabihf',
129         # TODO(mcgrathr): Enable this if the binaries are proven to
130         # actually work, and bots get needed mingw* packages installed.
131         #'i686-w64-mingw32',
132         ],
133     }
134
135 # The list of targets to build toolchains for.
136 TARGET_LIST = ['arm', 'i686']
137
138 # These are extra arguments to pass gcc's configure that vary by target.
139 TARGET_GCC_CONFIG = {
140 # TODO(mcgrathr): Disabled tuning for now, tickling a constant-pool layout bug.
141 #    'arm': ['--with-tune=cortex-a15'],
142     }
143
144 PACKAGE_NAME = 'Native Client SDK [%(build_signature)s]'
145 BUG_URL = 'http://gonacl.com/reportissue'
146
147 TAR_XV = ['tar', '-x', '-v']
148 EXTRACT_STRIP_TGZ = TAR_XV + ['--gzip', '--strip-components=1', '-f']
149 EXTRACT_STRIP_TBZ2 = TAR_XV + ['--bzip2', '--strip-components=1', '-f']
150 CONFIGURE_CMD = ['sh', '%(src)s/configure']
151 MAKE_PARALLEL_CMD = ['make', '-j%(cores)s']
152 MAKE_CHECK_CMD = MAKE_PARALLEL_CMD + ['check']
153 MAKE_DESTDIR_CMD = ['make', 'DESTDIR=%(abs_output)s']
154
155 # This file gets installed by multiple packages' install steps, but it is
156 # never useful when installed in isolation.  So we remove it from the
157 # installation directories before packaging up.
158 REMOVE_INFO_DIR = command.Remove(command.path.join('%(output)s',
159                                                    'share', 'info', 'dir'))
160
161 def ConfigureHostArch(host):
162   configure_args = []
163
164   native, extra_cc_args = NativeTuple()
165   is_cross = host != native
166
167   if is_cross:
168     extra_cc_args = []
169     configure_args.append('--host=' + host)
170   elif extra_cc_args:
171     # The host we've chosen is "native enough", such as x86-32 on x86-64.
172     # But it's not what config.guess will yield, so we need to supply
173     # a --build switch to ensure things build correctly.
174     configure_args.append('--build=' + host)
175
176   extra_cxx_args = list(extra_cc_args)
177   if fnmatch.fnmatch(host, '*-linux*'):
178     # Avoid shipping binaries with a runtime dependency on
179     # a particular version of the libstdc++ shared library.
180     # TODO(mcgrathr): Do we want this for MinGW and/or Mac too?
181     extra_cxx_args.append('-static-libstdc++')
182
183   if extra_cc_args:
184     # These are the defaults when there is no setting, but we will add
185     # additional switches, so we must supply the command name too.
186     if is_cross:
187       cc = host + '-gcc'
188     else:
189       cc = 'gcc'
190     configure_args.append('CC=' + ' '.join([cc] + extra_cc_args))
191
192   if extra_cxx_args:
193     # These are the defaults when there is no setting, but we will add
194     # additional switches, so we must supply the command name too.
195     if is_cross:
196       cxx = host + '-g++'
197     else:
198       cxx = 'g++'
199     configure_args.append('CXX=' + ' '.join([cxx] + extra_cxx_args))
200
201   if HostIsWindows(host):
202     # The i18n support brings in runtime dependencies on MinGW DLLs
203     # that we don't want to have to distribute alongside our binaries.
204     # So just disable it, and compiler messages will always be in US English.
205     configure_args.append('--disable-nls')
206
207   return configure_args
208
209
210 def ConfigureHostCommon(host):
211   return ConfigureHostArch(host) + [
212       '--prefix=',
213       '--disable-silent-rules',
214       '--without-gcc-arch',
215       ]
216
217
218 def ConfigureHostLib(host):
219   return ConfigureHostCommon(host) + [
220       '--disable-shared',
221       ]
222
223
224 def ConfigureHostTool(host):
225   return ConfigureHostCommon(host) + [
226       '--with-pkgversion=' + PACKAGE_NAME,
227       '--with-bugurl=' + BUG_URL,
228       '--without-zlib',
229       ]
230
231
232 def MakeCommand(host, extra_args=[]):
233   if HostIsWindows(host):
234     # There appears to be nothing we can pass at top-level configure time
235     # that will prevent the configure scripts from finding MinGW's libiconv
236     # and using it.  We have to force this variable into the environment
237     # of the sub-configure runs, which are run via make.
238     make_command = MAKE_PARALLEL_CMD + ['HAVE_LIBICONV=no']
239   else:
240     make_command = MAKE_PARALLEL_CMD
241   return make_command + extra_args
242
243
244 # Return the 'make check' command to run.
245 # When cross-compiling, don't try to run test suites.
246 def MakeCheckCommand(host):
247   native, _ = NativeTuple()
248   if host != native:
249     return ['true']
250   return MAKE_CHECK_CMD
251
252
253 def InstallDocFiles(subdir, files):
254   doc_dir = command.path.join('%(output)s', 'share', 'doc', subdir)
255   dirs = sorted(set([command.path.dirname(command.path.join(doc_dir, file))
256                      for file in files]))
257   commands = ([command.Mkdir(dir, parents=True) for dir in dirs] +
258               [command.Copy(command.path.join('%(' + subdir + ')s', file),
259                             command.path.join(doc_dir, file))
260                for file in files])
261   return commands
262
263
264 def NewlibLibcScript(arch):
265   template = """/*
266  * This is a linker script that gets installed as libc.a for the
267  * newlib-based NaCl toolchain.  It brings in the constituent
268  * libraries that make up what -lc means semantically.
269  */
270 OUTPUT_FORMAT(%s)
271 GROUP ( libcrt_common.a libnacl.a )
272 """
273   if arch == 'arm':
274     # Listing three formats instead of one makes -EL/-EB switches work
275     # for the endian-switchable ARM backend.
276     format_list = ['elf32-littlearm-nacl',
277                    'elf32-bigarm-nacl',
278                    'elf32-littlearm-nacl']
279   elif arch == 'i686':
280     format_list = 'elf32-i386-nacl'
281   elif arch == 'x86_64':
282     format_list = 'elf32-x86_64-nacl'
283   else:
284     raise Exception('TODO(mcgrathr): OUTPUT_FORMAT for %s' % arch)
285   return template % ', '.join(['"' + fmt + '"' for fmt in format_list])
286
287
288 # The default strip behavior removes debugging and symbol table
289 # sections, but it leaves the .comment section.  This contains the
290 # compiler version string, and so it changes when the compiler changes
291 # even if the actual machine code it produces is completely identical.
292 # Hence, the target library packages will always change when the
293 # compiler changes unless these sections are removed.  Doing this
294 # requires somehow teaching the makefile rules to pass the
295 # --remove-section=.comment switch to TARGET-strip.  For the GCC
296 # target libraries, setting STRIP_FOR_TARGET is sufficient.  But
297 # quoting nightmares make it difficult to pass a command with a space
298 # in it as the STRIP_FOR_TARGET value.  So the build writes a little
299 # script that can be invoked with a simple name.
300 #
301 # Though the gcc target libraries' makefiles are smart enough to obey
302 # STRIP_FOR_TARGET for library files, the newlib makefiles just
303 # blindly use $(INSTALL_DATA) for both header (text) files and library
304 # files.  Hence it's necessary to override its INSTALL_DATA setting to
305 # one that will do stripping using this script, and thus the script
306 # must silently do nothing to non-binary files.
307 def ConfigureTargetPrep(arch):
308   script_file = 'strip_for_target'
309
310   config_target = arch + '-nacl'
311   script_contents = """\
312 #!/bin/sh
313 mode=--strip-all
314 for arg; do
315   case "$arg" in
316   -*) ;;
317   *)
318     type=`file --brief --mime-type "$arg"`
319     case "$type" in
320       application/x-executable|application/x-sharedlib) ;;
321       application/x-archive|application/x-object) mode=--strip-debug ;;
322       *) exit 0 ;;
323     esac
324     ;;
325   esac
326 done
327 exec %s-strip $mode --remove-section=.comment "$@"
328 """ % config_target
329
330   return [
331       command.WriteData(script_contents, script_file),
332       command.Command(['chmod', '+x', script_file]),
333       ]
334
335
336 def ConfigureTargetArgs(arch):
337   config_target = arch + '-nacl'
338   return [
339       '--target=' + config_target,
340       '--with-sysroot=/' + config_target,
341       'STRIP_FOR_TARGET=%(cwd)s/strip_for_target',
342       ]
343
344
345 def CommandsInBuild(command_lines):
346   return [
347       command.RemoveDirectory('build'),
348       command.Mkdir('build'),
349       ] + [command.Command(cmd, cwd='build')
350            for cmd in command_lines]
351
352
353 def PopulateDeps(dep_dirs):
354   commands = [command.RemoveDirectory('all_deps'),
355               command.Mkdir('all_deps')]
356   commands += [command.Command('cp -r "%s/"* all_deps' % dirname, shell=True)
357                for dirname in dep_dirs]
358   return commands
359
360
361 def WithDepsOptions(options, component=None):
362   if component is None:
363     directory = command.path.join('%(cwd)s', 'all_deps')
364   else:
365     directory = '%(abs_' + component + ')s'
366   return ['--with-' + option + '=' + directory
367           for option in options]
368
369
370 # Return the component name we'll use for a base component name and
371 # a host tuple.  The component names cannot contain dashes or other
372 # non-identifier characters, because the names of the files uploaded
373 # to Google Storage are constrained.  GNU configuration tuples contain
374 # dashes, which we translate to underscores.
375 def ForHost(component_name, host):
376   return component_name + '_' + gsd_storage.LegalizeName(host)
377
378
379 # These are libraries that go into building the compiler itself.
380 def HostGccLibs(host):
381   def H(component_name):
382     return ForHost(component_name, host)
383   host_gcc_libs = {
384       H('gmp'): {
385           'type': 'build',
386           'dependencies': ['gmp'],
387           'commands': [
388               command.Command(ConfigureCommand('gmp') +
389                               ConfigureHostLib(host) + [
390                                   '--with-sysroot=%(abs_output)s',
391                                   '--enable-cxx',
392                                   # Without this, the built library will
393                                   # assume the instruction set details
394                                   # available on the build machine.  With
395                                   # this, it dynamically chooses what code
396                                   # to use based on the details of the
397                                   # actual host CPU at runtime.
398                                   '--enable-fat',
399                                   ]),
400               command.Command(MakeCommand(host)),
401               command.Command(MakeCheckCommand(host)),
402               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
403               ],
404           },
405       H('mpfr'): {
406           'type': 'build',
407           'dependencies': ['mpfr', H('gmp')],
408           'commands': [
409               command.Command(ConfigureCommand('mpfr') +
410                               ConfigureHostLib(host) +
411                               WithDepsOptions(['sysroot', 'gmp'], H('gmp'))),
412               command.Command(MakeCommand(host)),
413               command.Command(MakeCheckCommand(host)),
414               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
415               ],
416           },
417       H('mpc'): {
418           'type': 'build',
419           'dependencies': ['mpc', H('gmp'), H('mpfr')],
420           'commands': PopulateDeps(['%(' + H('gmp') + ')s',
421                                     '%(' + H('mpfr') + ')s']) + [
422               command.Command(ConfigureCommand('mpc') +
423                               ConfigureHostLib(host) +
424                               WithDepsOptions(['sysroot', 'gmp', 'mpfr'])),
425               command.Command(MakeCommand(host)),
426               command.Command(MakeCheckCommand(host)),
427               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
428               ],
429           },
430       H('isl'): {
431           'type': 'build',
432           'dependencies': ['isl', H('gmp')],
433           'commands': [
434               command.Command(ConfigureCommand('isl') +
435                               ConfigureHostLib(host) +
436                               WithDepsOptions(['sysroot', 'gmp-prefix'],
437                                               H('gmp'))),
438               command.Command(MakeCommand(host)),
439               command.Command(MakeCheckCommand(host)),
440               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
441               # The .pc files wind up containing some absolute paths
442               # that make the output depend on the build directory name.
443               # The dependents' configure scripts don't need them anyway.
444               command.RemoveDirectory(command.path.join(
445                   '%(output)s', 'lib', 'pkgconfig')),
446               ],
447           },
448       H('cloog'): {
449           'type': 'build',
450           'dependencies': ['cloog', H('gmp'), H('isl')],
451           'commands': PopulateDeps(['%(' + H('gmp') + ')s',
452                                     '%(' + H('isl') + ')s']) + [
453               command.Command(ConfigureCommand('cloog') +
454                               ConfigureHostLib(host) + [
455                                   '--with-bits=gmp',
456                                   '--with-isl=system',
457                                   ] + WithDepsOptions(['sysroot',
458                                                        'gmp-prefix',
459                                                        'isl-prefix'])),
460               command.Command(MakeCommand(host)),
461               command.Command(MakeCheckCommand(host)),
462               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
463               # The .pc files wind up containing some absolute paths
464               # that make the output depend on the build directory name.
465               # The dependents' configure scripts don't need them anyway.
466               command.RemoveDirectory(command.path.join(
467                   '%(output)s', 'lib', 'pkgconfig')),
468               ],
469           },
470       }
471   return host_gcc_libs
472
473
474 HOST_GCC_LIBS_DEPS = ['gmp', 'mpfr', 'mpc', 'isl', 'cloog']
475
476 def HostGccLibsDeps(host):
477   return [ForHost(package, host) for package in HOST_GCC_LIBS_DEPS]
478
479
480 def ConfigureCommand(source_component):
481   return [command % {'src': '%(' + source_component + ')s'}
482           for command in CONFIGURE_CMD]
483
484
485 # When doing a Canadian cross, we need native-hosted cross components
486 # to do the GCC build.
487 def GccDeps(host, target):
488   native, _ = NativeTuple()
489   components = ['binutils_' + target]
490   if host != native:
491     components.append('gcc_' + target)
492     host = native
493   return [ForHost(component, host) for component in components]
494
495
496 def GccCommand(host, target, cmd):
497   components_for_path = GccDeps(host, target)
498   return command.Command(
499       cmd, path_dirs=[command.path.join('%(abs_' + component + ')s', 'bin')
500                       for component in components_for_path])
501
502
503 def ConfigureGccCommand(source_component, host, target, extra_args=[]):
504   target_cflagstr = ' '.join(CommonTargetCflags(target))
505   return GccCommand(
506       host,
507       target,
508       ConfigureCommand(source_component) +
509       ConfigureHostTool(host) +
510       ConfigureTargetArgs(target) +
511       TARGET_GCC_CONFIG.get(target, []) + [
512           '--with-gmp=%(abs_' + ForHost('gmp', host) + ')s',
513           '--with-mpfr=%(abs_' + ForHost('mpfr', host) + ')s',
514           '--with-mpc=%(abs_' + ForHost('mpc', host) + ')s',
515           '--with-isl=%(abs_' + ForHost('isl', host) + ')s',
516           '--with-cloog=%(abs_' + ForHost('cloog', host) + ')s',
517           '--enable-cloog-backend=isl',
518           '--disable-dlopen',
519           '--disable-shared',
520           '--with-newlib',
521           '--with-linker-hash-style=gnu',
522           '--enable-linker-build-id',
523           '--enable-languages=c,c++,lto',
524           'CFLAGS_FOR_TARGET=' + target_cflagstr,
525           'CXXFLAGS_FOR_TARGET=' + target_cflagstr,
526           ] + extra_args)
527
528
529
530 def HostTools(host, target):
531   def H(component_name):
532     return ForHost(component_name, host)
533   tools = {
534       H('binutils_' + target): {
535           'type': 'build',
536           'dependencies': ['binutils'],
537           'commands': ConfigureTargetPrep(target) + [
538               command.Command(
539                   ConfigureCommand('binutils') +
540                   ConfigureHostTool(host) +
541                   ConfigureTargetArgs(target) + [
542                       '--enable-deterministic-archives',
543                       '--enable-gold',
544                       ] + ([] if HostIsWindows(host) else [
545                           '--enable-plugins',
546                           ])),
547               command.Command(MakeCommand(host)),
548               command.Command(MakeCheckCommand(host)),
549               command.Command(MAKE_DESTDIR_CMD + ['install-strip']),
550               REMOVE_INFO_DIR,
551               ] + InstallDocFiles('binutils',
552                                   ['COPYING3'] +
553                                   [command.path.join(subdir, 'NEWS')
554                                    for subdir in
555                                    ['binutils', 'gas', 'ld', 'gold']]) +
556               # The top-level lib* directories contain host libraries
557               # that we don't want to include in the distribution.
558               [command.RemoveDirectory(command.path.join('%(output)s', name))
559                for name in ['lib', 'lib32', 'lib64']],
560           },
561
562       H('gcc_' + target): {
563           'type': 'build',
564           'dependencies': (['gcc'] + HostGccLibsDeps(host) +
565                            GccDeps(host, target)),
566           'commands': ConfigureTargetPrep(target) + [
567               ConfigureGccCommand('gcc', host, target),
568               # GCC's configure step writes configargs.h with some strings
569               # including the configure command line, which get embedded
570               # into the gcc driver binary.  The build only works if we use
571               # absolute paths in some of the configure switches, but
572               # embedding those paths makes the output differ in repeated
573               # builds done in different directories, which we do not want.
574               # So force the generation of that file early and then edit it
575               # in place to replace the absolute paths with something that
576               # never varies.  Note that the 'configure-gcc' target will
577               # actually build some components before running gcc/configure.
578               GccCommand(host, target,
579                          MakeCommand(host, ['configure-gcc'])),
580               command.Command(['sed', '-i', '-e',
581                                ';'.join(['s@%%(abs_%s)s@.../%s_install@g' %
582                                          (component, component)
583                                          for component in
584                                          HostGccLibsDeps(host)] +
585                                         ['s@%(cwd)s@...@g']),
586                                command.path.join('gcc', 'configargs.h')]),
587               # gcc/Makefile's install rules ordinarily look at the
588               # installed include directory for a limits.h to decide
589               # whether the lib/gcc/.../include-fixed/limits.h header
590               # should be made to expect a libc-supplied limits.h or not.
591               # Since we're doing this build in a clean environment without
592               # any libc installed, we need to force its hand here.
593               GccCommand(host, target,
594                          MakeCommand(host, ['all-gcc', 'LIMITS_H_TEST=true'])),
595               # gcc/Makefile's install targets populate this directory
596               # only if it already exists.
597               command.Mkdir(command.path.join('%(output)s',
598                                               target + '-nacl', 'bin'),
599                             True),
600               GccCommand(host, target,
601                          MAKE_DESTDIR_CMD + ['install-strip-gcc']),
602               REMOVE_INFO_DIR,
603               # Note we include COPYING.RUNTIME here and not with gcc_libs.
604               ] + InstallDocFiles('gcc', ['COPYING3', 'COPYING.RUNTIME']),
605           },
606       }
607   return tools
608
609
610 # configure defaults to -g -O2 but passing an explicit option overrides that.
611 # So we have to list -g -O2 explicitly since we need to add -mtp=soft.
612 def CommonTargetCflags(target):
613   options = ['-g', '-O2']
614   if target == 'arm':
615     options.append('-mtp=soft')
616   return options
617
618
619 def TargetCommands(host, target, command_list):
620   # First we have to copy the host tools into a common directory.
621   # We can't just have both directories in our PATH, because the
622   # compiler looks for the assembler and linker relative to itself.
623   commands = PopulateDeps(['%(' + ForHost('binutils_' + target, host) + ')s',
624                            '%(' + ForHost('gcc_' + target, host) + ')s'])
625   bindir = command.path.join('%(cwd)s', 'all_deps', 'bin')
626   commands += [command.Command(cmd, path_dirs=[bindir])
627                for cmd in command_list]
628   return commands
629
630
631 def TargetLibs(host, target):
632   lib_deps = [ForHost(component + '_' + target, host)
633               for component in ['binutils', 'gcc']]
634
635   def NewlibFile(subdir, name):
636     return command.path.join('%(output)s', target + '-nacl', subdir, name)
637
638   newlib_sysroot = '%(abs_newlib_' + target + ')s'
639   newlib_tooldir = '%s/%s-nacl' % (newlib_sysroot, target)
640
641   # See the comment at ConfigureTargetPrep, above.
642   newlib_install_data = ' '.join(['STRIPPROG=%(cwd)s/strip_for_target',
643                                   '%(abs_newlib)s/install-sh',
644                                   '-c', '-s', '-m', '644'])
645
646   iconv_encodings = 'UTF-8,UTF-16LE,UCS-4LE,UTF-16,UCS-4'
647   newlib_configure_args = [
648       '--disable-libgloss',
649       '--enable-newlib-iconv',
650       '--enable-newlib-iconv-from-encodings=' + iconv_encodings,
651       '--enable-newlib-iconv-to-encodings=' + iconv_encodings,
652       '--enable-newlib-io-long-long',
653       '--enable-newlib-io-long-double',
654       '--enable-newlib-io-c99-formats',
655       '--enable-newlib-mb',
656       'CFLAGS=-O2',
657       'CFLAGS_FOR_TARGET=' + ' '.join(CommonTargetCflags(target)),
658       'INSTALL_DATA=' + newlib_install_data,
659       ]
660
661   newlib_post_install = [
662       command.Rename(NewlibFile('lib', 'libc.a'),
663                      NewlibFile('lib', 'libcrt_common.a')),
664       command.WriteData(NewlibLibcScript(target),
665                         NewlibFile('lib', 'libc.a')),
666       ] + [
667       command.Copy(
668           command.path.join('%(pthread_headers)s', header),
669           NewlibFile('include', header))
670       for header in ('pthread.h', 'semaphore.h')
671       ]
672
673
674   libs = {
675       'newlib_' + target: {
676           'type': 'build',
677           'dependencies': ['newlib'] + lib_deps,
678           'inputs': { 'pthread_headers':
679                       os.path.join(NACL_DIR, 'src', 'untrusted',
680                                    'pthread') },
681           'commands': (ConfigureTargetPrep(target) +
682                        TargetCommands(host, target, [
683                            ConfigureCommand('newlib') +
684                            ConfigureHostTool(host) +
685                            ConfigureTargetArgs(target) +
686                            newlib_configure_args,
687                            MakeCommand(host),
688                            MAKE_DESTDIR_CMD + ['install-strip'],
689                            ]) +
690                        newlib_post_install +
691                        InstallDocFiles('newlib', ['COPYING.NEWLIB'])),
692           },
693
694       'gcc_libs_' + target: {
695           'type': 'build',
696           'dependencies': (['gcc_libs'] + lib_deps + ['newlib_' + target] +
697                            HostGccLibsDeps(host)),
698           # This actually builds the compiler again and uses that compiler
699           # to build the target libraries.  That's by far the easiest thing
700           # to get going given the interdependencies of the target
701           # libraries (especially libgcc) on the gcc subdirectory, and
702           # building the compiler doesn't really take all that long in the
703           # grand scheme of things.
704           # TODO(mcgrathr): If upstream ever cleans up all their
705           # interdependencies better, unpack the compiler, configure with
706           # --disable-gcc, and just build all-target.
707           'commands': ConfigureTargetPrep(target) + [
708               ConfigureGccCommand('gcc_libs', host, target, [
709                   '--with-build-sysroot=' + newlib_sysroot,
710                   ]),
711               GccCommand(host, target,
712                          MakeCommand(host) + [
713                              'build_tooldir=' + newlib_tooldir,
714                              'all-target',
715                              ]),
716               GccCommand(host, target,
717                          MAKE_DESTDIR_CMD + ['install-strip-target']),
718               REMOVE_INFO_DIR,
719               ],
720           },
721       }
722   return libs
723
724
725 def NativeTuple():
726   if sys.platform.startswith('linux'):
727     machine = platform.machine().lower()
728     if machine.startswith('arm'):
729       return ('arm-linux-gnueabihf', [])
730     if any(fnmatch.fnmatch(machine, pattern) for pattern in
731            ['x86_64*', 'amd64*', 'x64*', 'i?86*']):
732       # We build the tools for x86-32 hosts so they will run on either x86-32
733       # or x86-64 hosts (with the right compatibility libraries installed).
734       # So for an x86-64 host, we call x86-32 the "native" machine.
735       return ('i686-linux', ['-m32'])
736     raise Exception('Machine %s not recognized' % machine)
737   elif sys.platform.startswith('win'):
738     return ('i686-w64-mingw32', [])
739   elif sys.platform.startswith('darwin'):
740     return ('x86_64-apple-darwin', [])
741   raise Exception('Platform %s not recognized' % sys.platform)
742
743
744 def HostIsWindows(host):
745   return host == 'i686-w64-mingw32'
746
747
748 # We build target libraries only on Linux for two reasons:
749 # 1. We only need to build them once.
750 # 2. Linux is the fastest to build.
751 # TODO(mcgrathr): In future set up some scheme whereby non-Linux
752 # bots can build target libraries but not archive them, only verifying
753 # that the results came out the same as the ones archived by the
754 # official builder bot.  That will serve as a test of the host tools
755 # on the other host platforms.
756 def BuildTargetLibsOn(host):
757   return host == 'i686-linux'
758
759
760 def CollectPackagesForHost(host, targets):
761   packages = HostGccLibs(host).copy()
762   for target in targets:
763     packages.update(HostTools(host, target))
764     if BuildTargetLibsOn(host):
765       packages.update(TargetLibs(host, target))
766   return packages
767
768
769 def CollectPackages(targets):
770   packages = CollectSources()
771
772   native, _ = NativeTuple()
773   packages.update(CollectPackagesForHost(native, targets))
774
775   for host in EXTRA_HOSTS_MAP.get(native, []):
776     packages.update(CollectPackagesForHost(host, targets))
777
778   return packages
779
780
781 PACKAGES = CollectPackages(TARGET_LIST)
782
783
784 if __name__ == '__main__':
785   tb = toolchain_main.PackageBuilder(PACKAGES, sys.argv[1:])
786   # TODO(mcgrathr): The bot ought to run some native_client tests
787   # using the new toolchain, like the old x86 toolchain bots do.
788   tb.Main()