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 import filetype | |
20 | |
21 import hashlib | |
22 import json | |
23 | |
24 EXTRA_ENV = { | |
25 'INPUTS' : '', | |
26 'OUTPUT' : '', | |
27 | |
28 'BASELINE_NMF' : '', | |
29 | |
30 # FLAT_URL_SCHEME is '1' if we are just trying to test via scons and | |
31 # do not want to put libraries under any kind of directory structure | |
32 # to simplify file staging. This means that really only one of the files | |
33 # is valid, since all arches will have the same URL. This is only for tests. | |
34 'FLAT_URL_SCHEME' : '0', | |
35 | |
36 # Search path for finding pso's to grab transitive dependencies. | |
37 'SEARCH_DIRS' : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}', | |
38 'SEARCH_DIRS_USER' : '', | |
39 # This doesn't include native lib directories, so we aren't | |
40 # tracing transitive dependencies for those. | |
41 'SEARCH_DIRS_BUILTIN': ' ${BASE_USR}/lib/ ' + | |
42 ' ${BASE_SDK}/lib/ ' + | |
43 ' ${BASE_LIB}/ ', | |
44 } | |
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 ( ('--base-nmf', '(.+)'), | |
54 "env.set('BASELINE_NMF', pathtools.normalize($0))\n"), | |
55 | |
56 ( ('-o', '(.+)'), "env.set('OUTPUT', pathtools.normalize($0))\n"), | |
57 ( '--flat-url-scheme', "env.set('FLAT_URL_SCHEME', '1')"), | |
58 | |
59 ( '(.*)', "env.append('INPUTS', pathtools.normalize($0))"), | |
60 ] | |
61 | |
62 def get_help(argv): | |
63 return """ | |
64 NaCl Manifest Generator for PNaCl. | |
65 | |
66 Usage: %s [options] <pexe_file> | |
67 | |
68 BASIC OPTIONS: | |
69 -o <file> Output to <file> | |
70 --base-nmf <file> Use a baseline NMF file as a starting point. | |
71 -L <dir> Add library search path | |
72 --library-path=<dir> Ditto. | |
73 --flat-url-scheme For testing: Do not nest architecture-specific | |
74 files in an architecture-specific directory | |
75 when generating URLs. | |
76 """ % env.getone('SCRIPT_NAME') | |
77 | |
78 def AddUrlForAllArches(json_dict, shortname): | |
79 KNOWN_NMF_ARCHES = ['x86-32', 'x86-64', 'arm'] | |
80 for arch in KNOWN_NMF_ARCHES: | |
81 json_dict[arch] = {} | |
82 if env.getbool('FLAT_URL_SCHEME'): | |
83 json_dict[arch]['url'] = '%s' % shortname | |
84 else: | |
85 json_dict[arch]['url'] = 'lib-%s/%s' % (arch, shortname) | |
86 | |
87 def SetPortableUrl(json_dict, shortname, filepath): | |
88 json_dict['portable'] = {} | |
89 json_dict['portable']['pnacl-translate'] = {} | |
90 json_dict['portable']['pnacl-translate']['url'] = shortname | |
91 fd = DriverOpen(filepath, 'r') | |
92 sha = hashlib.sha256() | |
93 sha.update(fd.read()) | |
94 fd.close() | |
95 json_dict['portable']['pnacl-translate']['sha256'] = sha.hexdigest() | |
96 | |
97 def GetActualFilePathAndType(shortname): | |
98 actual_lib_path = ldtools.FindFile([shortname], | |
99 env.get('SEARCH_DIRS'), | |
100 ldtools.LibraryTypes.ANY) | |
101 if actual_lib_path == None: | |
102 Log.Warning('Could not find path of lib: %s, assuming it is native', | |
103 shortname) | |
104 file_type = 'so' | |
105 else: | |
106 file_type = filetype.FileType(actual_lib_path) | |
107 return actual_lib_path, file_type | |
108 | |
109 def GetTransitiveClosureOfNeeded(base_needed): | |
110 visited_portable = set() | |
111 visited_native = set() | |
112 all_needed = set(base_needed) | |
113 worklist = deque(base_needed) | |
114 while len(worklist) != 0: | |
115 lib = worklist.popleft() | |
116 if lib in visited_portable or lib in visited_native: | |
117 continue | |
118 actual_lib_path, file_type = GetActualFilePathAndType(lib) | |
119 if file_type == 'so': | |
120 # We could trace if we used objdump / readelf... | |
121 Log.Warning('Not tracing dependencies of native lib %s', lib) | |
122 visited_native.add(lib) | |
123 continue | |
124 elif file_type == 'pso': | |
125 visited_portable.add(lib) | |
126 more_needed = filetype.GetBitcodeMetadata( | |
127 actual_lib_path).get('NeedsLibrary', []) | |
128 all_needed |= set(more_needed) | |
129 worklist.extend(more_needed) | |
130 else: | |
131 Log.Fatal('Lib: %s is neither bitcode nor native', lib) | |
132 return all_needed | |
133 | |
134 def ReadBaselineNMF(baseline_file): | |
135 f = DriverOpen(baseline_file, 'r') | |
136 json_dict = json.load(f) | |
137 f.close() | |
138 return json_dict | |
139 | |
140 def EchoBaselineNMF(baseline_nmf, output_file): | |
141 json.dump(baseline_nmf, output_file, sort_keys=True, indent=2) | |
142 | |
143 def GenerateStaticNMF(pexe_file, baseline_nmf, output_file): | |
144 nmf_json = baseline_nmf | |
145 nmf_json['program'] = {} | |
146 SetPortableUrl(nmf_json['program'], | |
147 pathtools.basename(pexe_file), | |
148 pexe_file) | |
149 json.dump(nmf_json, output_file, sort_keys=True, indent=2) | |
150 | |
151 | |
152 def GenerateDynamicNMF(pexe_file, baseline_nmf, needed, output_file): | |
153 nmf_json = baseline_nmf | |
154 # Set runnable-ld.so as the program interpreter. | |
155 nmf_json['program'] = {} | |
156 AddUrlForAllArches(nmf_json['program'], 'runnable-ld.so') | |
157 # Set the pexe as the main program. | |
158 nmf_json['files'] = baseline_nmf.get('files', {}) | |
159 nmf_json['files']['main.nexe'] = {} | |
160 SetPortableUrl(nmf_json['files']['main.nexe'], | |
161 pathtools.basename(pexe_file), | |
162 pexe_file) | |
163 | |
164 # Get transitive closure of needed libraries. | |
165 transitive_needed = GetTransitiveClosureOfNeeded(needed) | |
166 | |
167 # Make urls for libraries. | |
168 for lib in transitive_needed: | |
169 nmf_json['files'][lib] = {} | |
170 actual_lib_path, file_type = GetActualFilePathAndType(lib) | |
171 if file_type == 'so': | |
172 # Assume a native version exists for every known arch. | |
173 AddUrlForAllArches(nmf_json['files'][lib], lib) | |
174 elif file_type == 'pso': | |
175 SetPortableUrl(nmf_json['files'][lib], | |
176 lib, | |
177 actual_lib_path) | |
178 else: | |
179 Log.Fatal('Needed library is not a .so nor a .pso') | |
180 json.dump(nmf_json, output_file, sort_keys=True, indent=2) | |
181 | |
182 | |
183 def main(argv): | |
184 env.update(EXTRA_ENV) | |
185 ParseArgs(argv, NMF_PATTERNS) | |
186 exe_file = env.getone('INPUTS') | |
187 | |
188 if not exe_file: | |
189 Log.Fatal('Expecting input exe_file') | |
190 DriverExit(1) | |
191 | |
192 baseline_file = env.getone('BASELINE_NMF') | |
193 if not baseline_file: | |
194 baseline_nmf = {} | |
195 else: | |
196 baseline_nmf = ReadBaselineNMF(baseline_file) | |
197 | |
198 output = env.getone('OUTPUT') | |
199 if output == '': | |
200 output_file = sys.stdout | |
201 else: | |
202 output_file = DriverOpen(output, 'w') | |
203 | |
204 if not FileType(exe_file) == 'pexe': | |
205 EchoBaselineNMF(baseline_nmf, output_file) | |
206 return 0 | |
207 needed = GetBitcodeMetadata(exe_file).get('NeedsLibrary', []) | |
208 if len(needed) == 0: | |
209 GenerateStaticNMF(exe_file, baseline_nmf, output_file) | |
210 else: | |
211 # runnable_ld.so is going to ask for libgcc_s.so.1 even though it does | |
212 # not show up in the pexe's NEEDED metadata, so it won't show up | |
213 # in the NMF and on the App's webserver. | |
214 # | |
215 # For now, hack in libgcc_s.so.1. | |
216 # http://code.google.com/p/nativeclient/issues/detail?id=2582 | |
217 needed.append('libgcc_s.so.1') | |
218 GenerateDynamicNMF(exe_file, baseline_nmf, needed, output_file) | |
219 DriverClose(output_file) | |
220 return 0 | |
OLD | NEW |