#!/usr/bin/python
# Copyright (c) 2013 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Recipes for PNaCl target libs."""

import fnmatch
import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import pynacl.gsd_storage
import pynacl.platform

import command
import pnacl_commands

from toolchain_build import NewlibLibcScript

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
NACL_DIR = os.path.dirname(SCRIPT_DIR)

CLANG_VER = '3.5.0'

# Return the path to a tool to build target libraries
# msys should be false if the path will be called directly rather than passed to
# an msys or cygwin tool such as sh or make.
def PnaclTool(toolname, arch='le32', msys=True):
  if not msys and pynacl.platform.IsWindows():
    ext = '.bat'
  else:
    ext = ''
  if IsBCArch(arch):
    base = 'pnacl-' + toolname
  else:
    base = '-'.join([TargetArch(arch), 'nacl', toolname])
  return command.path.join('%(abs_target_lib_compiler)s',
                           'bin', base + ext)

# PNaCl tools for newlib's environment, e.g. CC_FOR_TARGET=/path/to/pnacl-clang
TOOL_ENV_NAMES = { 'CC': 'clang', 'CXX': 'clang++', 'AR': 'ar', 'NM': 'nm',
                   'RANLIB': 'ranlib', 'READELF': 'readelf',
                   'OBJDUMP': 'illegal', 'AS': 'as', 'LD': 'illegal',
                   'STRIP': 'illegal' }

def TargetTools(arch):
  return [ tool + '_FOR_TARGET=' + PnaclTool(name, arch=arch, msys=True)
           for tool, name in TOOL_ENV_NAMES.iteritems() ]


def MakeCommand():
  make_command = ['make']
  if not pynacl.platform.IsWindows():
    # The make that ships with msys sometimes hangs when run with -j.
    # The ming32-make that comes with the compiler itself reportedly doesn't
    # have this problem, but it has issues with pathnames with LLVM's build.
    make_command.append('-j%(cores)s')
  return make_command

# Return the component name to use for a component name with
# a host triple. GNU configuration triples contain dashes, which are converted
# to underscores so the names are legal for Google Storage.
def GSDJoin(*args):
  return '_'.join([pynacl.gsd_storage.LegalizeName(arg) for arg in args])


def TripleFromArch(bias_arch):
  return bias_arch + '-nacl'


def IsBiasedBCArch(arch):
  return arch.endswith('_bc')


def IsBCArch(arch):
  return IsBiasedBCArch(arch) or arch == 'le32'


# Return the target arch recognized by the compiler (e.g. i686) from an arch
# string which might be a biased-bitcode-style arch (e.g. i686_bc)
def TargetArch(bias_arch):
  if IsBiasedBCArch(bias_arch):
    return bias_arch[:bias_arch.index('_bc')]
  return bias_arch


def MultilibArch(bias_arch):
  return 'x86_64' if bias_arch == 'i686' else bias_arch


def MultilibLibDir(bias_arch):
  suffix = '32' if bias_arch == 'i686' else ''
  return os.path.join(TripleFromArch(MultilibArch(bias_arch)), 'lib' + suffix)


def BiasedBitcodeTargetFlag(bias_arch):
  arch = TargetArch(bias_arch)
  flagmap = {
      # Arch     Target                           Extra flags.
      'x86_64': ('x86_64-unknown-nacl',           []),
      'i686':   ('i686-unknown-nacl',             []),
      'arm':    ('armv7-unknown-nacl-gnueabihf',  ['-mfloat-abi=hard']),
      # MIPS doesn't use biased bitcode:
      'mips32': ('le32-unknown-nacl',             []),
  }
  return ['--target=%s' % flagmap[arch][0]] + flagmap[arch][1]


def TargetLibCflags(bias_arch):
  flags = '-g -O2'
  if IsBCArch(bias_arch):
    flags += ' -mllvm -inline-threshold=5 -allow-asm'
  if IsBiasedBCArch(bias_arch):
    flags += ' ' + ' '.join(BiasedBitcodeTargetFlag(bias_arch))
  return flags


def NewlibIsystemCflags(bias_arch):
  include_arch = MultilibArch(bias_arch)
  return ' '.join([
    '-isystem',
    command.path.join('%(' + GSDJoin('abs_newlib', include_arch) +')s',
                      TripleFromArch(include_arch), 'include')])

