Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Side by Side Diff: native_client_sdk/src/tools/create_nmf.py

Issue 9234043: Support GLBIC example. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 from __future__ import with_statement 6 from __future__ import with_statement
7 7
8 import errno 8 import errno
9 import optparse 9 import optparse
10 import os 10 import os
(...skipping 11 matching lines...) Expand all
22 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') 22 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$')
23 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') 23 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$')
24 24
25 FORMAT_ARCH_MAP = { 25 FORMAT_ARCH_MAP = {
26 # Names returned by Linux's objdump: 26 # Names returned by Linux's objdump:
27 'elf64-x86-64': 'x86-64', 27 'elf64-x86-64': 'x86-64',
28 'elf32-i386': 'x86-32', 28 'elf32-i386': 'x86-32',
29 # Names returned by x86_64-nacl-objdump: 29 # Names returned by x86_64-nacl-objdump:
30 'elf64-nacl': 'x86-64', 30 'elf64-nacl': 'x86-64',
31 'elf32-nacl': 'x86-32', 31 'elf32-nacl': 'x86-32',
32 # TODO(mball): Add support for 'arm-32' and 'portable' architectures
33 # 'elf32-little': 'arm-32',
34 } 32 }
35 33
34 ARCH_LOCATION = {
35 'x86-32': 'lib32',
36 'x86-64': 'lib64',
37 }
38
36 # These constants are used within nmf files. 39 # These constants are used within nmf files.
37 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader 40 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
38 MAIN_NEXE = 'main.nexe' # Name of entry point for execution 41 MAIN_NEXE = 'main.nexe' # Name of entry point for execution
39 PROGRAM_KEY = 'program' # Key of the program section in an nmf file 42 PROGRAM_KEY = 'program' # Key of the program section in an nmf file
40 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file 43 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file
41 FILES_KEY = 'files' # Key of the files section in an nmf file 44 FILES_KEY = 'files' # Key of the files section in an nmf file
42 45
43 # The proper name of the dynamic linker, as kept in the IRT. This is 46 # The proper name of the dynamic linker, as kept in the IRT. This is
44 # excluded from the nmf file by convention. 47 # excluded from the nmf file by convention.
45 LD_NACL_MAP = { 48 LD_NACL_MAP = {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 both for staging the libraries and for inclusion into the nmf file. 106 both for staging the libraries and for inclusion into the nmf file.
104 Examples: ['..'], ['lib_dir'] ''' 107 Examples: ['..'], ['lib_dir'] '''
105 self.objdump = objdump 108 self.objdump = objdump
106 self.main_files = main_files or [] 109 self.main_files = main_files or []
107 self.extra_files = extra_files or [] 110 self.extra_files = extra_files or []
108 self.lib_path = lib_path or [] 111 self.lib_path = lib_path or []
109 self.manifest = None 112 self.manifest = None
110 self.needed = None 113 self.needed = None
111 self.lib_prefix = lib_prefix or [] 114 self.lib_prefix = lib_prefix or []
112 115
116
113 def GleanFromObjdump(self, files): 117 def GleanFromObjdump(self, files):
114 '''Get architecture and dependency information for given files 118 '''Get architecture and dependency information for given files
115 119
116 Args: 120 Args:
117 files: A dict with key=filename and value=list or set of archs. E.g.: 121 files: A dict with key=filename and value=list or set of archs. E.g.:
118 { '/path/to/my.nexe': ['x86-32', 'x86-64'], 122 { '/path/to/my.nexe': ['x86-32']
119 '/path/to/libmy.so': ['x86-32'], 123 '/path/to/lib64/libmy.so': ['x86-64'],
120 '/path/to/my2.nexe': None } # Indicates all architectures 124 '/path/to/mydata.so': ['x86-32', 'x86-64'],
125 '/path/to/my.data': None } # Indicates all architectures
121 126
122 Returns: A tuple with the following members: 127 Returns: A tuple with the following members:
123 input_info: A dict with key=filename and value=ArchFile of input files. 128 input_info: A dict with key=filename and value=ArchFile of input files.
124 Includes the input files as well, with arch filled in if absent. 129 Includes the input files as well, with arch filled in if absent.
125 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 130 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
126 '/path/to/libfoo.so': ArchFile(libfoo.so) } 131 '/path/to/libfoo.so': ArchFile(libfoo.so) }
127 needed: A set of strings formatted as "arch/name". Example: 132 needed: A set of strings formatted as "arch/name". Example:
128 set(['x86-32/libc.so', 'x86-64/libgcc.so']) 133 set(['x86-32/libc.so', 'x86-64/libgcc.so'])
129 ''' 134 '''
130 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys())) 135 DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys()))
131 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(), 136 proc = subprocess.Popen([self.objdump, '-p'] + files.keys(),
132 stdout=subprocess.PIPE, 137 stdout=subprocess.PIPE,
133 stderr=subprocess.PIPE, bufsize=-1) 138 stderr=subprocess.PIPE, bufsize=-1)
134 input_info = {} 139 input_info = {}
135 needed = set() 140 needed = set()
136 output, err_output = proc.communicate() 141 output, err_output = proc.communicate()
137 for line in output.splitlines(True): 142 for line in output.splitlines(True):
138 # Objdump should display the architecture first and then the dependencies 143 # Objdump should display the architecture first and then the dependencies
139 # second for each file in the list. 144 # second for each file in the list.
140 matched = FormatMatcher.match(line) 145 matched = FormatMatcher.match(line)
141 if matched is not None: 146 if matched is not None:
142 filename = matched.group(1) 147 filename = matched.group(1)
143 arch = FORMAT_ARCH_MAP[matched.group(2)] 148 arch = FORMAT_ARCH_MAP[matched.group(2)]
144 if files[filename] is None or arch in files[filename]: 149 if files[filename] is None or arch in files[filename]:
145 name = os.path.basename(filename) 150 name = os.path.basename(filename)
146 input_info[filename] = ArchFile( 151 input_info[filename] = ArchFile(
147 arch=arch, 152 arch=arch,
148 name=name, 153 name=name,
149 path=filename, 154 path=filename,
150 url='/'.join(self.lib_prefix + [arch, name])) 155 url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name]))
151 matched = NeededMatcher.match(line) 156 matched = NeededMatcher.match(line)
152 if matched is not None: 157 if matched is not None:
153 if files[filename] is None or arch in files[filename]: 158 if files[filename] is None or arch in files[filename]:
154 needed.add('/'.join([arch, matched.group(1)])) 159 needed.add('/'.join([arch, matched.group(1)]))
155 status = proc.poll() 160 status = proc.poll()
156 if status != 0: 161 if status != 0:
157 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % 162 raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' %
158 (output, err_output, status)) 163 (output, err_output, status))
159 return input_info, needed 164 return input_info, needed
160 165
(...skipping 21 matching lines...) Expand all
182 A dict with key=filename and value=ArchFile of input files. 187 A dict with key=filename and value=ArchFile of input files.
183 Includes the input files as well, with arch filled in if absent. 188 Includes the input files as well, with arch filled in if absent.
184 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 189 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
185 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' 190 '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
186 if not self.needed: 191 if not self.needed:
187 DebugPrint('GetNeeded(%s)' % self.main_files) 192 DebugPrint('GetNeeded(%s)' % self.main_files)
188 examined = set() 193 examined = set()
189 all_files, unexamined = self.GleanFromObjdump( 194 all_files, unexamined = self.GleanFromObjdump(
190 dict([(file, None) for file in self.main_files])) 195 dict([(file, None) for file in self.main_files]))
191 for name, arch_file in all_files.items(): 196 for name, arch_file in all_files.items():
192 arch_file.url = os.path.basename(name) 197 arch_file.url = name
193 if unexamined: 198 if unexamined:
194 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) 199 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
195 while unexamined: 200 while unexamined:
196 files_to_examine = {} 201 files_to_examine = {}
197 for arch_name in unexamined: 202 for arch_name in unexamined:
198 arch, name = arch_name.split('/') 203 arch, name = arch_name.split('/')
199 for path in self.FindLibsInPath(name): 204 for path in self.FindLibsInPath(name):
200 files_to_examine.setdefault(path, set()).add(arch) 205 files_to_examine.setdefault(path, set()).add(arch)
201 new_files, needed = self.GleanFromObjdump(files_to_examine) 206 new_files, needed = self.GleanFromObjdump(files_to_examine)
202 all_files.update(new_files) 207 all_files.update(new_files)
(...skipping 22 matching lines...) Expand all
225 urllib.url2pathname(arch_file.url)) 230 urllib.url2pathname(arch_file.url))
226 try: 231 try:
227 os.makedirs(os.path.dirname(destination)) 232 os.makedirs(os.path.dirname(destination))
228 except OSError as exception_info: 233 except OSError as exception_info:
229 if exception_info.errno != errno.EEXIST: 234 if exception_info.errno != errno.EEXIST:
230 raise 235 raise
231 if (os.path.normcase(os.path.abspath(source)) != 236 if (os.path.normcase(os.path.abspath(source)) !=
232 os.path.normcase(os.path.abspath(destination))): 237 os.path.normcase(os.path.abspath(destination))):
233 shutil.copy2(source, destination) 238 shutil.copy2(source, destination)
234 239
235 def _GenerateManifest(self): 240 def _GenerateManifest(self, runnable=True):
236 programs = {} 241 '''Create a JSON formatted dict containing the files
237 files = {} 242
243 NaCl will map url requests based on architecture. The startup NEXE
244 can always be found under the top key PROGRAM. Additional files are under
245 the FILES key further mapped by file name. In the case of 'runnable' the
246 PROGRAM key is populated with urls pointing the runnable-ld.so which acts
247 as the startup nexe. The application itself, is then placed under the
248 FILES key mapped as 'main.exe' instead of it's original name so that the
249 loader can find it.'''
250 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
251 needed = self.GetNeeded()
238 252
239 def add_files(needed): 253 for need in needed:
240 for filename, arch_file in needed.items(): 254 archinfo = needed[need]
241 files.setdefault(arch_file.arch, set()).add(arch_file.name) 255 urlinfo = { URL_KEY: archinfo.url }
256 name = archinfo.name
242 257
243 needed = self.GetNeeded() 258 # If starting with runnable-ld.so, make that the main executable.
244 add_files(needed) 259 if runnable:
260 if need.endswith(RUNNABLE_LD):
261 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
262 continue
245 263
246 for filename in self.main_files: 264 # For the main nexes:
247 arch_file = needed[filename] 265 if need.endswith('.nexe') and need in self.main_files:
248 programs[arch_file.arch] = arch_file.name 266 # Place it under program if we aren't using the runnable-ld.so.
267 if not runnable:
268 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
269 continue
270 # Otherwise, treat it like another another file named main.nexe.
271 name = MAIN_NEXE
249 272
250 filemap = {} 273 fileinfo = manifest[FILES_KEY].get(name, {})
251 for arch in files: 274 fileinfo[archinfo.arch] = urlinfo
252 for file in files[arch]: 275 manifest[FILES_KEY][name] = fileinfo
253 if file not in programs.values() and file != RUNNABLE_LD:
254 filemap.setdefault(file, set()).add(arch)
255 276
256 def arch_name(arch, file):
257 # nmf files expect unix-style path separators
258 return {URL_KEY: '/'.join(self.lib_prefix + [arch, file])}
259
260 # TODO(mcgrathr): perhaps notice a program with no deps
261 # (i.e. statically linked) and generate program=nexe instead?
262 manifest = {PROGRAM_KEY: {}, FILES_KEY: {MAIN_NEXE: {}}}
263 for arch in programs:
264 manifest[PROGRAM_KEY][arch] = arch_name(arch, RUNNABLE_LD)
265 manifest[FILES_KEY][MAIN_NEXE][arch] = {URL_KEY: programs[arch]}
266
267 for file in filemap:
268 manifest[FILES_KEY][file] = dict([(arch, arch_name(arch, file))
269 for arch in filemap[file]])
270 self.manifest = manifest 277 self.manifest = manifest
271 278
272 def GetManifest(self): 279 def GetManifest(self):
273 '''Returns a JSON-formatted dict containing the NaCl dependencies''' 280 '''Returns a JSON-formatted dict containing the NaCl dependencies'''
274 if not self.manifest: 281 if not self.manifest:
275 self._GenerateManifest() 282 self._GenerateManifest()
276 283
277 return self.manifest 284 return self.manifest
278 285
279 def GetJson(self): 286 def GetJson(self):
(...skipping 14 matching lines...) Expand all
294 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', 301 parser.add_option('-D', '--objdump', dest='objdump', default='objdump',
295 help='Use TOOL as the "objdump" tool to run', 302 help='Use TOOL as the "objdump" tool to run',
296 metavar='TOOL') 303 metavar='TOOL')
297 parser.add_option('-L', '--library-path', dest='lib_path', 304 parser.add_option('-L', '--library-path', dest='lib_path',
298 action='append', default=[], 305 action='append', default=[],
299 help='Add DIRECTORY to library search path', 306 help='Add DIRECTORY to library search path',
300 metavar='DIRECTORY') 307 metavar='DIRECTORY')
301 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', 308 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
302 help='Destination directory for staging libraries', 309 help='Destination directory for staging libraries',
303 metavar='DIRECTORY') 310 metavar='DIRECTORY')
311 parser.add_option('-r', '--remove', dest='remove',
312 help='Remove the prefix from the files.',
313 metavar='PATH')
304 (options, args) = parser.parse_args(argv) 314 (options, args) = parser.parse_args(argv)
305 315
306 if len(args) < 1: 316 if len(args) < 1:
307 parser.print_usage() 317 parser.print_usage()
308 sys.exit(1) 318 sys.exit(1)
309 319
310 nmf = NmfUtils(objdump=options.objdump, 320 nmf = NmfUtils(objdump=options.objdump,
311 main_files=args, 321 main_files=args,
312 lib_path=options.lib_path) 322 lib_path=options.lib_path)
313 323
314 manifest = nmf.GetManifest() 324 manifest = nmf.GetManifest()
315 325
316 if options.output is None: 326 if options.output is None:
317 sys.stdout.write(nmf.GetJson()) 327 sys.stdout.write(nmf.GetJson())
318 else: 328 else:
319 with open(options.output, 'w') as output: 329 with open(options.output, 'w') as output:
320 output.write(nmf.GetJson()) 330 output.write(nmf.GetJson())
321 331
322 if options.stage_dependencies: 332 if options.stage_dependencies:
323 nmf.StageDependencies(options.stage_dependencies) 333 nmf.StageDependencies(options.stage_dependencies)
324 334
325 335
326 # Invoke this file directly for simple testing. 336 # Invoke this file directly for simple testing.
327 if __name__ == '__main__': 337 if __name__ == '__main__':
328 sys.exit(Main(sys.argv[1:])) 338 sys.exit(Main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698