Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / native_client / toolchain_build / toolchain_build_pnacl.py
1 #!/usr/bin/python
2 # Copyright (c) 2013 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 PNaCl toolchain packages.
7
8    Recipes consist of specially-structured dictionaries, with keys for package
9    name, type, commands to execute, etc. The structure is documented in the
10    PackageBuilder docstring in toolchain_main.py.
11
12    The real entry plumbing and CLI flags are also in toolchain_main.py.
13 """
14
15 import fnmatch
16 import logging
17 import os
18 import shutil
19 import sys
20 import zipfile
21
22 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
23 import pynacl.gsd_storage
24 import pynacl.platform
25 import pynacl.repo_tools
26
27 import command
28 import pnacl_commands
29 import pnacl_targetlibs
30 import toolchain_main
31
32 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
33 NACL_DIR = os.path.dirname(SCRIPT_DIR)
34 # Use the argparse from third_party to ensure it's the same on all platorms
35 python_lib_dir = os.path.join(os.path.dirname(NACL_DIR), 'third_party',
36                               'python_libs', 'argparse')
37 sys.path.insert(0, python_lib_dir)
38 import argparse
39
40 PNACL_DRIVER_DIR = os.path.join(NACL_DIR, 'pnacl', 'driver')
41 NACL_TOOLS_DIR = os.path.join(NACL_DIR, 'tools')
42
43 # Scons tests can check this version number to decide whether to enable tests
44 # for toolchain bug fixes or new features.  This allows tests to be enabled on
45 # the toolchain buildbots/trybots before the new toolchain version is pinned
46 # (i.e. before the tests would pass on the main NaCl buildbots/trybots).
47 # If you are adding a test that depends on a toolchain change, you can
48 # increment this version number manually.
49 FEATURE_VERSION = 9
50
51 # For backward compatibility, these key names match the directory names
52 # previously used with gclient
53 GIT_REPOS = {
54     'binutils': 'nacl-binutils.git',
55     'clang': 'pnacl-clang.git',
56     'llvm': 'pnacl-llvm.git',
57     'gcc': 'pnacl-gcc.git',
58     'libcxx': 'pnacl-libcxx.git',
59     'libcxxabi': 'pnacl-libcxxabi.git',
60     'nacl-newlib': 'nacl-newlib.git',
61     'llvm-test-suite': 'pnacl-llvm-testsuite.git',
62     'compiler-rt': 'pnacl-compiler-rt.git',
63     'subzero': 'pnacl-subzero.git',
64     'binutils-x86': 'nacl-binutils.git',
65     }
66
67 GIT_BASE_URL = 'https://chromium.googlesource.com/native_client/'
68 GIT_PUSH_URL = 'ssh://gerrit.chromium.org/native_client/'
69 GIT_DEPS_FILE = os.path.join(NACL_DIR, 'pnacl', 'COMPONENT_REVISIONS')
70
71 ALT_GIT_BASE_URL = 'https://chromium.googlesource.com/a/native_client/'
72
73 KNOWN_MIRRORS = [('http://git.chromium.org/native_client/', GIT_BASE_URL)]
74 PUSH_MIRRORS = [('http://git.chromium.org/native_client/', GIT_PUSH_URL),
75                 (ALT_GIT_BASE_URL, GIT_PUSH_URL),
76                 (GIT_BASE_URL, GIT_PUSH_URL)]
77
78 PACKAGE_NAME = 'Native Client SDK [%(build_signature)s]'
79 BUG_URL = 'http://gonacl.com/reportissue'
80
81 # TODO(dschuff): Some of this mingw logic duplicates stuff in command.py
82 BUILD_CROSS_MINGW = False
83 # Path to the mingw cross-compiler libs on Ubuntu
84 CROSS_MINGW_LIBPATH = '/usr/lib/gcc/i686-w64-mingw32/4.6'
85 # Path and version of the native mingw compiler to be installed on Windows hosts
86 MINGW_PATH = os.path.join(NACL_DIR, 'mingw32')
87 MINGW_VERSION = 'i686-w64-mingw32-4.8.1'
88
89 CHROME_CLANG = os.path.join(os.path.dirname(NACL_DIR), 'third_party',
90                             'llvm-build', 'Release+Asserts', 'bin', 'clang')
91 CHROME_CLANGXX = CHROME_CLANG + '++'
92
93 # Redirectors are small shims acting like sym links with optional arguments.
94 # For mac/linux we simply use a shell script which create small redirector
95 # shell scripts. For windows we compile an executable which redirects to
96 # the target using a compiled in table.
97 REDIRECTOR_SCRIPT = os.path.join(NACL_TOOLS_DIR, 'create_redirector.sh')
98 REDIRECTOR_WIN32_SRC = os.path.join(NACL_TOOLS_DIR, 'redirector')
99
100 TOOL_X64_I686_REDIRECTS = [
101     #Toolname, Tool Args
102     ('as',     '--32'),
103     ('ld',     '-melf_i386_nacl'),
104     ]
105
106 TRANSLATOR_ARCHES = ('x86-32', 'x86-64', 'arm', 'mips32',
107                      'x86-32-nonsfi', 'arm-nonsfi')
108 # MIPS32 doesn't use biased bitcode, and nonsfi targets don't need it.
109 BITCODE_BIASES = tuple(
110     bias for bias in ('le32', 'i686_bc', 'x86_64_bc', 'arm_bc'))
111
112 DIRECT_TO_NACL_ARCHES = ('x86_64', 'i686')
113
114 MAKE_DESTDIR_CMD = ['make', 'DESTDIR=%(abs_output)s']
115
116 def TripleIsWindows(t):
117   return fnmatch.fnmatch(t, '*-mingw32*')
118
119 def TripleIsCygWin(t):
120   return fnmatch.fnmatch(t, '*-cygwin*')
121
122 def TripleIsLinux(t):
123   return fnmatch.fnmatch(t, '*-linux*')
124
125 def TripleIsMac(t):
126   return fnmatch.fnmatch(t, '*-darwin*')
127
128 def TripleIsX8664(t):
129   return fnmatch.fnmatch(t, 'x86_64*')
130
131
132 # Return a tuple (C compiler, C++ compiler) of the compilers to compile the host
133 # toolchains
134 def CompilersForHost(host):
135   compiler = {
136       # For now we only do native builds for linux and mac
137       # treat 32-bit linux like a native build
138       'i686-linux': (CHROME_CLANG, CHROME_CLANGXX),
139       'x86_64-linux': (CHROME_CLANG, CHROME_CLANGXX),
140       'x86_64-apple-darwin': (CHROME_CLANG, CHROME_CLANGXX),
141       # Windows build should work for native and cross
142       'i686-w64-mingw32': ('i686-w64-mingw32-gcc', 'i686-w64-mingw32-g++'),
143       # TODO: add arm-hosted support
144       'i686-pc-cygwin': ('gcc', 'g++'),
145   }
146   return compiler[host]
147
148
149 def GSDJoin(*args):
150   return '_'.join([pynacl.gsd_storage.LegalizeName(arg) for arg in args])
151
152
153 def ConfigureHostArchFlags(host, extra_cflags, options):
154   """ Return flags passed to LLVM and binutils configure for compilers and
155   compile flags. """
156   configure_args = []
157   extra_cc_args = []
158
159   native = pynacl.platform.PlatformTriple()
160   is_cross = host != native
161   if is_cross:
162     if (pynacl.platform.IsLinux64() and
163         fnmatch.fnmatch(host, '*-linux*')):
164       # 64 bit linux can build 32 bit linux binaries while still being a native
165       # build for our purposes. But it's not what config.guess will yield, so
166       # use --build to force it and make sure things build correctly.
167       configure_args.append('--build=' + host)
168     else:
169       configure_args.append('--host=' + host)
170   if TripleIsLinux(host) and not TripleIsX8664(host):
171     # Chrome clang defaults to 64-bit builds, even when run on 32-bit Linux.
172     extra_cc_args = ['-m32']
173
174   extra_cxx_args = list(extra_cc_args)
175
176   if not options.gcc:
177     cc, cxx = CompilersForHost(host)
178
179     configure_args.append('CC=' + ' '.join([cc] + extra_cc_args))
180     configure_args.append('CXX=' + ' '.join([cxx] + extra_cxx_args))
181
182   if TripleIsWindows(host):
183     # The i18n support brings in runtime dependencies on MinGW DLLs
184     # that we don't want to have to distribute alongside our binaries.
185     # So just disable it, and compiler messages will always be in US English.
186     configure_args.append('--disable-nls')
187     configure_args.extend(['LDFLAGS=-L%(abs_libdl)s -ldl',
188                            'CFLAGS=-isystem %(abs_libdl)s',
189                            'CXXFLAGS=-isystem %(abs_libdl)s'])
190     if is_cross:
191       # LLVM's linux->mingw cross build needs this
192       configure_args.append('CC_FOR_BUILD=gcc')
193   else:
194     if options.gcc:
195       configure_args.extend(['CFLAGS=' + ' '.join(extra_cflags),
196                              'CXXFLAGS=' + ' '.join(extra_cflags)])
197     else:
198       configure_args.extend(
199        ['CFLAGS=' + ' '.join(extra_cflags),
200         'LDFLAGS=-L%(' + GSDJoin('abs_libcxx', host) + ')s/lib',
201         'CXXFLAGS=-stdlib=libc++ -I%(' + GSDJoin('abs_libcxx', host) +
202         ')s/include/c++/v1 ' + ' '.join(extra_cflags)])
203
204   return configure_args
205
206
207 def LibCxxHostArchFlags(host):
208   cc, cxx = CompilersForHost(host)
209   cmake_flags = []
210   cmake_flags.extend(['-DCMAKE_C_COMPILER='+cc, '-DCMAKE_CXX_COMPILER='+cxx])
211   if TripleIsLinux(host) and not TripleIsX8664(host):
212     # Chrome clang defaults to 64-bit builds, even when run on 32-bit Linux
213     cmake_flags.extend(['-DCMAKE_C_FLAGS=-m32',
214                         '-DCMAKE_CXX_FLAGS=-m32'])
215   return cmake_flags
216
217 def CmakeHostArchFlags(host, options):
218   """ Set flags passed to LLVM cmake for compilers and compile flags. """
219   cmake_flags = []
220   cc, cxx = CompilersForHost(host)
221
222   cmake_flags.extend(['-DCMAKE_C_COMPILER='+cc, '-DCMAKE_CXX_COMPILER='+cxx])
223
224   # There seems to be a bug in chrome clang where it exposes the msan interface
225   # (even when compiling without msan) but then does not link with an
226   # msan-enabled compiler_rt, leaving references to __msan_allocated_memory
227   # undefined.
228   cmake_flags.append('-DHAVE_SANITIZER_MSAN_INTERFACE_H=FALSE')
229
230   if options.sanitize:
231     cmake_flags.extend(['-DCMAKE_%s_FLAGS=-fsanitize=%s' % (c, options.sanitize)
232                         for c in ('C', 'CXX')])
233     cmake_flags.append('-DCMAKE_EXE_LINKER_FLAGS=-fsanitize=%s' %
234                        options.sanitize)
235
236   return cmake_flags
237
238
239 def ConfigureBinutilsCommon():
240   return ['--with-pkgversion=' + PACKAGE_NAME,
241           '--with-bugurl=' + BUG_URL,
242           '--without-zlib',
243           '--prefix=',
244           '--disable-silent-rules',
245           '--enable-deterministic-archives',
246          ]
247
248 def LLVMConfigureAssertionsFlags(options):
249   if options.enable_llvm_assertions:
250     return []
251   else:
252     return ['--disable-debug', '--disable-assertions']
253
254
255 def MakeCommand(host):
256   make_command = ['make']
257   if not pynacl.platform.IsWindows() or pynacl.platform.IsCygWin():
258     # The make that ships with msys sometimes hangs when run with -j.
259     # The ming32-make that comes with the compiler itself reportedly doesn't
260     # have this problem, but it has issues with pathnames with LLVM's build.
261     make_command.append('-j%(cores)s')
262
263   if TripleIsWindows(host):
264     # There appears to be nothing we can pass at top-level configure time
265     # that will prevent the configure scripts from finding MinGW's libiconv
266     # and using it.  We have to force this variable into the environment
267     # of the sub-configure runs, which are run via make.
268     make_command.append('HAVE_LIBICONV=no')
269   return make_command
270
271
272 def CopyWindowsHostLibs(host):
273   if not TripleIsWindows(host) and not TripleIsCygWin(host):
274     return []
275
276   if TripleIsCygWin(host):
277     lib_path = '/bin'
278     libs = ('cyggcc_s-1.dll', 'cygiconv-2.dll', 'cygwin1.dll',
279             'cygintl-8.dll', 'cygstdc++-6.dll', 'cygz.dll')
280   elif pynacl.platform.IsWindows():
281     lib_path = os.path.join(MINGW_PATH, 'bin')
282     # The native minGW compiler uses winpthread, but the Ubuntu cross compiler
283     # does not.
284     libs = ('libgcc_s_sjlj-1.dll', 'libstdc++-6.dll', 'libwinpthread-1.dll')
285   else:
286     lib_path = os.path.join(CROSS_MINGW_LIBPATH)
287     libs = ('libgcc_s_sjlj-1.dll', 'libstdc++-6.dll')
288   return [command.Copy(
289                   os.path.join(lib_path, lib),
290                   os.path.join('%(output)s', 'bin', lib))
291                for lib in libs]
292
293 def GetGitSyncCmdsCallback(revisions):
294   """Return a callback which returns the git sync commands for a component.
295
296      This allows all the revision information to be processed here while giving
297      other modules like pnacl_targetlibs.py the ability to define their own
298      source targets with minimal boilerplate.
299   """
300   def GetGitSyncCmds(component):
301     git_url = GIT_BASE_URL + GIT_REPOS[component]
302     git_push_url = GIT_PUSH_URL + GIT_REPOS[component]
303
304     return (command.SyncGitRepoCmds(git_url, '%(output)s', revisions[component],
305                                     git_cache='%(git_cache_dir)s',
306                                     push_url=git_push_url,
307                                     known_mirrors=KNOWN_MIRRORS,
308                                     push_mirrors=PUSH_MIRRORS) +
309             [command.Runnable(lambda opts: opts.IsBot(),
310                               pnacl_commands.CmdCheckoutGitBundleForTrybot,
311                               component, '%(output)s')])
312
313   return GetGitSyncCmds
314
315
316 def HostToolsSources(GetGitSyncCmds):
317   sources = {
318       'libcxx_src': {
319           'type': 'source',
320           'output_dirname': 'libcxx',
321           'commands': GetGitSyncCmds('libcxx'),
322       },
323       'libcxxabi_src': {
324           'type': 'source',
325           'output_dirname': 'libcxxabi',
326           'commands': GetGitSyncCmds('libcxxabi'),
327       },
328       'binutils_pnacl_src': {
329           'type': 'source',
330           'output_dirname': 'binutils',
331           'commands': GetGitSyncCmds('binutils'),
332       },
333       # For some reason, the llvm build using --with-clang-srcdir chokes if the
334       # clang source directory is named something other than 'clang', so don't
335       # change output_dirname for clang.
336       'clang_src': {
337           'type': 'source',
338           'output_dirname': 'clang',
339           'commands': GetGitSyncCmds('clang'),
340       },
341       'llvm_src': {
342           'type': 'source',
343           'output_dirname': 'llvm',
344           'commands': GetGitSyncCmds('llvm'),
345       },
346       'subzero_src': {
347           'type': 'source',
348           'output_dirname': 'subzero',
349           'commands': GetGitSyncCmds('subzero'),
350       },
351       'binutils_x86_src': {
352           'type': 'source',
353           'output_dirname': 'binutils-x86',
354           'commands': GetGitSyncCmds('binutils-x86'),
355       },
356   }
357   return sources
358
359 def TestsuiteSources(GetGitSyncCmds):
360   sources = {
361       'llvm_testsuite_src': {
362           'type': 'source',
363           'output_dirname': 'llvm-test-suite',
364           'commands': GetGitSyncCmds('llvm-test-suite'),
365       },
366   }
367   return sources
368
369
370 def CopyHostLibcxxForLLVMBuild(host, dest, options):
371   """Copy libc++ to the working directory for build tools."""
372   if options.gcc:
373     return []
374   if TripleIsLinux(host):
375     libname = 'libc++.so.1'
376   elif TripleIsMac(host):
377     libname = 'libc++.1.dylib'
378   else:
379     return []
380   return [command.Mkdir(dest, parents=True),
381           command.Copy('%(' + GSDJoin('abs_libcxx', host) +')s/lib/' + libname,
382                        os.path.join(dest, libname))]
383
384 def HostLibs(host, options):
385   def H(component_name):
386     # Return a package name for a component name with a host triple.
387     return GSDJoin(component_name, host)
388   libs = {}
389   if TripleIsWindows(host):
390     if pynacl.platform.IsWindows():
391       ar = 'ar'
392     else:
393       ar = 'i686-w64-mingw32-ar'
394
395     libs.update({
396       'libdl': {
397           'type': 'build',
398           'inputs' : { 'src' : os.path.join(NACL_DIR, '..', 'third_party',
399                                             'dlfcn-win32') },
400           'commands': [
401               command.CopyTree('%(src)s', 'src'),
402               command.Command(['i686-w64-mingw32-gcc',
403                                '-o', 'dlfcn.o', '-c',
404                                os.path.join('src', 'dlfcn.c'),
405                                '-Wall', '-O3', '-fomit-frame-pointer']),
406               command.Command([ar, 'cru',
407                                'libdl.a', 'dlfcn.o']),
408               command.Copy('libdl.a',
409                            os.path.join('%(output)s', 'libdl.a')),
410               command.Copy(os.path.join('src', 'dlfcn.h'),
411                            os.path.join('%(output)s', 'dlfcn.h')),
412           ],
413       },
414     })
415   elif not options.gcc:
416     # Libc++ is only tested with the clang build
417     libs.update({
418         H('libcxx'): {
419             'dependencies': ['libcxx_src', 'libcxxabi_src'],
420             'type': 'build',
421             'commands': [
422                 command.SkipForIncrementalCommand([
423                     'cmake', '-G', 'Unix Makefiles'] +
424                      LibCxxHostArchFlags(host) +
425                      ['-DLIBCXX_CXX_ABI=libcxxabi',
426                       '-DLIBCXX_LIBCXXABI_INCLUDE_PATHS=' + command.path.join(
427                           '%(abs_libcxxabi_src)s', 'include'),
428                       '-DLIBCXX_ENABLE_SHARED=ON',
429                       '-DCMAKE_INSTALL_PREFIX=',
430                       '-DCMAKE_INSTALL_NAME_DIR=@executable_path/../lib',
431                       '%(libcxx_src)s']),
432                 command.Command(MakeCommand(host) + ['VERBOSE=1']),
433                 command.Command(MAKE_DESTDIR_CMD + ['VERBOSE=1', 'install']),
434             ],
435         },
436     })
437   return libs
438
439
440 def HostTools(host, options):
441   def H(component_name):
442     # Return a package name for a component name with a host triple.
443     return GSDJoin(component_name, host)
444   # Return the file name with the appropriate suffix for an executable file.
445   def Exe(file):
446     if TripleIsWindows(host):
447       return file + '.exe'
448     else:
449       return file
450   # Binutils still has some warnings when building with clang
451   warning_flags = ['-Wno-extended-offsetof', '-Wno-absolute-value',
452                    '-Wno-unused-function', '-Wno-unused-const-variable',
453                    '-Wno-unneeded-internal-declaration',
454                    '-Wno-unused-private-field', '-Wno-format-security']
455   tools = {
456       H('binutils_pnacl'): {
457           'dependencies': ['binutils_pnacl_src'],
458           'type': 'build',
459           'commands': [
460               command.SkipForIncrementalCommand([
461                   'sh',
462                   '%(binutils_pnacl_src)s/configure'] +
463                   ConfigureBinutilsCommon() +
464                   ConfigureHostArchFlags(host, warning_flags, options) +
465                   ['--target=arm-pc-nacl',
466                   '--program-prefix=le32-nacl-',
467                   '--enable-targets=arm-pc-nacl,i686-pc-nacl,x86_64-pc-nacl,' +
468                   'mipsel-pc-nacl',
469                   '--enable-shared=no',
470                   '--enable-gold=default',
471                   '--enable-ld=no',
472                   '--enable-plugins',
473                   '--without-gas',
474                   '--with-sysroot=/le32-nacl']),
475               command.Command(MakeCommand(host)),
476               command.Command(MAKE_DESTDIR_CMD + ['install-strip'])] +
477               [command.RemoveDirectory(os.path.join('%(output)s', dir))
478                for dir in ('arm-pc-nacl', 'lib', 'lib32')]
479       },
480       H('driver'): {
481         'type': 'build',
482         'output_subdir': 'bin',
483         'inputs': { 'src': PNACL_DRIVER_DIR },
484         'commands': [
485             command.Runnable(
486                 None,
487                 pnacl_commands.InstallDriverScripts,
488                 '%(src)s', '%(output)s',
489                 host_windows=TripleIsWindows(host) or TripleIsCygWin(host),
490                 host_64bit=TripleIsX8664(host))
491         ],
492       },
493   }
494
495   llvm_cmake = {
496       H('llvm'): {
497           'dependencies': ['clang_src', 'llvm_src', 'binutils_pnacl_src'],
498           'type': 'build',
499           'commands': [
500               command.SkipForIncrementalCommand([
501                   'cmake', '-G', 'Ninja'] +
502                   CmakeHostArchFlags(host, options) +
503                   ['-DCMAKE_BUILD_TYPE=RelWithDebInfo',
504                   '-DCMAKE_INSTALL_PREFIX=%(output)s',
505                   '-DCMAKE_INSTALL_RPATH=$ORIGIN/../lib',
506                   '-DLLVM_ENABLE_LIBCXX=ON',
507                   '-DBUILD_SHARED_LIBS=ON',
508                   '-DLLVM_TARGETS_TO_BUILD=X86;ARM;Mips',
509                   '-DLLVM_ENABLE_ASSERTIONS=ON',
510                   '-DLLVM_ENABLE_ZLIB=OFF',
511                   '-DLLVM_BUILD_TESTS=ON',
512                   '-DLLVM_APPEND_VC_REV=ON',
513                   '-DLLVM_BINUTILS_INCDIR=%(abs_binutils_pnacl_src)s/include',
514                   '-DLLVM_EXTERNAL_CLANG_SOURCE_DIR=%(clang_src)s',
515                   '%(llvm_src)s']),
516               command.Command(['ninja', '-v']),
517               command.Command(['ninja', 'install']),
518         ],
519       },
520   }
521   llvm_autoconf = {
522       H('llvm'): {
523           'dependencies': ['clang_src', 'llvm_src', 'binutils_pnacl_src',
524                            'subzero_src'],
525           'type': 'build',
526           'commands': [
527               command.SkipForIncrementalCommand([
528                   'sh',
529                   '%(llvm_src)s/configure'] +
530                   ConfigureHostArchFlags(host, [], options) +
531                   LLVMConfigureAssertionsFlags(options) +
532                   ['--prefix=/',
533                    '--enable-shared',
534                    '--disable-zlib',
535                    '--disable-terminfo',
536                    '--disable-jit',
537                    '--disable-bindings', # ocaml is currently the only binding.
538                    '--with-binutils-include=%(abs_binutils_pnacl_src)s/include',
539                    '--enable-targets=x86,arm,mips',
540                    '--program-prefix=',
541                    '--enable-optimized',
542                    '--with-clang-srcdir=%(abs_clang_src)s'])] +
543               CopyHostLibcxxForLLVMBuild(
544                   host,
545                   os.path.join('Release+Asserts', 'lib'),
546                   options) +
547               [command.Command(MakeCommand(host) + [
548                   'VERBOSE=1',
549                   'NACL_SANDBOX=0',
550                   'SUBZERO_SRC_ROOT=%(abs_subzero_src)s',
551                   'all']),
552               command.Command(MAKE_DESTDIR_CMD + ['install']),
553               command.Remove(*[os.path.join('%(output)s', 'lib', f) for f in
554                               '*.a', '*Hello.*', 'BugpointPasses.*']),
555               command.Remove(*[os.path.join('%(output)s', 'bin', f) for f in
556                                Exe('clang-format'), Exe('clang-check'),
557                                Exe('c-index-test'), Exe('clang-tblgen'),
558                                Exe('llvm-tblgen')])] +
559               [command.Command(['ln', '-f',
560                                 command.path.join('%(output)s', 'bin','clang'),
561                                 command.path.join('%(output)s', 'bin',
562                                                   arch + '-nacl-clang')])
563                for arch in DIRECT_TO_NACL_ARCHES] +
564               [command.Command(['ln', '-f',
565                                 command.path.join('%(output)s', 'bin','clang'),
566                                 command.path.join('%(output)s', 'bin',
567                                                   arch + '-nacl-clang++')])
568                for arch in DIRECT_TO_NACL_ARCHES] +
569               CopyWindowsHostLibs(host),
570       },
571   }
572   if options.cmake:
573     tools.update(llvm_cmake)
574   else:
575     tools.update(llvm_autoconf)
576   if TripleIsWindows(host):
577     tools[H('binutils_pnacl')]['dependencies'].append('libdl')
578     tools[H('llvm')]['dependencies'].append('libdl')
579   elif not options.gcc:
580     tools[H('binutils_pnacl')]['dependencies'].append(H('libcxx'))
581     tools[H('llvm')]['dependencies'].append(H('libcxx'))
582   return tools
583
584
585 def TargetLibCompiler(host, options):
586   def H(component_name):
587     return GSDJoin(component_name, host)
588   compiler = {
589       # Because target_lib_compiler is not a memoized target, its name doesn't
590       # need to have the host appended to it (it can be different on different
591       # hosts), which means that target library build rules don't have to care
592       # what host they run on; they can just depend on 'target_lib_compiler'
593       'target_lib_compiler': {
594           'type': 'work',
595           'output_subdir': 'target_lib_compiler',
596           'dependencies': [ H('binutils_pnacl'), H('llvm'), H('binutils_x86') ],
597           'inputs': { 'driver': PNACL_DRIVER_DIR },
598           'commands': [
599               command.CopyRecursive('%(' + t + ')s', '%(output)s')
600               for t in [H('llvm'), H('binutils_pnacl'), H('binutils_x86')]] + [
601               command.Runnable(
602                   None, pnacl_commands.InstallDriverScripts,
603                   '%(driver)s', os.path.join('%(output)s', 'bin'),
604                   host_windows=TripleIsWindows(host) or TripleIsCygWin(host),
605                   host_64bit=TripleIsX8664(host))
606           ]
607       },
608   }
609
610   if TripleIsWindows(host) or not options.gcc:
611     host_lib = 'libdl' if TripleIsWindows(host) else H('libcxx')
612     compiler['target_lib_compiler']['dependencies'].append(host_lib)
613     compiler['target_lib_compiler']['commands'].append(
614         command.CopyRecursive('%(' + host_lib + ')s', '%(output)s'))
615   return compiler
616
617
618 def Metadata(revisions):
619   data = {
620       'metadata': {
621           'type': 'build',
622           'inputs': { 'readme': os.path.join(NACL_DIR, 'pnacl', 'README'),
623                       'COMPONENT_REVISIONS': GIT_DEPS_FILE,
624                       'driver': PNACL_DRIVER_DIR },
625           'commands': [
626               command.Copy('%(readme)s', os.path.join('%(output)s', 'README')),
627               command.WriteData(str(FEATURE_VERSION),
628                                 os.path.join('%(output)s', 'FEATURE_VERSION')),
629               command.Runnable(None, pnacl_commands.WriteREVFile,
630                                os.path.join('%(output)s', 'REV'),
631                                GIT_BASE_URL,
632                                GIT_REPOS,
633                                revisions),
634           ],
635       }
636   }
637   return data
638
639
640 def HostToolsDirectToNacl(host):
641   def H(component_name):
642     return GSDJoin(component_name, host)
643
644   tools = {}
645
646   if TripleIsWindows(host):
647     redirector_table = ''
648     for tool, args in TOOL_X64_I686_REDIRECTS:
649       redirector_table += '  {L"/bin/i686-nacl-%s.exe",' % tool + \
650                           '   L"/bin/x86_64-nacl-%s.exe",' % tool + \
651                           ' L"%s"},\n' % args
652
653     cc, cxx = CompilersForHost(host)
654     tools.update({
655         'redirector_src': {
656             'type': 'source',
657             'inputs': { 'source_directory': REDIRECTOR_WIN32_SRC },
658             'commands': [
659                 command.CopyTree('%(source_directory)s', '%(output)s'),
660                 command.WriteData(redirector_table,
661                                   os.path.join('%(output)s',
662                                                'redirector_table.txt')),
663             ],
664         },
665         'redirector': {
666             'type': 'build',
667             'dependencies': ['redirector_src'],
668             'commands': [
669                 command.Mkdir('%(output)s', parents=True),
670                 command.Command([cc, '-O3', '-std=c99',
671                                  '-I',
672                                  os.path.join(NACL_DIR, '..'),
673                                  '-o',
674                                  os.path.join('%(output)s', 'redirector.exe'),
675                                  os.path.join('%(redirector_src)s',
676                                               'redirector.c')]),
677             ],
678         },
679     })
680
681     redirect_deps = ['redirector']
682     redirect_inputs = {}
683     redirect_cmds = [
684         command.Command([
685             'ln', '-f',
686             command.path.join('%(redirector)s', 'redirector.exe'),
687             command.path.join('%(output)s', 'bin', 'i686-nacl-%s.exe' % tool)])
688         for tool, args in TOOL_X64_I686_REDIRECTS]
689   else:
690     redirect_deps = []
691     redirect_inputs = { 'redirector_script': REDIRECTOR_SCRIPT }
692     redirect_cmds = [
693         command.Command([
694             '%(abs_redirector_script)s',
695             command.path.join('%(output)s', 'bin', 'i686-nacl-' + tool),
696             'x86_64-nacl-' + tool,
697             args])
698         for tool, args in TOOL_X64_I686_REDIRECTS]
699
700   tools.update({
701       H('binutils_x86'): {
702           'type': 'build',
703           'dependencies': ['binutils_x86_src'] + redirect_deps,
704           'inputs': redirect_inputs,
705           'commands': [
706               command.SkipForIncrementalCommand(
707                   ['sh', '%(binutils_x86_src)s/configure'] +
708                   ConfigureBinutilsCommon() +
709                   ['--target=x86_64-nacl',
710                    '--enable-targets=x86_64-nacl,i686-nacl',
711                    '--disable-werror']),
712               command.Command(MakeCommand(host)),
713               command.Command(MAKE_DESTDIR_CMD + ['install-strip'])] +
714               # Remove the share dir from this binutils build and leave the one
715               # from the newer version used for bitcode linking. Always remove
716               # the lib dirs, which have unneeded host libs.
717               [command.RemoveDirectory(os.path.join('%(output)s', dir))
718                for dir in ('lib', 'lib32', 'lib64', 'share')] +
719               # Create the set of directories for target libs and includes, for
720               # experimentation before we actually build them.
721               # Libc includes (libs dir is created by binutils)
722               # TODO(dschuff): remove these when they are populated by target
723               # library packages.
724               [command.Mkdir(command.path.join(
725                   '%(output)s', 'x86_64-nacl', 'include'), parents=True),
726                command.Mkdir(command.path.join(
727                    '%(output)s', 'x86_64-nacl', 'lib32')),
728                command.Command(['ln', '-s', command.path.join('..','lib32'),
729                                command.path.join(
730                                    '%(output)s', 'x86_64-nacl', 'lib', '32')]),
731                command.Command(['ln', '-s', 'lib',
732                                command.path.join(
733                                    '%(output)s', 'x86_64-nacl', 'lib64')])] +
734               # Compiler libs (includes are shared)
735               [command.Mkdir(command.path.join('%(output)s',
736                   'lib', 'clang', '3.4', 'lib', target), parents=True)
737                for target in ['i686-nacl', 'x86_64-nacl']] +
738               # Create links for i686-flavored names of the tools. For now we
739               # don't use the redirector scripts that pass different arguments
740               # because the compiler driver doesn't need them.
741               [command.Command([
742                   'ln', '-f',
743                   command.path.join('%(output)s', 'bin', 'x86_64-nacl-' + tool),
744                   command.path.join('%(output)s', 'bin', 'i686-nacl-' + tool)])
745                for tool in ['addr2line', 'ar', 'nm', 'objcopy', 'objdump',
746                             'ranlib', 'readelf', 'size', 'strings', 'strip']] +
747               redirect_cmds
748       }
749   })
750   return tools
751
752 def ParseComponentRevisionsFile(filename):
753   ''' Parse a simple-format deps file, with fields of the form:
754 key=value
755 Keys should match the keys in GIT_REPOS above, which match the previous
756 directory names used by gclient (with the exception that '_' in the file is
757 replaced by '-' in the returned key name).
758 Values are the git hashes for each repo.
759 Empty lines or lines beginning with '#' are ignored.
760 This function returns a dictionary mapping the keys found in the file to their
761 values.
762 '''
763   with open(filename) as f:
764     deps = {}
765     for line in f:
766       stripped = line.strip()
767       if stripped.startswith('#') or len(stripped) == 0:
768         continue
769       tokens = stripped.split('=')
770       if len(tokens) != 2:
771         raise Exception('Malformed component revisions file: ' + filename)
772       deps[tokens[0].replace('_', '-')] = tokens[1]
773   return deps
774
775
776 def GetSyncPNaClReposSource(revisions, GetGitSyncCmds):
777   sources = {}
778   for repo, revision in revisions.iteritems():
779     sources['legacy_pnacl_%s_src' % repo] = {
780         'type': 'source',
781         'output_dirname': os.path.join(NACL_DIR, 'pnacl', 'git', repo),
782         'commands': GetGitSyncCmds(repo),
783     }
784   return sources
785
786
787 def InstallMinGWHostCompiler():
788   """Install the MinGW host compiler used to build the host tools on Windows.
789
790   We could use an ordinary source rule for this, but that would require hashing
791   hundreds of MB of toolchain files on every build. Instead, check for the
792   presence of the specially-named file <version>.installed in the install
793   directory. If it is absent, check for the presence of the zip file
794   <version>.zip. If it is absent, attempt to download it from Google Storage.
795   Then extract the zip file and create the install file.
796   """
797   if not os.path.isfile(os.path.join(MINGW_PATH, MINGW_VERSION + '.installed')):
798     downloader = pynacl.gsd_storage.GSDStorage([], ['nativeclient-mingw'])
799     zipfilename = MINGW_VERSION + '.zip'
800     zipfilepath = os.path.join(NACL_DIR, zipfilename)
801     # If the zip file is not present, try to download it from Google Storage.
802     # If that fails, bail out.
803     if (not os.path.isfile(zipfilepath) and
804         not downloader.GetSecureFile(zipfilename, zipfilepath)):
805         print >>sys.stderr, 'Failed to install MinGW tools:'
806         print >>sys.stderr, 'could not find or download', zipfilename
807         sys.exit(1)
808     logging.info('Extracting %s' % zipfilename)
809     zf = zipfile.ZipFile(zipfilepath)
810     if os.path.exists(MINGW_PATH):
811       shutil.rmtree(MINGW_PATH)
812     zf.extractall(NACL_DIR)
813     with open(os.path.join(MINGW_PATH, MINGW_VERSION + '.installed'), 'w') as _:
814       pass
815   os.environ['MINGW'] = MINGW_PATH
816
817
818 def GetUploadPackageTargets():
819   """Package Targets describes all the archived package targets.
820
821   This build can be built among many build bots, but eventually all things
822   will be combined together. This package target dictionary describes the final
823   output of the entire build.
824   """
825   package_targets = {}
826
827   common_packages = ['metadata']
828
829   # Target translator libraries
830   for arch in TRANSLATOR_ARCHES:
831     legal_arch = pynacl.gsd_storage.LegalizeName(arch)
832     common_packages.append('libs_support_translator_%s' % legal_arch)
833     common_packages.append('compiler_rt_%s' % legal_arch)
834     if not 'nonsfi' in arch:
835       common_packages.append('libgcc_eh_%s' % legal_arch)
836
837   # Target libraries
838   for bias in BITCODE_BIASES:
839     legal_bias = pynacl.gsd_storage.LegalizeName(bias)
840     common_packages.append('newlib_%s' % legal_bias)
841     common_packages.append('libcxx_%s' % legal_bias)
842     common_packages.append('libstdcxx_%s' % legal_bias)
843     common_packages.append('libs_support_%s' % legal_bias)
844
845   # Direct-to-nacl target libraries
846   for arch in DIRECT_TO_NACL_ARCHES:
847     common_packages.append('newlib_%s' % arch)
848     common_packages.append('libcxx_%s' % arch)
849     common_packages.append('libs_support_%s' % arch)
850
851   # Host components
852   host_packages = {}
853   for os_name, arch in (('win', 'x86-32'),
854                         ('mac', 'x86-64'),
855                         ('linux', 'x86-64')):
856     triple = pynacl.platform.PlatformTriple(os_name, arch)
857     legal_triple = pynacl.gsd_storage.LegalizeName(triple)
858     host_packages.setdefault(os_name, []).extend(
859         ['binutils_pnacl_%s' % legal_triple,
860          'binutils_x86_%s' % legal_triple,
861          'llvm_%s' % legal_triple,
862          'driver_%s' % legal_triple])
863     if os_name != 'win':
864       host_packages[os_name].append('libcxx_%s' % legal_triple)
865
866   # Unsandboxed target IRT libraries
867   for os_name in ('linux', 'mac'):
868     legal_triple = pynacl.gsd_storage.LegalizeName('x86-32-' + os_name)
869     host_packages[os_name].append('unsandboxed_irt_%s' % legal_triple)
870
871   for os_name, os_packages in host_packages.iteritems():
872     package_target = '%s_x86' % pynacl.platform.GetOS(os_name)
873     package_targets[package_target] = {}
874     package_name = 'pnacl_newlib'
875     combined_packages = os_packages + common_packages
876     package_targets[package_target][package_name] = combined_packages
877
878   return package_targets
879
880 if __name__ == '__main__':
881   # This sets the logging for gclient-alike repo sync. It will be overridden
882   # by the package builder based on the command-line flags.
883   logging.getLogger().setLevel(logging.DEBUG)
884   parser = argparse.ArgumentParser(add_help=False)
885   parser.add_argument('--legacy-repo-sync', action='store_true',
886                       dest='legacy_repo_sync', default=False,
887                       help='Sync the git repo directories used by build.sh')
888   parser.add_argument('--disable-llvm-assertions', action='store_false',
889                       dest='enable_llvm_assertions', default=True)
890   parser.add_argument('--cmake', action='store_true', default=False,
891                       help="Use LLVM's cmake ninja build instead of autoconf")
892   parser.add_argument('--gcc', action='store_true', default=False,
893                       help="Use the default compiler 'cc' instead of clang")
894   parser.add_argument('--sanitize', choices=['address', 'thread', 'memory',
895                                              'undefined'],
896                       help="Use a sanitizer with LLVM's clang cmake build")
897   parser.add_argument('--testsuite-sync', action='store_true', default=False,
898                       help=('Sync the sources for the LLVM testsuite. '
899                       'Only useful if --sync/ is also enabled'))
900   args, leftover_args = parser.parse_known_args()
901   if '-h' in leftover_args or '--help' in leftover_args:
902     print 'The following arguments are specific to toolchain_build_pnacl.py:'
903     parser.print_help()
904     print 'The rest of the arguments are generic, in toolchain_main.py'
905
906   if args.sanitize and not args.cmake:
907     print 'Use of sanitizers requires a cmake build'
908     sys.exit(1)
909
910   if args.gcc and args.cmake:
911     print 'gcc build is not supported with cmake'
912     sys.exit(1)
913
914   packages = {}
915   upload_packages = {}
916
917   rev = ParseComponentRevisionsFile(GIT_DEPS_FILE)
918   if args.legacy_repo_sync:
919     packages = GetSyncPNaClReposSource(rev, GetGitSyncCmdsCallback(rev))
920
921     # Make sure sync is inside of the args to toolchain_main.
922     if not set(['-y', '--sync', '--sync-only']).intersection(leftover_args):
923       leftover_args.append('--sync-only')
924   else:
925     upload_packages = GetUploadPackageTargets()
926     if pynacl.platform.IsWindows():
927       InstallMinGWHostCompiler()
928
929     packages.update(HostToolsSources(GetGitSyncCmdsCallback(rev)))
930     if args.testsuite_sync:
931       packages.update(TestsuiteSources(GetGitSyncCmdsCallback(rev)))
932
933     hosts = [pynacl.platform.PlatformTriple()]
934     if pynacl.platform.IsLinux() and BUILD_CROSS_MINGW:
935       hosts.append(pynacl.platform.PlatformTriple('win', 'x86-32'))
936     for host in hosts:
937       packages.update(HostLibs(host, args))
938       packages.update(HostTools(host, args))
939       packages.update(HostToolsDirectToNacl(host))
940     packages.update(TargetLibCompiler(pynacl.platform.PlatformTriple(), args))
941     # Don't build the target libs on Windows because of pathname issues.
942     # Only the linux64 bot is canonical (i.e. it will upload its packages).
943     # The other bots will use a 'work' target instead of a 'build' target for
944     # the target libs, so they will not be memoized, but can be used for tests.
945     # TODO(dschuff): Even better would be if we could memoize non-canonical
946     # build targets without doing things like mangling their names (and for e.g.
947     # scons tests, skip running them if their dependencies haven't changed, like
948     # build targets)
949     is_canonical = pynacl.platform.IsLinux64()
950     if pynacl.platform.IsLinux() or pynacl.platform.IsMac():
951       packages.update(pnacl_targetlibs.TargetLibsSrc(
952         GetGitSyncCmdsCallback(rev)))
953       for bias in BITCODE_BIASES:
954         packages.update(pnacl_targetlibs.TargetLibs(bias, is_canonical))
955       for arch in DIRECT_TO_NACL_ARCHES:
956         packages.update(pnacl_targetlibs.TargetLibs(arch, is_canonical))
957       for arch in TRANSLATOR_ARCHES:
958         packages.update(pnacl_targetlibs.TranslatorLibs(arch, is_canonical))
959       packages.update(Metadata(rev))
960     if pynacl.platform.IsLinux() or pynacl.platform.IsMac():
961       packages.update(pnacl_targetlibs.UnsandboxedIRT(
962           'x86-32-%s' % pynacl.platform.GetOS()))
963
964
965   tb = toolchain_main.PackageBuilder(packages,
966                                      upload_packages,
967                                      leftover_args)
968   tb.Main()