def LibCxxCflags(bias_arch):
  # HAS_THREAD_LOCAL is used by libc++abi's exception storage, the fallback is
  # pthread otherwise.
  return ' '.join([TargetLibCflags(bias_arch), NewlibIsystemCflags(bias_arch),
                   '-DHAS_THREAD_LOCAL=1', '-D__ARM_DWARF_EH__'])


def LibStdcxxCflags(bias_arch):
  return ' '.join([TargetLibCflags(bias_arch),
                   NewlibIsystemCflags(bias_arch)])


# Build a single object file for the target.
def BuildTargetObjectCmd(source, output, bias_arch, output_dir='%(cwd)s'):
  flags = ['-Wall', '-Werror', '-O2', '-c']
  if IsBiasedBCArch(bias_arch):
    flags.extend(BiasedBitcodeTargetFlag(bias_arch))
  flags.extend(NewlibIsystemCflags(bias_arch).split())
  return command.Command(
      [PnaclTool('clang', arch=bias_arch, msys=False)] + flags + [
          command.path.join('%(src)s', source),
     '-o', command.path.join(output_dir, output)])


# Build a single object file as native code for the translator.
def BuildTargetTranslatorCmd(sourcefile, output, arch, extra_flags=[],
                             source_dir='%(src)s', output_dir='%(cwd)s'):
  return command.Command(
    [PnaclTool('clang', msys=False),
     '--pnacl-allow-native', '--pnacl-allow-translate', '-Wall', '-Werror',
     '-arch', arch, '--pnacl-bias=' + arch, '-O3',
     # TODO(dschuff): this include breaks the input encapsulation for build
     # rules.
     '-I%(top_srcdir)s/..','-c'] +
    NewlibIsystemCflags('le32').split() +
    extra_flags +
    [command.path.join(source_dir, sourcefile),
     '-o', command.path.join(output_dir, output)])


def BuildLibgccEhCmd(sourcefile, output, arch):
  # Return a command to compile a file from libgcc_eh (see comments in at the
  # rule definition below).
  flags_common = ['-DENABLE_RUNTIME_CHECKING', '-g', '-O2', '-W', '-Wall',
                  '-Wwrite-strings', '-Wcast-qual', '-Wstrict-prototypes',
                  '-Wmissing-prototypes', '-Wold-style-definition',
                  '-DIN_GCC', '-DCROSS_DIRECTORY_STRUCTURE', '-DIN_LIBGCC2',
                  '-D__GCC_FLOAT_NOT_NEEDED', '-Dinhibit_libc',
                  '-DHAVE_CC_TLS', '-DHIDE_EXPORTS',
                  '-fno-stack-protector', '-fexceptions',
                  '-fvisibility=hidden',
                  '-I.', '-I../.././gcc', '-I%(abs_gcc_src)s/gcc/libgcc',
                  '-I%(abs_gcc_src)s/gcc', '-I%(abs_gcc_src)s/include',
                  '-isystem', './include']
  # For x86 we use nacl-gcc to build libgcc_eh because of some issues with
  # LLVM's handling of the gcc intrinsics used in the library. See
  # https://code.google.com/p/nativeclient/issues/detail?id=1933
  # and http://llvm.org/bugs/show_bug.cgi?id=8541
  # For ARM, LLVM does work and we use it to avoid dealing with the fact that
  # arm-nacl-gcc uses different libgcc support functions than PNaCl.
  if arch in ('arm', 'mips32'):
    cc = PnaclTool('clang', msys=False)
    flags_naclcc = ['-arch', arch, '--pnacl-bias=' + arch,
                    '--pnacl-allow-translate', '--pnacl-allow-native']
  else:
    os_name = pynacl.platform.GetOS()
    arch_name = pynacl.platform.GetArch()
    platform_dir = '%s_%s' % (os_name, arch_name)
    newlib_dir = 'nacl_x86_newlib_raw'

    nnacl_dir = os.path.join(NACL_DIR, 'toolchain', platform_dir,
                             newlib_dir, 'bin')
    gcc_binaries = {
        'x86-32': 'i686-nacl-gcc',
        'x86-64': 'x86_64-nacl-gcc',
    }

    cc = os.path.join(nnacl_dir, gcc_binaries[arch])
    flags_naclcc = []
  return command.Command([cc] + flags_naclcc + flags_common +
                         ['-c',
                          command.path.join('%(gcc_src)s', 'gcc', sourcefile),
                          '-o', output])



