15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
60f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)"""Tool for automatically creating .nmf files from .nexe/.pexe executables.
70f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
80f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)As well as creating the nmf file this tool can also find and stage
90f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)any shared libraries dependancies that the executables might have.
100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)"""
110f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import errno
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import json
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import struct
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import getos
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import quote
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)if sys.version_info < (2, 6, 0):
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.stderr.write("python 2.6 or later is required run this script\n")
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.exit(1)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$')
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$')
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OBJDUMP_ARCH_MAP = {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Names returned by Linux's objdump:
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf64-x86-64': 'x86-64',
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf32-i386': 'x86-32',
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf32-little': 'arm',
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf32-littlearm': 'arm',
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # Names returned by old x86_64-nacl-objdump:
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf64-nacl': 'x86-64',
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'elf32-nacl': 'x86-32',
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # Names returned by new x86_64-nacl-objdump:
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    'elf64-x86-64-nacl': 'x86-64',
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    'elf32-x86-64-nacl': 'x86-64',
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    'elf32-i386-nacl': 'x86-32',
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ARCH_LOCATION = {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'x86-32': 'lib32',
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'x86-64': 'lib64',
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'arm': 'lib',
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These constants are used within nmf files.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RUNNABLE_LD = 'runnable-ld.so'  # Name of the dynamic loader
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MAIN_NEXE = 'main.nexe'  # Name of entry point for execution
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PROGRAM_KEY = 'program'  # Key of the program section in an nmf file
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)URL_KEY = 'url'  # Key of the url field for a particular file in an nmf file
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FILES_KEY = 'files'  # Key of the files section in an nmf file
62ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben MurdochPNACL_OPTLEVEL_KEY = 'optlevel' # key for PNaCl optimization level
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)PORTABLE_KEY = 'portable' # key for portable section of manifest
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TRANSLATE_KEY = 'pnacl-translate' # key for translatable objects
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The proper name of the dynamic linker, as kept in the IRT.  This is
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# excluded from the nmf file by convention.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LD_NACL_MAP = {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'x86-32': 'ld-nacl-x86-32.so.1',
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'x86-64': 'ld-nacl-x86-64.so.1',
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'arm': None,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def DebugPrint(message):
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if DebugPrint.debug_mode:
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.stderr.write('%s\n' % message)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DebugPrint.debug_mode = False  # Set to True to enable extra debug prints
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MakeDir(dirname):
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Just like os.makedirs but doesn't generate errors when dirname
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  already exists.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isdir(dirname):
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Trace("mkdir: %s" % dirname)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.makedirs(dirname)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError as exception_info:
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if exception_info.errno != errno.EEXIST:
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Error(Exception):
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Local Error class for this file.'''
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pass
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def ParseElfHeader(path):
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Determine properties of a nexe by parsing elf header.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Return tuple of architecture and boolean signalling whether
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  the executable is dynamic (has INTERP header) or static.
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # From elf.h:
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # typedef struct
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # {
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   Elf64_Half e_type; /* Object file type */
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   Elf64_Half e_machine; /* Architecture */
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   ...
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # } Elf32_Ehdr;
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  elf_header_format = '16s2H'
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  elf_header_size = struct.calcsize(elf_header_format)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  with open(path, 'rb') as f:
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header = f.read(elf_header_size)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  try:
1247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    header = struct.unpack(elf_header_format, header)
1257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  except struct.error:
1267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    raise Error("error parsing elf header: %s" % path)
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  e_ident, _, e_machine = header[:3]
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  elf_magic = '\x7fELF'
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if e_ident[:4] != elf_magic:
1310f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    raise Error('Not a valid NaCl executable: %s' % path)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  e_machine_mapping = {
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    3 : 'x86-32',
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    40 : 'arm',
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    62 : 'x86-64'
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if e_machine not in e_machine_mapping:
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    raise Error('Unknown machine type: %s' % e_machine)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Set arch based on the machine type in the elf header
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  arch = e_machine_mapping[e_machine]
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Now read the full header in either 64bit or 32bit mode
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dynamic = IsDynamicElf(path, arch == 'x86-64')
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return arch, dynamic
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def IsDynamicElf(path, is64bit):
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Examine an elf file to determine if it is dynamically
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  linked or not.
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  This is determined by searching the program headers for
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  a header of type PT_INTERP.
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if is64bit:
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elf_header_format = '16s2HI3QI3H'
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else:
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elf_header_format = '16s2HI3II3H'
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  elf_header_size = struct.calcsize(elf_header_format)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  with open(path, 'rb') as f:
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header = f.read(elf_header_size)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    header = struct.unpack(elf_header_format, header)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    p_header_offset = header[5]
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    p_header_entry_size = header[9]
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    num_p_header = header[10]
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    f.seek(p_header_offset)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    p_headers = f.read(p_header_entry_size*num_p_header)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # Read the first word of each Phdr to find out its type.
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # typedef struct
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   Elf32_Word  p_type;     /* Segment type */
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  #   ...
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  # } Elf32_Phdr;
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  elf_phdr_format = 'I'
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PT_INTERP = 3
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while p_headers:
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    p_header = p_headers[:p_header_entry_size]
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    p_headers = p_headers[p_header_entry_size:]
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    phdr_type = struct.unpack(elf_phdr_format, p_header[:4])[0]
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if phdr_type == PT_INTERP:
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return True
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return False
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ArchFile(object):
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Simple structure containing information about
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Attributes:
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: Name of this file
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path: Full path to this file on the build system
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    arch: Architecture of this file (e.g., x86-32)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url: Relative path to file in the staged web directory.
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Used for specifying the "url" attribute in the nmf file.'''
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, name, path, url, arch=None):
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.name = name
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.path = path
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.url = url
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.arch = arch
20690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if not arch:
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.arch = ParseElfHeader(path)[0]
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __repr__(self):
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return '<ArchFile %s>' % self.path
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __str__(self):
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Return the file path when invoked with the str() function'''
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.path
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NmfUtils(object):
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''Helper class for creating and managing nmf files
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Attributes:
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manifest: A JSON-structured dict containing the nmf structure
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed: A dict with key=filename and value=ArchFile (see GetNeeded)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '''
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, main_files=None, objdump=None,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               lib_path=None, extra_files=None, lib_prefix=None,
227ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               remap=None, pnacl_optlevel=None):
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Constructor
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      main_files: List of main entry program files.  These will be named
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          files->main.nexe for dynamic nexes, and program for static nexes
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lib_path: List of paths to library directories
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extra_files: List of extra files to include in the nmf
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lib_prefix: A list of path components to prepend to the library paths,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          both for staging the libraries and for inclusion into the nmf file.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Examples:  ['..'], ['lib_dir']
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      remap: Remaps the library name in the manifest.
240ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      pnacl_optlevel: Optimization level for PNaCl translation.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '''
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.objdump = objdump
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.main_files = main_files or []
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.extra_files = extra_files or []
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.lib_path = lib_path or []
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.manifest = None
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.needed = {}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.lib_prefix = lib_prefix or []
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.remap = remap or {}
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.pnacl = main_files and main_files[0].endswith('pexe')
251ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    self.pnacl_optlevel = pnacl_optlevel
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for filename in self.main_files:
25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if not os.path.exists(filename):
25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        raise Error('Input file not found: %s' % filename)
25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if not os.path.isfile(filename):
25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        raise Error('Input is not a file: %s' % filename)
25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  def GleanFromObjdump(self, files, arch):
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Get architecture and dependency information for given files
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
26390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      files: A list of files to examine.
26490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          [ '/path/to/my.nexe',
26590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            '/path/to/lib64/libmy.so',
26690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            '/path/to/mydata.so',
26790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            '/path/to/my.data' ]
26890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      arch: The architecure we are looking for, or None to accept any
26990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            architecture.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns: A tuple with the following members:
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      input_info: A dict with key=filename and value=ArchFile of input files.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Includes the input files as well, with arch filled in if absent.
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Example: { '/path/to/my.nexe': ArchFile(my.nexe),
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     '/path/to/libfoo.so': ArchFile(libfoo.so) }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      needed: A set of strings formatted as "arch/name".  Example:
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          set(['x86-32/libc.so', 'x86-64/libgcc.so'])
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.objdump:
28090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      self.objdump = FindObjdumpExecutable()
28190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if not self.objdump:
28290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        raise Error('No objdump executable found (see --help for more info)')
28390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
28490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    full_paths = set()
28590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for filename in files:
28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if os.path.exists(filename):
28790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        full_paths.add(filename)
28890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      else:
28990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        for path in self.FindLibsInPath(filename):
29090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          full_paths.add(path)
29190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
29290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    cmd = [self.objdump, '-p'] + list(full_paths)
29390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    DebugPrint('GleanFromObjdump[%s](%s)' % (arch, cmd))
29490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            stderr=subprocess.PIPE, bufsize=-1)
29690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    input_info = {}
29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    found_basenames = set()
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed = set()
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output, err_output = proc.communicate()
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if proc.returncode:
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' %
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  (output, err_output, proc.returncode))
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line in output.splitlines(True):
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Objdump should display the architecture first and then the dependencies
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # second for each file in the list.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      matched = FormatMatcher.match(line)
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if matched:
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filename = matched.group(1)
31190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        file_arch = OBJDUMP_ARCH_MAP[matched.group(2)]
31290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        if arch and file_arch != arch:
31390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          continue
31490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        name = os.path.basename(filename)
31590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        found_basenames.add(name)
31690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        input_info[filename] = ArchFile(
31790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            arch=file_arch,
31890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            name=name,
31990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            path=filename,
32090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            url='/'.join(self.lib_prefix + [ARCH_LOCATION[file_arch], name]))
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      matched = NeededMatcher.match(line)
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if matched:
32390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        match = '/'.join([file_arch, matched.group(1)])
32490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        needed.add(match)
32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        Trace("NEEDED: %s" % match)
32690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for filename in files:
32890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if os.path.basename(filename) not in found_basenames:
32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        raise Error('Library not found [%s]: %s' % (arch, filename))
33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return input_info, needed
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FindLibsInPath(self, name):
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Finds the set of libraries matching |name| within lib_path
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name: name of library to find
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A list of system paths that match the given name within the lib_path'''
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    files = []
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for dirname in self.lib_path:
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename = os.path.join(dirname, name)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if os.path.exists(filename):
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        files.append(filename)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not files:
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise Error('cannot find library %s' % name)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return files
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetNeeded(self):
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Collect the list of dependencies for the main_files
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A dict with key=filename and value=ArchFile of input files.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Includes the input files as well, with arch filled in if absent.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Example: { '/path/to/my.nexe': ArchFile(my.nexe),
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.needed:
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self.needed
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DebugPrint('GetNeeded(%s)' % self.main_files)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    dynamic = any(ParseElfHeader(f)[1] for f in self.main_files)
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if dynamic:
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      examined = set()
36890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      all_files, unexamined = self.GleanFromObjdump(self.main_files, None)
36990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      for arch_file in all_files.itervalues():
37090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        arch_file.url = arch_file.path
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if unexamined:
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
37390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while unexamined:
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        files_to_examine = {}
37690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
37790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        # Take all the currently unexamined files and group them
37890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        # by architecture.
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for arch_name in unexamined:
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          arch, name = arch_name.split('/')
38190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          files_to_examine.setdefault(arch, []).append(name)
38290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
38390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        # Call GleanFromObjdump() for each architecture.
38490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        needed = set()
38590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        for arch, files in files_to_examine.iteritems():
38690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          new_files, new_needed = self.GleanFromObjdump(files, arch)
38790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          all_files.update(new_files)
38890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          needed |= new_needed
38990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        examined |= unexamined
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        unexamined = needed - examined
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # With the runnable-ld.so scheme we have today, the proper name of
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # the dynamic linker should be excluded from the list of files.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ldso = [LD_NACL_MAP[arch] for arch in set(OBJDUMP_ARCH_MAP.values())]
39690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      for name, arch_file in all_files.items():
39790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        if arch_file.name in ldso:
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          del all_files[name]
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.needed = all_files
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for filename in self.main_files:
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        url = os.path.split(filename)[1]
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        archfile = ArchFile(name=os.path.basename(filename),
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            path=filename, url=url)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.needed[filename] = archfile
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.needed
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def StageDependencies(self, destination_dir):
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Copies over the dependencies into a given destination directory
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Each library will be put into a subdirectory that corresponds to the arch.
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      destination_dir: The destination directory for staging the dependencies
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    nexe_root = os.path.dirname(os.path.abspath(self.main_files[0]))
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    nexe_root = os.path.normcase(nexe_root)
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed = self.GetNeeded()
42290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for arch_file in needed.itervalues():
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      urldest = arch_file.url
42490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      source = arch_file.path
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # for .nexe and .so files specified on the command line stage
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # them in paths relative to the .nexe (with the .nexe always
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # being staged at the root).
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if source in self.main_files:
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        absdest = os.path.normcase(os.path.abspath(urldest))
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if absdest.startswith(nexe_root):
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          urldest = os.path.relpath(urldest, nexe_root)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      destination = os.path.join(destination_dir, urldest)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (os.path.normcase(os.path.abspath(source)) ==
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          os.path.normcase(os.path.abspath(destination))):
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # make sure target dir exists
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MakeDir(os.path.dirname(destination))
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Trace('copy: %s -> %s' % (source, destination))
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      shutil.copy2(source, destination)
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GeneratePNaClManifest(self):
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    manifest = {}
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    manifest[PROGRAM_KEY] = {}
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    manifest[PROGRAM_KEY][PORTABLE_KEY] = {}
450ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    translate_dict =  {
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "url": os.path.basename(self.main_files[0]),
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
453ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if self.pnacl_optlevel is not None:
454ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      translate_dict[PNACL_OPTLEVEL_KEY] = self.pnacl_optlevel
455ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    manifest[PROGRAM_KEY][PORTABLE_KEY][TRANSLATE_KEY] = translate_dict
4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.manifest = manifest
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateManifest(self):
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Create a JSON formatted dict containing the files
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NaCl will map url requests based on architecture.  The startup NEXE
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    can always be found under the top key PROGRAM.  Additional files are under
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the FILES key further mapped by file name.  In the case of 'runnable' the
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PROGRAM key is populated with urls pointing the runnable-ld.so which acts
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    as the startup nexe.  The application itself is then placed under the
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FILES key mapped as 'main.exe' instead of the original name so that the
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    loader can find it. '''
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed = self.GetNeeded()
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    runnable = any(n.endswith(RUNNABLE_LD) for n in needed)
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    extra_files_kv = [(key, ArchFile(name=key,
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     arch=arch,
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     path=url,
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     url=url))
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      for key, arch, url in self.extra_files]
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    nexe_root = os.path.dirname(os.path.abspath(self.main_files[0]))
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for need, archinfo in needed.items() + extra_files_kv:
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      urlinfo = { URL_KEY: archinfo.url }
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name = archinfo.name
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If starting with runnable-ld.so, make that the main executable.
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if runnable:
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if need.endswith(RUNNABLE_LD):
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if need in self.main_files:
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # Ensure that the .nexe and .so names are relative to the root
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # of where the .nexe lives.
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if os.path.abspath(urlinfo[URL_KEY]).startswith(nexe_root):
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          urlinfo[URL_KEY] = os.path.relpath(urlinfo[URL_KEY], nexe_root)
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if need.endswith(".nexe"):
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # Place it under program if we aren't using the runnable-ld.so.
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if not runnable:
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            continue
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # Otherwise, treat it like another another file named main.nexe.
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          name = MAIN_NEXE
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name = self.remap.get(name, name)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fileinfo = manifest[FILES_KEY].get(name, {})
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fileinfo[archinfo.arch] = urlinfo
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      manifest[FILES_KEY][name] = fileinfo
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.manifest = manifest
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetManifest(self):
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Returns a JSON-formatted dict containing the NaCl dependencies'''
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.manifest:
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if self.pnacl:
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self._GeneratePNaClManifest()
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self._GenerateManifest()
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.manifest
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetJson(self):
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    '''Returns the Manifest as a JSON-formatted string'''
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pretty_string = json.dumps(self.GetManifest(), indent=2)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # json.dumps sometimes returns trailing whitespace and does not put
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # a newline at the end.  This code fixes these problems.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pretty_lines = pretty_string.split('\n')
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Trace(msg):
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if Trace.verbose:
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.stderr.write(str(msg) + '\n')
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Trace.verbose = False
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def ParseExtraFiles(encoded_list, err):
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Parse the extra-files list and return a canonicalized list of
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  [key, arch, url] triples.  The |encoded_list| should be a list of
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  strings of the form 'key:url' or 'key:arch:url', where an omitted
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  'arch' is taken to mean 'portable'.
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  All entries in |encoded_list| are checked for syntax errors before
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  returning.  Error messages are written to |err| (typically
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.stderr) so that the user has actionable feedback for fixing all
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  errors, rather than one at a time.  If there are any errors, None is
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  returned instead of a list, since an empty list is a valid return
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  value.
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  seen_error = False
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canonicalized = []
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for ix in range(len(encoded_list)):
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    kv = encoded_list[ix]
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    unquoted = quote.unquote(kv, ':')
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if len(unquoted) == 3:
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if unquoted[1] != ':':
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        err.write('Syntax error for key:value tuple ' +
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  'for --extra-files argument: ' + kv + '\n')
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        seen_error = True
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        canonicalized.append([unquoted[0], 'portable', unquoted[2]])
5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif len(unquoted) == 5:
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if unquoted[1] != ':' or unquoted[3] != ':':
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        err.write('Syntax error for key:arch:url tuple ' +
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  'for --extra-files argument: ' +
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  kv + '\n')
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        seen_error = True
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        canonicalized.append([unquoted[0], unquoted[2], unquoted[4]])
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      err.write('Bad key:arch:url tuple for --extra-files: ' + kv + '\n')
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if seen_error:
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return None
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return canonicalized
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
57790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)def GetSDKRoot():
57890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """Determine current NACL_SDK_ROOT, either via the environment variable
57990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  itself, or by attempting to derive it from the location of this script.
58090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """
58190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  sdk_root = os.environ.get('NACL_SDK_ROOT')
58290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not sdk_root:
58390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    sdk_root = os.path.dirname(SCRIPT_DIR)
58490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if not os.path.exists(os.path.join(sdk_root, 'toolchain')):
58590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return None
58690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
58790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return sdk_root
58890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
58990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
59090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)def FindObjdumpExecutable():
59190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """Derive path to objdump executable to use for determining shared
59290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  object dependencies.
59390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """
59490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  sdk_root = GetSDKRoot()
59590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not sdk_root:
59690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return None
59790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
59890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  osname = getos.GetPlatform()
59990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  toolchain = os.path.join(sdk_root, 'toolchain', '%s_x86_glibc' % osname)
60090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  objdump = os.path.join(toolchain, 'bin', 'x86_64-nacl-objdump')
60190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if osname == 'win':
60290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    objdump += '.exe'
60390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
60490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not os.path.exists(objdump):
60590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    sys.stderr.write('WARNING: failed to find objdump in default '
60690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                     'location: %s' % objdump)
60790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return None
60890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
60990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return objdump
61090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
61190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
61290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)def GetDefaultLibPath(config):
61390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """Derive default library path to use when searching for shared
61490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  objects.  This currently include the toolchain library folders
61590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  as well as the top level SDK lib folder and the naclports lib
61690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  folder.  We include both 32-bit and 64-bit library paths.
61790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  """
61890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  assert(config in ('Debug', 'Release'))
61990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  sdk_root = GetSDKRoot()
62090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not sdk_root:
62190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # TOOD(sbc): output a warning here?  We would also need to suppress
62290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # the warning when run from the chromium build.
62390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return []
62490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
62590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  osname = getos.GetPlatform()
62690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  libpath = [
627868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    # Core toolchain libraries
62890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'toolchain/%s_x86_glibc/x86_64-nacl/lib' % osname,
62990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'toolchain/%s_x86_glibc/x86_64-nacl/lib32' % osname,
630868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    # naclports installed libraries
631868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    'toolchain/%s_x86_glibc/x86_64-nacl/usr/lib' % osname,
632868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    'toolchain/%s_x86_glibc/i686-nacl/usr/lib' % osname,
633868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    # SDK bundle libraries
63490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'lib/glibc_x86_32/%s' % config,
63590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'lib/glibc_x86_64/%s' % config,
636868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    # naclports bundle libraries
63790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'ports/lib/glibc_x86_32/%s' % config,
63890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'ports/lib/glibc_x86_64/%s' % config,
63990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  ]
64090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  libpath = [os.path.normpath(p) for p in libpath]
64190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  libpath = [os.path.join(sdk_root, p) for p in libpath]
64290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return libpath
64390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
64490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def main(argv):
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser(
6470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      usage='Usage: %prog [options] nexe [extra_libs...]', description=__doc__)
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-o', '--output', dest='output',
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Write manifest file to FILE (default is stdout)',
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    metavar='FILE')
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-D', '--objdump', dest='objdump',
65290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                    help='Override the default "objdump" tool used to find '
65390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                         'shared object dependencies',
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    metavar='TOOL')
65590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  parser.add_option('--no-default-libpath', action='store_true',
65690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                    help="Don't include the SDK default library paths")
65790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  parser.add_option('--debug-libs', action='store_true',
65890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                    help='Use debug library paths when constructing default '
65990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                         'library path.')
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-L', '--library-path', dest='lib_path',
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    action='append', default=[],
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Add DIRECTORY to library search path',
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    metavar='DIRECTORY')
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-P', '--path-prefix', dest='path_prefix', default='',
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='A path to prepend to shared libraries in the .nmf',
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    metavar='DIRECTORY')
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Destination directory for staging libraries',
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    metavar='DIRECTORY')
6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parser.add_option('-t', '--toolchain', help='Legacy option, do not use')
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-n', '--name', dest='name',
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Rename FOO as BAR',
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    action='append', default=[], metavar='FOO,BAR')
6742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parser.add_option('-x', '--extra-files',
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    help=('Add extra key:file tuple to the "files"' +
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ' section of the .nmf'),
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    action='append', default=[], metavar='FILE')
678ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  parser.add_option('-O', '--pnacl-optlevel',
679ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    help='Set the optimization level to N in PNaCl manifests',
680ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    metavar='N')
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-v', '--verbose',
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Verbose output', action='store_true')
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option('-d', '--debug-mode',
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    help='Debug mode', action='store_true')
6850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # To enable bash completion for this command first install optcomplete
6870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # and then add this line to your .bashrc:
6880f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  #  complete -F _optcomplete create_nmf.py
6890f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  try:
6900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    import optcomplete
6910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    optcomplete.autocomplete(parser)
6920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  except ImportError:
6930f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    pass
6940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options, args = parser.parse_args(argv)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.verbose:
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Trace.verbose = True
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.debug_mode:
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DebugPrint.debug_mode = True
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if options.toolchain is not None:
702868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    sys.stderr.write('warning: option -t/--toolchain is deprecated.\n')
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if len(args) < 1:
705868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    parser.error('No nexe files specified.  See --help for more info')
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  canonicalized = ParseExtraFiles(options.extra_files, sys.stderr)
7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if canonicalized is None:
7092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    parser.error('Bad --extra-files (-x) argument syntax')
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  remap = {}
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for ren in options.name:
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parts = ren.split(',')
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(parts) != 2:
715868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      parser.error('Expecting --name=<orig_arch.so>,<new_name.so>')
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    remap[parts[0]] = parts[1]
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.path_prefix:
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path_prefix = options.path_prefix.split('/')
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path_prefix = []
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for libpath in options.lib_path:
72490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if not os.path.exists(libpath):
725868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      sys.stderr.write('Specified library path does not exist: %s\n' % libpath)
726868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    elif not os.path.isdir(libpath):
727868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      sys.stderr.write('Specified library is not a directory: %s\n' % libpath)
72890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
72990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not options.no_default_libpath:
73090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # Add default libraries paths to the end of the search path.
73190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    config = options.debug_libs and 'Debug' or 'Release'
73290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    options.lib_path += GetDefaultLibPath(config)
73390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
734ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  pnacl_optlevel = None
735ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if options.pnacl_optlevel is not None:
736ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    pnacl_optlevel = int(options.pnacl_optlevel)
737ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if pnacl_optlevel < 0 or pnacl_optlevel > 3:
738ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      sys.stderr.write(
739ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          'warning: PNaCl optlevel %d is unsupported (< 0 or > 3)\n' %
740ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          pnacl_optlevel)
741ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  nmf = NmfUtils(objdump=options.objdump,
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 main_files=args,
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 lib_path=options.lib_path,
7452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 extra_files=canonicalized,
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 lib_prefix=path_prefix,
747ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 remap=remap,
748ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 pnacl_optlevel=pnacl_optlevel)
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  nmf.GetManifest()
75190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if not options.output:
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.stdout.write(nmf.GetJson())
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    with open(options.output, 'w') as output:
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output.write(nmf.GetJson())
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if options.stage_dependencies and not nmf.pnacl:
7582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Trace('Staging dependencies...')
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nmf.StageDependencies(options.stage_dependencies)
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rtn = main(sys.argv[1:])
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except Error, e:
7682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rtn = 1
77090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  except KeyboardInterrupt:
77190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
77290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    rtn = 1
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(rtn)
774