OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2012 The Native Client Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 # |
| 6 # IMPORTANT NOTE: If you make local mods to this file, you must run: |
| 7 # % pnacl/build.sh driver |
| 8 # in order for them to take effect in the scons build. This command |
| 9 # updates the copy in the toolchain/ tree. |
| 10 # |
| 11 |
| 12 # Generate a draft manifest file given bitcode pexe. |
| 13 # URLs and unknown runtime dependencies may be manually changed afterwards. |
| 14 |
| 15 from driver_tools import * |
| 16 from driver_env import env |
| 17 from driver_log import Log, DriverOpen, TempFiles |
| 18 from collections import deque |
| 19 |
| 20 try: |
| 21 import json |
| 22 except Exception: |
| 23 import simplejson as json |
| 24 |
| 25 EXTRA_ENV = { |
| 26 'INPUTS' : '', |
| 27 'OUTPUT' : '', |
| 28 |
| 29 # FLAT_URL_SCHEME is '1' if we are just trying to test via scons and |
| 30 # do not want to put libraries under any kind of directory structure |
| 31 # to simplify file staging. This means that really only one of the files |
| 32 # is valid, since all arches will have the same URL. This is only for tests. |
| 33 'FLAT_URL_SCHEME' : '0', |
| 34 |
| 35 # Search path for finding pso's to grab transitive dependencies. |
| 36 'SEARCH_DIRS' : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}', |
| 37 'SEARCH_DIRS_USER' : '', |
| 38 # This doesn't include native lib directories, so we aren't |
| 39 # tracing transitive dependencies for those. |
| 40 'SEARCH_DIRS_BUILTIN': ' ${BASE_USR}/lib/ ' + |
| 41 ' ${BASE_SDK}/lib/ ' + |
| 42 ' ${BASE_LIB}/ ', |
| 43 } |
| 44 env.update(EXTRA_ENV) |
| 45 |
| 46 NMF_PATTERNS = [ |
| 47 ( ('-L', '(.+)'), |
| 48 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
| 49 ( '-L(.+)', |
| 50 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
| 51 ( '--library-path=(.+)', |
| 52 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))\n"), |
| 53 |
| 54 ( ('-o', '(.+)'), "env.set('OUTPUT', pathtools.normalize($0))\n"), |
| 55 ( '--flat-url-scheme', "env.set('FLAT_URL_SCHEME', '1')"), |
| 56 |
| 57 ( '(.*)', "env.append('INPUTS', pathtools.normalize($0))"), |
| 58 ] |
| 59 |
| 60 def Usage(): |
| 61 print "Usage: pnacl-nmf <pexe_file> [-L=...]" |
| 62 print "Generate a draft NMF file based on the given pexe file." |
| 63 |
| 64 def AddUrlForAllArches(json_dict, shortname): |
| 65 # TODO(jvoung): We may want to calculate a hash to go with the URL. |
| 66 KNOWN_NMF_ARCHES = ['x86-32', 'x86-64', 'arm'] |
| 67 for arch in KNOWN_NMF_ARCHES: |
| 68 json_dict[arch] = {} |
| 69 if env.getbool('FLAT_URL_SCHEME'): |
| 70 json_dict[arch]['url'] = '%s' % shortname |
| 71 else: |
| 72 json_dict[arch]['url'] = 'lib-%s/%s' % (arch, shortname) |
| 73 |
| 74 def SetPortableUrl(json_dict, shortname): |
| 75 # TODO(jvoung): We may want to calculate a hash to go with the URL. |
| 76 json_dict['portable'] = {} |
| 77 json_dict['portable']['url'] = shortname |
| 78 |
| 79 def GetActualFilePathAndType(shortname): |
| 80 actual_lib_path = ldtools.FindFile([shortname], env.get('SEARCH_DIRS')) |
| 81 if actual_lib_path == None: |
| 82 Log.Warning('Could not find path of lib: %s, assuming it is native', |
| 83 shortname) |
| 84 file_type = 'so' |
| 85 else: |
| 86 file_type = FileType(actual_lib_path) |
| 87 return actual_lib_path, file_type |
| 88 |
| 89 def GetTransitiveClosureOfNeeded(base_needed): |
| 90 # TODO(jvoung): We may need to get the topological sort of depedencies. |
| 91 visited_portable = set() |
| 92 visited_native = set() |
| 93 all_needed = set(base_needed) |
| 94 worklist = deque(base_needed) |
| 95 while len(worklist) != 0: |
| 96 lib = worklist.popleft() |
| 97 if lib in visited_portable or lib in visited_native: |
| 98 continue |
| 99 actual_lib_path, file_type = GetActualFilePathAndType(lib) |
| 100 if file_type == 'so': |
| 101 # We could trace if we used objdump / readelf... |
| 102 Log.Warning('Not tracing dependencies of native lib %s', lib) |
| 103 visited_native.add(lib) |
| 104 continue |
| 105 elif file_type == 'pso': |
| 106 visited_portable.add(lib) |
| 107 more_needed = GetBitcodeMetadata(actual_lib_path).get('NeedsLibrary', []) |
| 108 all_needed |= set(more_needed) |
| 109 worklist.extend(more_needed) |
| 110 else: |
| 111 Log.Fatal('Lib: %s is neither bitcode nor native', lib) |
| 112 return all_needed |
| 113 |
| 114 |
| 115 def GenerateStaticNMF(pexe_file, output_file): |
| 116 nmf_json = {} |
| 117 nmf_json['program'] = {} |
| 118 SetPortableUrl(nmf_json['program'], pathtools.basename(pexe_file)) |
| 119 json.dump(nmf_json, output_file, sort_keys=True, indent=2) |
| 120 |
| 121 |
| 122 def GenerateDynamicNMF(pexe_file, needed, output_file): |
| 123 nmf_json = {} |
| 124 # Set runnable-ld.so as the program interpreter. |
| 125 nmf_json['program'] = {} |
| 126 AddUrlForAllArches(nmf_json['program'], 'runnable-ld.so') |
| 127 # Set the pexe as the main program. |
| 128 nmf_json['files'] = {} |
| 129 nmf_json['files']['main.nexe'] = {} |
| 130 SetPortableUrl(nmf_json['files']['main.nexe'], pathtools.basename(pexe_file)) |
| 131 |
| 132 # Get transitive closure of needed libraries. |
| 133 transitive_needed = GetTransitiveClosureOfNeeded(needed) |
| 134 |
| 135 # Make urls for libraries. |
| 136 for lib in transitive_needed: |
| 137 nmf_json['files'][lib] = {} |
| 138 actual_lib_path, file_type = GetActualFilePathAndType(lib) |
| 139 if file_type == 'so': |
| 140 # Assume a native version exists for every known arch. |
| 141 AddUrlForAllArches(nmf_json['files'][lib], lib) |
| 142 elif file_type == 'pso': |
| 143 SetPortableUrl(nmf_json['files'][lib], lib) |
| 144 else: |
| 145 Log.Fatal('Needed library is not a .so nor a .pso') |
| 146 json.dump(nmf_json, output_file, sort_keys=True, indent=2) |
| 147 |
| 148 |
| 149 def main(argv): |
| 150 ParseArgs(argv, NMF_PATTERNS) |
| 151 inputs = env.get('INPUTS') |
| 152 |
| 153 if not inputs or len(inputs) != 1: |
| 154 Usage() |
| 155 DriverExit(0) |
| 156 pexe_file = inputs[0] |
| 157 output = env.getone('OUTPUT') |
| 158 if output == '': |
| 159 output_file = sys.stdout |
| 160 else: |
| 161 output_file = DriverOpen(output, 'w') |
| 162 |
| 163 if not FileType(pexe_file) == 'pexe': |
| 164 Log.Fatal("File %s is not an executable bitcode file", |
| 165 pathtools.touser(pexe_file)) |
| 166 needed = GetBitcodeMetadata(pexe_file).get('NeedsLibrary', []) |
| 167 if len(needed) == 0: |
| 168 GenerateStaticNMF(pexe_file, output_file) |
| 169 else: |
| 170 GenerateDynamicNMF(pexe_file, needed, output_file) |
| 171 DriverClose(output_file) |
| 172 return 0 |
| 173 |
| 174 if __name__ == "__main__": |
| 175 DriverMain(main) |
OLD | NEW |