def TargetLibsSrc(GitSyncCmds):
  newlib_sys_nacl = command.path.join('%(output)s',
                                      'newlib', 'libc', 'sys', 'nacl')
  source = {
      'newlib_src': {
          'type': 'source',
          'output_dirname': 'pnacl-newlib',
          'commands': [
              # Clean any headers exported from the NaCl tree before syncing.
              command.CleanGitWorkingDir(
                  '%(output)s',
                  reset=False,
                  path=os.path.join('newlib', 'libc', 'include'))] +
              GitSyncCmds('nacl-newlib') +
              # Remove newlib versions of headers that will be replaced by
              # headers from the NaCl tree.
              [command.RemoveDirectory(command.path.join(newlib_sys_nacl,
                                                         dirname))
               for dirname in ['bits', 'sys', 'machine']] + [
              command.Command([
                  sys.executable,
                  command.path.join('%(top_srcdir)s', 'src', 'trusted',
                                    'service_runtime', 'export_header.py'),
                  command.path.join('%(top_srcdir)s', 'src', 'trusted',
                                    'service_runtime', 'include'),
                  newlib_sys_nacl],
                  cwd='%(abs_output)s',
              )] + [
              command.Copy(
                  os.path.join('%(top_srcdir)s', 'src', 'untrusted', 'pthread',
                               header),
                  os.path.join('%(output)s', 'newlib', 'libc', 'include',
                               header))
              for header in ('pthread.h', 'semaphore.h')
       ]
      },
      'compiler_rt_src': {
          'type': 'source',
          'output_dirname': 'compiler-rt',
          'commands': GitSyncCmds('compiler-rt'),
      },
      'gcc_src': {
          'type': 'source',
          'output_dirname': 'pnacl-gcc',
          'commands': GitSyncCmds('gcc'),
      },
  }
  return source


def NewlibDirectoryCmds(bias_arch, newlib_triple):
  commands = []
  def NewlibLib(name):
    return os.path.join('%(output)s', newlib_triple, 'lib', name)
  if not IsBCArch(bias_arch):
    commands.extend([
      command.Rename(NewlibLib('libc.a'), NewlibLib('libcrt_common.a')),
      command.WriteData(NewlibLibcScript(bias_arch, 'elf64'),
                        NewlibLib('libc.a'))])
  target_triple = TripleFromArch(bias_arch)
  if bias_arch != 'i686':
    commands.extend([
        # For biased bitcode builds, we configured newlib with target=le32-nacl
        # to get its pure C implementation, so rename its output dir (which
        # matches the target to the output dir for the package we are building)
        command.Rename(os.path.join('%(output)s', newlib_triple),
                       os.path.join('%(output)s', target_triple)),
        # Copy nacl_random.h, used by libc++. It uses the IRT, so should
        # be safe to include in the toolchain.
        command.Mkdir(
            os.path.join('%(output)s', target_triple, 'include', 'nacl')),
        command.Copy(os.path.join('%(top_srcdir)s', 'src', 'untrusted',
                                  'nacl', 'nacl_random.h'),
                     os.path.join('%(output)s', target_triple, 'include',
                                  'nacl', 'nacl_random.h'))
    ])
  else:
    # Use multilib-style directories for i686
    multilib_triple = TripleFromArch(MultilibArch(bias_arch))
    commands.extend([
        command.Rename(os.path.join('%(output)s', newlib_triple),
                       os.path.join('%(output)s', multilib_triple)),
        command.RemoveDirectory(
            os.path.join('%(output)s', multilib_triple, 'include')),
        command.Rename(
            os.path.join('%(output)s', multilib_triple, 'lib'),
            os.path.join('%(output)s', multilib_triple, 'lib32')),
    ])
  # Remove the 'share' directory from the biased builds; the data is
  # duplicated exactly and takes up 2MB per package.
  if bias_arch != 'le32':
    commands.append(command.RemoveDirectory(os.path.join('%(output)s','share')))
  return commands


def LibcxxDirectoryCmds(bias_arch):
  if bias_arch != 'i686':
    return []
  lib_dir = os.path.join('%(output)s', MultilibLibDir(bias_arch))
  return [
      # Use the multlib-style lib dir and shared headers for i686
      command.Mkdir(os.path.dirname(lib_dir)),
      command.Rename(os.path.join('%(output)s', 'i686-nacl', 'lib'),
                     lib_dir),
      # The only thing left in i686-nacl is the headers
      command.RemoveDirectory(os.path.join('%(output)s', 'i686-nacl')),
  ]


def TargetLibs(bias_arch, is_canonical):
  def T(component_name):
    return GSDJoin(component_name, bias_arch)
  target_triple = TripleFromArch(bias_arch)
  newlib_triple = target_triple if not IsBCArch(bias_arch) else 'le32-nacl'
  clang_libdir = os.path.join(
      '%(output)s', 'lib', 'clang', CLANG_VER, 'lib', target_triple)
  libc_libdir = os.path.join('%(output)s', MultilibLibDir(bias_arch))
  libs = {
      T('newlib'): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': [ 'newlib_src', 'target_lib_compiler'],
          'commands' : [
              command.SkipForIncrementalCommand(
                  ['sh', '%(newlib_src)s/configure'] +
                  TargetTools(bias_arch) +
                  ['CFLAGS_FOR_TARGET=' + TargetLibCflags(bias_arch),
                  '--prefix=',
                  '--disable-newlib-supplied-syscalls',
                  '--disable-texinfo',
                  '--disable-libgloss',
                  '--enable-newlib-iconv',
                  '--enable-newlib-iconv-from-encodings=' +
                  'UTF-8,UTF-16LE,UCS-4LE,UTF-16,UCS-4',
                  '--enable-newlib-iconv-to-encodings=' +
                  'UTF-8,UTF-16LE,UCS-4LE,UTF-16,UCS-4',
                  '--enable-newlib-io-long-long',
                  '--enable-newlib-io-long-double',
                  '--enable-newlib-io-c99-formats',
                  '--enable-newlib-mb',
                  '--target=' + newlib_triple
              ]),
              command.Command(MakeCommand()),
              command.Command(['make', 'DESTDIR=%(abs_output)s', 'install']),
          ] + NewlibDirectoryCmds(bias_arch, newlib_triple)
      },
      T('libcxx'): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': ['libcxx_src', 'libcxxabi_src', 'llvm_src', 'gcc_src',
                           'target_lib_compiler', T('newlib'),
                           GSDJoin('newlib', MultilibArch(bias_arch)),
                           T('libs_support')],
          'commands' :
              [command.SkipForIncrementalCommand(
                  ['cmake', '-G', 'Unix Makefiles',
                   '-DCMAKE_C_COMPILER_WORKS=1',
                   '-DCMAKE_CXX_COMPILER_WORKS=1',
                   '-DCMAKE_INSTALL_PREFIX=',
                   '-DCMAKE_BUILD_TYPE=Release',
                   '-DCMAKE_C_COMPILER=' + PnaclTool('clang', bias_arch),
                   '-DCMAKE_CXX_COMPILER=' + PnaclTool('clang++', bias_arch),
                   '-DCMAKE_SYSTEM_NAME=nacl',
                   '-DCMAKE_AR=' + PnaclTool('ar', bias_arch),
                   '-DCMAKE_NM=' + PnaclTool('nm', bias_arch),
                   '-DCMAKE_RANLIB=' + PnaclTool('ranlib', bias_arch),
                   '-DCMAKE_LD=' + PnaclTool('illegal', bias_arch),
                   '-DCMAKE_AS=' + PnaclTool('as', bias_arch),
                   '-DCMAKE_OBJDUMP=' + PnaclTool('illegal', bias_arch),
                   '-DCMAKE_C_FLAGS=-std=gnu11 ' + LibCxxCflags(bias_arch),
                   '-DCMAKE_CXX_FLAGS=-std=gnu++11 ' + LibCxxCflags(bias_arch),
                   '-DLIT_EXECUTABLE=' + command.path.join(
                       '%(llvm_src)s', 'utils', 'lit', 'lit.py'),
                   # The lit flags are used by the libcxx testsuite, which is
                   # currenty driven by an external script.
                   '-DLLVM_LIT_ARGS=--verbose  --param shell_prefix="' +
                    os.path.join(NACL_DIR,'run.py') +' -arch env --retries=1" '+
                    '--param exe_suffix=".pexe" --param use_system_lib=true ' +
                    '--param cxx_under_test="' + os.path.join(NACL_DIR,
                        'toolchain/linux_x86/pnacl_newlib',
                        'bin/pnacl-clang++') +
                    '" '+
                    '--param link_flags="-std=gnu++11 --pnacl-exceptions=sjlj"',
                   '-DLIBCXX_ENABLE_CXX0X=0',
                   '-DLIBCXX_ENABLE_SHARED=0',
                   '-DLIBCXX_CXX_ABI=libcxxabi',
                   '-DLIBCXX_LIBCXXABI_INCLUDE_PATHS=' + command.path.join(
                       '%(abs_libcxxabi_src)s', 'include'),
                   '%(libcxx_src)s']),
              command.Copy(os.path.join('%(gcc_src)s', 'gcc',
                                        'unwind-generic.h'),
                           os.path.join('include', 'unwind.h')),
              command.Command(MakeCommand() + ['VERBOSE=1']),
              command.Command([
                  'make',
                  'DESTDIR=' + os.path.join('%(abs_output)s', target_triple),
                  'VERBOSE=1',
                  'install']),
          ] + LibcxxDirectoryCmds(bias_arch)
      },
      T('libstdcxx'): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': ['gcc_src', 'gcc_src', 'target_lib_compiler',
                           T('newlib')],
          'commands' : [
              command.SkipForIncrementalCommand([
                  'sh',
                  command.path.join('%(gcc_src)s', 'libstdc++-v3',
                                    'configure')] +
                  TargetTools(bias_arch) + [
                  'CC_FOR_BUILD=cc',
                  'CC=' + PnaclTool('clang'),
                  'CXX=' + PnaclTool('clang++'),
                  'AR=' + PnaclTool('ar'),
                  'NM=' + PnaclTool('nm'),
                  'RAW_CXX_FOR_TARGET=' + PnaclTool('clang++'),
                  'LD=' + PnaclTool('illegal'),
                  'RANLIB=' + PnaclTool('ranlib'),
                  'CFLAGS=' + LibStdcxxCflags(bias_arch),
                  'CXXFLAGS=' + LibStdcxxCflags(bias_arch),
                  'CPPFLAGS=' + NewlibIsystemCflags(bias_arch),
                  'CFLAGS_FOR_TARGET=' + LibStdcxxCflags(bias_arch),
                  'CXXFLAGS_FOR_TARGET=' + LibStdcxxCflags(bias_arch),
                  '--host=arm-none-linux-gnueabi',
                  '--prefix=',
                  '--enable-cxx-flags=-D__SIZE_MAX__=4294967295',
                  '--disable-multilib',
                  '--disable-linux-futex',
                  '--disable-libstdcxx-time',
                  '--disable-sjlj-exceptions',
                  '--disable-libstdcxx-pch',
                  '--with-newlib',
                  '--disable-shared',
                  '--disable-rpath']),
              command.Copy(os.path.join('%(gcc_src)s', 'gcc',
                                        'unwind-generic.h'),
                           os.path.join('include', 'unwind.h')),
              command.Command(MakeCommand()),
              command.Command([
                  'make',
                  'DESTDIR=' + os.path.join('%(abs_output)s', target_triple),
                  'install-data']),
              command.RemoveDirectory(
                  os.path.join('%(output)s', target_triple, 'share')),
              command.Remove(os.path.join('%(output)s', target_triple, 'lib',
                                          'libstdc++*-gdb.py')),
              command.Copy(
                  os.path.join('src', '.libs', 'libstdc++.a'),
                  os.path.join('%(output)s', target_triple, 'lib',
                               'libstdc++.a')),
          ],
      },
  }
  if IsBCArch(bias_arch):
    libs.update({
      T('libs_support'): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': [ T('newlib'), 'target_lib_compiler'],
          'inputs': { 'src': os.path.join(NACL_DIR,
                                          'pnacl', 'support', 'bitcode')},
          'commands': [
              command.Mkdir(clang_libdir, parents=True),
              command.Mkdir(libc_libdir, parents=True),
              # Two versions of crt1.x exist, for different scenarios (with and
              # without EH).  See:
              # https://code.google.com/p/nativeclient/issues/detail?id=3069
              command.Copy(command.path.join('%(src)s', 'crt1.x'),
                           command.path.join(libc_libdir, 'crt1.x')),
              command.Copy(command.path.join('%(src)s', 'crt1_for_eh.x'),
                           command.path.join(libc_libdir, 'crt1_for_eh.x')),
              # Install crti.bc (empty _init/_fini)
              BuildTargetObjectCmd('crti.c', 'crti.bc', bias_arch,
                                    output_dir=libc_libdir),
              # Install crtbegin bitcode (__cxa_finalize for C++)
              BuildTargetObjectCmd('crtbegin.c', 'crtbegin.bc', bias_arch,
                                    output_dir=clang_libdir),
              # Stubs for _Unwind_* functions when libgcc_eh is not included in
              # the native link).
              BuildTargetObjectCmd('unwind_stubs.c', 'unwind_stubs.bc',
                                    bias_arch, output_dir=clang_libdir),
              BuildTargetObjectCmd('sjlj_eh_redirect.cc',
                                    'sjlj_eh_redirect.bc', bias_arch,
                                    output_dir=clang_libdir),
              # libpnaclmm.a (__atomic_* library functions).
              BuildTargetObjectCmd('pnaclmm.c', 'pnaclmm.bc', bias_arch),
              command.Command([
                  PnaclTool('ar'), 'rc',
                  command.path.join(clang_libdir, 'libpnaclmm.a'),
                  'pnaclmm.bc']),
          ]
      }
    })
  else:
    # For now most of the D2N support libs currently come from our native
    # translator libs (libgcc_eh, compiler_rt, crtbegin/end). crti/crtn and crt1
    # come from libnacl, built by scons/gyp.  TODO(dschuff): Do proper support
    # libs for D2N which also don't use .init/.fini

    # Translate from bias_arch's triple-style (i686) names to the translator's
    # style (x86-32). We don't change the translator's naming scheme to avoid
    # churning the in-browser translator.
    def TL(lib):
      return GSDJoin(lib, pynacl.platform.GetArch3264(bias_arch))
    def TranslatorFile(lib, filename):
      return os.path.join('%(' + TL(lib) + ')s', filename)
    libs.update({
      T('libs_support'): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': [ GSDJoin('newlib', MultilibArch(bias_arch)),
                            TL('compiler_rt'), TL('libgcc_eh'),
                            'target_lib_compiler'],
          'inputs': { 'src': os.path.join(NACL_DIR, 'pnacl', 'support')},
          'commands': [
              command.Mkdir(clang_libdir, parents=True),
              command.Copy(
                  TranslatorFile('compiler_rt', 'libgcc.a'),
                  os.path.join('%(output)s', clang_libdir, 'libgcc.a')),
              command.Copy(
                  TranslatorFile('libgcc_eh', 'libgcc_eh.a'),
                  os.path.join('%(output)s', clang_libdir, 'libgcc_eh.a')),
              BuildTargetObjectCmd('clang_direct/crtbegin.c', 'crtbeginT.o',
                                   bias_arch, output_dir=clang_libdir),
              BuildTargetObjectCmd('crtend.c', 'crtend.o',
                                   bias_arch, output_dir=clang_libdir),
          ],
      }
    })
    # We do not plan to support libstdcxx for direct-to-nacl since we are close
    # to being able to remove it from PNaCl.
    del libs[T('libstdcxx')]

  return libs


def TranslatorLibs(arch, is_canonical):
  setjmp_arch = arch
  if setjmp_arch.endswith('-nonsfi'):
    setjmp_arch = setjmp_arch[:-len('-nonsfi')]

  arch_cmds = []
  if arch == 'arm':
    arch_cmds.append(
        BuildTargetTranslatorCmd('aeabi_read_tp.S', 'aeabi_read_tp.o', arch))
  elif arch == 'x86-32-nonsfi':
    arch_cmds.extend(
        [BuildTargetTranslatorCmd('entry_linux.c', 'entry_linux.o', arch),
         BuildTargetTranslatorCmd('entry_linux_x86_32.S', 'entry_linux_asm.o',
                                  arch)])
  elif arch == 'arm-nonsfi':
    arch_cmds.extend(
        [BuildTargetTranslatorCmd('entry_linux.c', 'entry_linux.o', arch),
         BuildTargetTranslatorCmd('entry_linux_arm.S', 'entry_linux_asm.o',
                                  arch)])
  translator_lib_dir = os.path.join('translator', arch, 'lib')

  libs = {
      GSDJoin('libs_support_translator', arch): {
          'type': 'build' if is_canonical else 'work',
          'output_subdir': translator_lib_dir,
          'dependencies': [ 'newlib_src', 'newlib_le32',
                            'target_lib_compiler'],
          # These libs include
          # arbitrary stuff from native_client/src/{include,untrusted,trusted}
          'inputs': { 'src': os.path.join(NACL_DIR, 'pnacl', 'support'),
                      'include': os.path.join(NACL_DIR, 'src'),
                      'newlib_subset': os.path.join(
                          NACL_DIR, 'src', 'third_party',
                          'pnacl_native_newlib_subset')},
          'commands': [
              BuildTargetTranslatorCmd('crtbegin.c', 'crtbegin.o', arch,
                                       output_dir='%(output)s'),
              BuildTargetTranslatorCmd('crtbegin.c', 'crtbegin_for_eh.o', arch,
                                       ['-DLINKING_WITH_LIBGCC_EH'],
                                       output_dir='%(output)s'),
              BuildTargetTranslatorCmd('crtend.c', 'crtend.o', arch,
                                       output_dir='%(output)s'),
              # libcrt_platform.a
              BuildTargetTranslatorCmd('pnacl_irt.c', 'pnacl_irt.o', arch),
              BuildTargetTranslatorCmd('relocate.c', 'relocate.o', arch),
              BuildTargetTranslatorCmd(
                  'setjmp_%s.S' % setjmp_arch.replace('-', '_'),
                  'setjmp.o', arch),
              BuildTargetTranslatorCmd('string.c', 'string.o', arch,
                                       ['-std=c99'],
                                       source_dir='%(newlib_subset)s'),
              # Pull in non-errno __ieee754_fmod from newlib and rename it to
              # fmod. This is to support the LLVM frem instruction.
              BuildTargetTranslatorCmd(
                  'e_fmod.c', 'e_fmod.o', arch,
                  ['-std=c99', '-I%(abs_newlib_src)s/newlib/libm/common/',
                   '-D__ieee754_fmod=fmod'],
                  source_dir='%(abs_newlib_src)s/newlib/libm/math'),
              BuildTargetTranslatorCmd(
                  'ef_fmod.c', 'ef_fmod.o', arch,
                  ['-std=c99', '-I%(abs_newlib_src)s/newlib/libm/common/',
                   '-D__ieee754_fmodf=fmodf'],
                  source_dir='%(abs_newlib_src)s/newlib/libm/math')] +
              arch_cmds + [
              command.Command(' '.join([
                  PnaclTool('ar'), 'rc',
                  command.path.join('%(output)s', 'libcrt_platform.a'),
                  '*.o']), shell=True),
              # Dummy IRT shim
              BuildTargetTranslatorCmd(
                  'dummy_shim_entry.c', 'dummy_shim_entry.o', arch),
              command.Command([PnaclTool('ar'), 'rc',
                               command.path.join('%(output)s',
                                                 'libpnacl_irt_shim_dummy.a'),
                               'dummy_shim_entry.o']),
          ],
      },
      GSDJoin('compiler_rt', arch): {
          'type': 'build' if is_canonical else 'work',
          'output_subdir': translator_lib_dir,
          'dependencies': ['compiler_rt_src', 'target_lib_compiler',
                           'newlib_le32'],
          'commands': [
              command.Command(MakeCommand() + [
                  '-f',
                  command.path.join('%(compiler_rt_src)s', 'lib',
                                    'Makefile-pnacl'),
                  'libgcc.a', 'CC=' + PnaclTool('clang'),
                  'AR=' + PnaclTool('ar')] +
                  ['SRC_DIR=' + command.path.join('%(abs_compiler_rt_src)s',
                                                  'lib'),
                   'CFLAGS=-arch ' + arch + ' -DPNACL_' +
                    arch.replace('-', '_') + ' --pnacl-allow-translate -O3 ' +
                   NewlibIsystemCflags('le32')]),
              command.Copy('libgcc.a', os.path.join('%(output)s', 'libgcc.a')),
          ],
      },
  }
  if not arch.endswith('-nonsfi'):
    libs.update({
      GSDJoin('libgcc_eh', arch): {
          'type': 'build' if is_canonical else 'work',
          'output_subdir': translator_lib_dir,
          'dependencies': [ 'gcc_src', 'target_lib_compiler'],
          'inputs': { 'scripts': os.path.join(NACL_DIR, 'pnacl', 'scripts')},
          'commands': [
              # Instead of trying to use gcc's build system to build only
              # libgcc_eh, we just build the C files and archive them manually.
              command.RemoveDirectory('include'),
              command.Mkdir('include'),
              command.Copy(os.path.join('%(gcc_src)s', 'gcc',
                           'unwind-generic.h'),
                           os.path.join('include', 'unwind.h')),
              command.Copy(os.path.join('%(scripts)s', 'libgcc-tconfig.h'),
                           'tconfig.h'),
              command.WriteData('', 'tm.h'),
              BuildLibgccEhCmd('unwind-dw2.c', 'unwind-dw2.o', arch),
              BuildLibgccEhCmd('unwind-dw2-fde-glibc.c',
                               'unwind-dw2-fde-glibc.o', arch),
              command.Command([PnaclTool('ar'), 'rc',
                               command.path.join('%(output)s', 'libgcc_eh.a'),
                               'unwind-dw2.o', 'unwind-dw2-fde-glibc.o']),
          ],
      },
    })
  return libs

def UnsandboxedIRT(arch):
  libs = {
      GSDJoin('unsandboxed_irt', arch): {
          'type': 'build',
          'output_subdir': os.path.join('translator', arch, 'lib'),
          # This lib #includes
          # arbitrary stuff from native_client/src/{include,untrusted,trusted}
          'inputs': { 'support': os.path.join(NACL_DIR, 'src', 'nonsfi', 'irt'),
                      'untrusted': os.path.join(
                          NACL_DIR, 'src', 'untrusted', 'irt'),
                      'include': os.path.join(NACL_DIR, 'src'), },
          'commands': [
              # The NaCl headers insist on having a platform macro such as
              # NACL_LINUX defined, but src/nonsfi/irt_interfaces.c does not
              # itself use any of these macros, so defining NACL_LINUX here
              # even on non-Linux systems is OK.
              # TODO(dschuff): this include path breaks the input encapsulation
              # for build rules.
              command.Command([
                  'gcc', '-m32', '-O2', '-Wall', '-Werror',
                  '-I%(top_srcdir)s/..', '-DNACL_LINUX=1', '-DDEFINE_MAIN',
                  '-c', command.path.join('%(support)s', 'irt_interfaces.c'),
                  '-o', command.path.join('%(output)s', 'unsandboxed_irt.o')]),
              command.Command([
                  'gcc', '-m32', '-O2', '-Wall', '-Werror',
                  '-I%(top_srcdir)s/..',
                  '-c', command.path.join('%(support)s', 'irt_random.c'),
                  '-o', command.path.join('%(output)s', 'irt_random.o')]),
              command.Command([
                  'gcc', '-m32', '-O2', '-Wall', '-Werror',
                  '-I%(top_srcdir)s/..',
                  '-c', command.path.join('%(untrusted)s', 'irt_query_list.c'),
                  '-o', command.path.join('%(output)s', 'irt_query_list.o')]),
          ],
      },
  }
  return libs


def SDKCompiler(arches):
  arch_packages = ([GSDJoin('newlib', arch) for arch in arches] +
                   [GSDJoin('libcxx', arch) for arch in arches])
  compiler = {
      # TODO(dschuff): When we get a sandboxed translator build, move this
      # target out to somewhere shareable. It's necessary for now because
      # there's no way to inject the right -i and -L flags into SCons to find
      # newlib.
      'sdk_compiler': {
          'type': 'work',
          'output_subdir': 'sdk_compiler',
          'dependencies': ['target_lib_compiler'] + arch_packages,
          'commands': [
              command.CopyRecursive('%(' + t + ')s', '%(output)s')
              for t in ['target_lib_compiler'] + arch_packages],
      },
  }
  return compiler


def SDKLibs(arch, is_canonical):
  scons_flags = ['--verbose', 'MODE=nacl', '-j%(cores)s', 'naclsdk_validate=0',
                 'pnacl_newlib_dir=%(abs_sdk_compiler)s',
                 'DESTINATION_ROOT=%(work_dir)s']
  if arch == 'le32':
    scons_flags.extend(['bitcode=1', 'platform=x86-32'])
  elif not IsBCArch(arch):
    scons_flags.extend(['nacl_clang=1',
                        'platform=' + pynacl.platform.GetArch3264(arch)])
  else:
    raise ValueError('Should not be building SDK libs for', arch)
  libs = {
      GSDJoin('core_sdk_libs', arch): {
          'type': 'build' if is_canonical else 'work',
          'dependencies': ['sdk_compiler', 'target_lib_compiler'],
          'inputs': {
              'src_untrusted': os.path.join(NACL_DIR, 'src', 'untrusted'),
              'src_include': os.path.join(NACL_DIR, 'src', 'include'),
              'scons.py': os.path.join(NACL_DIR, 'scons.py'),
              'site_scons': os.path.join(NACL_DIR, 'site_scons'),
              'prep_nacl_sdk':
                  os.path.join(NACL_DIR, 'build', 'prep_nacl_sdk.py'),
          },
          'commands': [
            command.Command(
                [sys.executable, '%(scons.py)s',
                 'includedir=' +os.path.join('%(output)s',
                                             TripleFromArch(MultilibArch(arch)),
                                             'include'),
                 'libdir=' + os.path.join('%(output)s', MultilibLibDir(arch)),
                 'install'] + scons_flags,
                cwd=NACL_DIR),
          ],
      }
  }
  return libs
