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

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

Issue 10837028: create_nmf improvements (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 } 32 }
33 33
34 ARCH_LOCATION = { 34 ARCH_LOCATION = {
35 'x86-32': 'lib32', 35 'x86-32': 'lib32',
36 'x86-64': 'lib64', 36 'x86-64': 'lib64',
37 } 37 }
38 38
39 NAME_ARCH_MAP = { 39 NAME_ARCH_MAP = {
40 '32.nexe': 'x86-32', 40 '32.nexe': 'x86-32',
41 '64.nexe': 'x86-64', 41 '64.nexe': 'x86-64',
42 'arm.nexe': 'arm' 42 'arm.nexe': 'arm'
(...skipping 19 matching lines...) Expand all
62 def DebugPrint(message): 62 def DebugPrint(message):
63 if _debug_mode: 63 if _debug_mode:
64 sys.stderr.write('%s\n' % message) 64 sys.stderr.write('%s\n' % message)
65 sys.stderr.flush() 65 sys.stderr.flush()
66 66
67 67
68 class Error(Exception): 68 class Error(Exception):
69 '''Local Error class for this file.''' 69 '''Local Error class for this file.'''
70 pass 70 pass
71 71
72
73 class ArchFile(object): 72 class ArchFile(object):
74 '''Simple structure containing information about 73 '''Simple structure containing information about
75 74
76 Attributes: 75 Attributes:
77 arch: Architecture of this file (e.g., x86-32) 76 arch: Architecture of this file (e.g., x86-32)
78 filename: name of this file 77 filename: name of this file
79 path: Full path to this file on the build system 78 path: Full path to this file on the build system
80 url: Relative path to file in the staged web directory. 79 url: Relative path to file in the staged web directory.
81 Used for specifying the "url" attribute in the nmf file.''' 80 Used for specifying the "url" attribute in the nmf file.'''
82 def __init__(self, arch, name, path='', url=None): 81 def __init__(self, arch, name, path='', url=None):
83 self.arch = arch 82 self.arch = arch
84 self.name = name 83 self.name = name
85 self.path = path 84 self.path = path
86 self.url = url or '/'.join([arch, name]) 85 self.url = url or '/'.join([arch, name])
87 86
87 def __repr__(self):
88 return "ArchFile: %s" % self.path
binji 2012/08/01 04:06:46 repr(foo) is usually "<foo ...>"
Sam Clegg 2012/08/01 17:21:03 Done.
89
88 def __str__(self): 90 def __str__(self):
89 '''Return the file path when invoked with the str() function''' 91 '''Return the file path when invoked with the str() function'''
90 return self.path 92 return self.path
91 93
92 94
93 class NmfUtils(object): 95 class NmfUtils(object):
94 '''Helper class for creating and managing nmf files 96 '''Helper class for creating and managing nmf files
95 97
96 Attributes: 98 Attributes:
97 manifest: A JSON-structured dict containing the nmf structure 99 manifest: A JSON-structured dict containing the nmf structure
98 needed: A dict with key=filename and value=ArchFile (see GetNeeded) 100 needed: A dict with key=filename and value=ArchFile (see GetNeeded)
99 ''' 101 '''
100 102
101 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump', 103 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump',
102 lib_path=None, extra_files=None, lib_prefix=None, 104 lib_path=None, extra_files=None, lib_prefix=None,
103 toolchain=None, remap={}): 105 toolchain=None, remap={}):
104 ''' Constructor 106 '''Constructor
105 107
106 Args: 108 Args:
107 main_files: List of main entry program files. These will be named 109 main_files: List of main entry program files. These will be named
108 files->main.nexe for dynamic nexes, and program for static nexes 110 files->main.nexe for dynamic nexes, and program for static nexes
109 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent) 111 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
110 lib_path: List of paths to library directories 112 lib_path: List of paths to library directories
111 extra_files: List of extra files to include in the nmf 113 extra_files: List of extra files to include in the nmf
112 lib_prefix: A list of path components to prepend to the library paths, 114 lib_prefix: A list of path components to prepend to the library paths,
113 both for staging the libraries and for inclusion into the nmf file. 115 both for staging the libraries and for inclusion into the nmf file.
114 Examples: ['..'], ['lib_dir'] 116 Examples: ['..'], ['lib_dir']
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 Returns: 201 Returns:
200 A dict with key=filename and value=ArchFile of input files. 202 A dict with key=filename and value=ArchFile of input files.
201 Includes the input files as well, with arch filled in if absent. 203 Includes the input files as well, with arch filled in if absent.
202 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 204 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
203 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' 205 '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
204 if self.needed: 206 if self.needed:
205 return self.needed 207 return self.needed
206 208
207 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 209 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
208 DebugPrint('GetNeeded(%s)' % self.main_files) 210 DebugPrint('GetNeeded(%s)' % self.main_files)
209 if runnable: 211 if runnable:
210 examined = set() 212 examined = set()
211 all_files, unexamined = self.GleanFromObjdump( 213 all_files, unexamined = self.GleanFromObjdump(
212 dict([(file, None) for file in self.main_files])) 214 dict([(file, None) for file in self.main_files]))
213 for name, arch_file in all_files.items(): 215 for name, arch_file in all_files.items():
214 arch_file.url = name 216 arch_file.url = name
215 if unexamined: 217 if unexamined:
216 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) 218 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
217 while unexamined: 219 while unexamined:
218 files_to_examine = {} 220 files_to_examine = {}
219 for arch_name in unexamined: 221 for arch_name in unexamined:
(...skipping 12 matching lines...) Expand all
232 del all_files[name] 234 del all_files[name]
233 self.needed = all_files 235 self.needed = all_files
234 else: 236 else:
235 need = {} 237 need = {}
236 for filename in self.main_files: 238 for filename in self.main_files:
237 arch = filename.split('_')[-1] 239 arch = filename.split('_')[-1]
238 arch = NAME_ARCH_MAP[arch] 240 arch = NAME_ARCH_MAP[arch]
239 url = os.path.split(filename)[1] 241 url = os.path.split(filename)[1]
240 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename), 242 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename),
241 path=filename, url=url) 243 path=filename, url=url)
242 self.needed = need 244 self.needed = need
245
243 return self.needed 246 return self.needed
244 247
245 def StageDependencies(self, destination_dir): 248 def StageDependencies(self, destination_dir):
246 '''Copies over the dependencies into a given destination directory 249 '''Copies over the dependencies into a given destination directory
247 250
248 Each library will be put into a subdirectory that corresponds to the arch. 251 Each library will be put into a subdirectory that corresponds to the arch.
249 252
250 Args: 253 Args:
251 destination_dir: The destination directory for staging the dependencies 254 destination_dir: The destination directory for staging the dependencies
252 ''' 255 '''
253 needed = self.GetNeeded() 256 needed = self.GetNeeded()
254 for source, arch_file in needed.items(): 257 for source, arch_file in needed.items():
255 destination = os.path.join(destination_dir, 258 urldest = urllib.url2pathname(arch_file.url)
256 urllib.url2pathname(arch_file.url)) 259 if source.endswith('.nexe') and source in self.main_files:
binji 2012/08/01 04:06:46 I don't think we should make any determination bas
Sam Clegg 2012/08/01 17:21:03 I'm only mimicking what _GenerateManifest does. T
binji 2012/08/01 18:05:20 ok
257 try: 260 urldest = os.path.basename(urldest)
258 os.makedirs(os.path.dirname(destination)) 261
259 except OSError as exception_info: 262 destination = os.path.join(destination_dir, urldest)
260 if exception_info.errno != errno.EEXIST: 263
261 raise 264 # make sure target dir exists
265 dirname = os.path.dirname(destination)
266 if not os.path.exists(dirname):
binji 2012/08/01 04:06:46 IMO, catching the exception is better. Testing for
binji 2012/08/01 18:05:20 opinion on this?
Sam Clegg 2012/08/01 18:26:39 Well.. it pretty much guarantees that it won't fai
267 Trace("mkdir: %s" % dirname)
268 os.makedirs(dirname)
269
262 if (os.path.normcase(os.path.abspath(source)) != 270 if (os.path.normcase(os.path.abspath(source)) !=
263 os.path.normcase(os.path.abspath(destination))): 271 os.path.normcase(os.path.abspath(destination))):
272 Trace("copy: %s -> %s" % (source, destination))
264 shutil.copy2(source, destination) 273 shutil.copy2(source, destination)
265 274
266 def _GenerateManifest(self): 275 def _GenerateManifest(self):
267 '''Create a JSON formatted dict containing the files 276 '''Create a JSON formatted dict containing the files
268 277
269 NaCl will map url requests based on architecture. The startup NEXE 278 NaCl will map url requests based on architecture. The startup NEXE
270 can always be found under the top key PROGRAM. Additional files are under 279 can always be found under the top key PROGRAM. Additional files are under
271 the FILES key further mapped by file name. In the case of 'runnable' the 280 the FILES key further mapped by file name. In the case of 'runnable' the
272 PROGRAM key is populated with urls pointing the runnable-ld.so which acts 281 PROGRAM key is populated with urls pointing the runnable-ld.so which acts
273 as the startup nexe. The application itself, is then placed under the 282 as the startup nexe. The application itself, is then placed under the
274 FILES key mapped as 'main.exe' instead of it's original name so that the 283 FILES key mapped as 'main.exe' instead of it's original name so that the
275 loader can find it.''' 284 loader can find it.'''
276 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } 285 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
277 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 286 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
278 287
279 needed = self.GetNeeded() 288 needed = self.GetNeeded()
280 for need in needed: 289 for need, archinfo in needed.items():
281 archinfo = needed[need]
282 urlinfo = { URL_KEY: archinfo.url } 290 urlinfo = { URL_KEY: archinfo.url }
283 name = archinfo.name 291 name = archinfo.name
284 292
285 # If starting with runnable-ld.so, make that the main executable. 293 # If starting with runnable-ld.so, make that the main executable.
286 if runnable: 294 if runnable:
287 if need.endswith(RUNNABLE_LD): 295 if need.endswith(RUNNABLE_LD):
288 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo 296 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
289 continue 297 continue
290 298
291 # For the main nexes: 299 # For the main nexes:
(...skipping 24 matching lines...) Expand all
316 324
317 def GetJson(self): 325 def GetJson(self):
318 '''Returns the Manifest as a JSON-formatted string''' 326 '''Returns the Manifest as a JSON-formatted string'''
319 pretty_string = json.dumps(self.GetManifest(), indent=2) 327 pretty_string = json.dumps(self.GetManifest(), indent=2)
320 # json.dumps sometimes returns trailing whitespace and does not put 328 # json.dumps sometimes returns trailing whitespace and does not put
321 # a newline at the end. This code fixes these problems. 329 # a newline at the end. This code fixes these problems.
322 pretty_lines = pretty_string.split('\n') 330 pretty_lines = pretty_string.split('\n')
323 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' 331 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
324 332
325 333
326 def ErrorOut(text):
327 sys.stderr.write(text + '\n')
328 sys.exit(1)
329
330
331 def DetermineToolchain(objdump): 334 def DetermineToolchain(objdump):
332 objdump = objdump.replace('\\', '/') 335 objdump = objdump.replace('\\', '/')
333 paths = objdump.split('/') 336 paths = objdump.split('/')
334 count = len(paths) 337 count = len(paths)
335 for index in range(count - 2, 0, -1): 338 for index in range(count - 2, 0, -1):
336 if paths[index] == 'toolchain': 339 if paths[index] == 'toolchain':
337 if paths[index + 1].endswith('newlib'): 340 if paths[index + 1].endswith('newlib'):
338 return 'newlib' 341 return 'newlib'
339 if paths[index + 1].endswith('glibc'): 342 if paths[index + 1].endswith('glibc'):
340 return 'glibc' 343 return 'glibc'
341 ErrorOut('Could not deternime which toolchain to use.') 344 raise Error('Could not deternime which toolchain to use.')
342 345
binji 2012/08/01 04:06:46 style: Two newlines between top-level definitions.
Sam Clegg 2012/08/01 17:21:03 Done.
346 def Trace(msg):
347 if Trace.verbose:
348 sys.stderr.write(str(msg) + '\n')
349
350 Trace.verbose = False
343 351
344 def Main(argv): 352 def Main(argv):
345 parser = optparse.OptionParser( 353 parser = optparse.OptionParser(
346 usage='Usage: %prog [options] nexe [extra_libs...]') 354 usage='Usage: %prog [options] nexe [extra_libs...]')
347 parser.add_option('-o', '--output', dest='output', 355 parser.add_option('-o', '--output', dest='output',
348 help='Write manifest file to FILE (default is stdout)', 356 help='Write manifest file to FILE (default is stdout)',
349 metavar='FILE') 357 metavar='FILE')
350 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', 358 parser.add_option('-D', '--objdump', dest='objdump', default='objdump',
351 help='Use TOOL as the "objdump" tool to run', 359 help='Use TOOL as the "objdump" tool to run',
352 metavar='TOOL') 360 metavar='TOOL')
353 parser.add_option('-L', '--library-path', dest='lib_path', 361 parser.add_option('-L', '--library-path', dest='lib_path',
354 action='append', default=[], 362 action='append', default=[],
355 help='Add DIRECTORY to library search path', 363 help='Add DIRECTORY to library search path',
356 metavar='DIRECTORY') 364 metavar='DIRECTORY')
357 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', 365 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
358 help='Destination directory for staging libraries', 366 help='Destination directory for staging libraries',
359 metavar='DIRECTORY') 367 metavar='DIRECTORY')
360 parser.add_option('-r', '--remove', dest='remove', 368 parser.add_option('-r', '--remove', dest='remove',
361 help='Remove the prefix from the files.', 369 help='Remove the prefix from the files.',
362 metavar='PATH') 370 metavar='PATH')
363 parser.add_option('-t', '--toolchain', dest='toolchain', 371 parser.add_option('-t', '--toolchain', dest='toolchain',
364 help='Add DIRECTORY to library search path', 372 help='Add DIRECTORY to library search path',
365 default=None, metavar='TOOLCHAIN') 373 default=None, metavar='TOOLCHAIN')
366 parser.add_option('-n', '--name', dest='name', 374 parser.add_option('-n', '--name', dest='name',
367 help='Rename FOO as BAR', 375 help='Rename FOO as BAR',
368 action='append', default=[], metavar='FOO,BAR') 376 action='append', default=[], metavar='FOO,BAR')
377 parser.add_option('-v', '--verbose',
378 help='Verbose output', action='store_true')
369 (options, args) = parser.parse_args(argv) 379 (options, args) = parser.parse_args(argv)
370 380 if options.verbose:
381 Trace.verbose = True
382
383 if len(args) < 1:
384 raise Error("No nexe files specified. See --help for more info")
binji 2012/08/01 04:06:46 maybe just display help in this case?
Sam Clegg 2012/08/01 17:21:03 The convention seems to be to tell the user what h
binji 2012/08/01 18:05:20 ok
385
386 remap = {}
binji 2012/08/01 04:06:46 I prefer initialization closer to first use.
Sam Clegg 2012/08/01 17:21:03 Ooops. Didn't mean to move that. Done.
371 if not options.toolchain: 387 if not options.toolchain:
372 options.toolchain = DetermineToolchain(os.path.abspath(options.objdump)) 388 options.toolchain = DetermineToolchain(os.path.abspath(options.objdump))
373 389
374 if options.toolchain not in ['newlib', 'glibc', 'pnacl']: 390 if options.toolchain not in ['newlib', 'glibc', 'pnacl']:
375 ErrorOut('Unknown toolchain: ' + str(options.toolchain)) 391 raise Error('Unknown toolchain: ' + str(options.toolchain))
376 392
377 if len(args) < 1:
378 parser.print_usage()
379 sys.exit(1)
380
381 remap = {}
382 for ren in options.name: 393 for ren in options.name:
383 parts = ren.split(',') 394 parts = ren.split(',')
384 if len(parts) != 2: 395 if len(parts) != 2:
385 ErrorOut('Expecting --name=<orig_arch.so>,<new_name.so>') 396 raise Error('Expecting --name=<orig_arch.so>,<new_name.so>')
386 remap[parts[0]] = parts[1] 397 remap[parts[0]] = parts[1]
387 398
388 nmf = NmfUtils(objdump=options.objdump, 399 nmf = NmfUtils(objdump=options.objdump,
389 main_files=args, 400 main_files=args,
390 lib_path=options.lib_path, 401 lib_path=options.lib_path,
391 toolchain=options.toolchain, 402 toolchain=options.toolchain,
392 remap=remap) 403 remap=remap)
393 404
394 manifest = nmf.GetManifest() 405 manifest = nmf.GetManifest()
395 if options.output is None: 406 if options.output is None:
396 sys.stdout.write(nmf.GetJson()) 407 sys.stdout.write(nmf.GetJson())
397 else: 408 else:
398 with open(options.output, 'w') as output: 409 with open(options.output, 'w') as output:
399 output.write(nmf.GetJson()) 410 output.write(nmf.GetJson())
400 411
401 if options.stage_dependencies: 412 if options.stage_dependencies:
413 Trace("Staging dependancies...")
binji 2012/08/01 04:06:46 sp: dependencies
Sam Clegg 2012/08/01 17:21:03 Done.
402 nmf.StageDependencies(options.stage_dependencies) 414 nmf.StageDependencies(options.stage_dependencies)
403 415
416 return 0
417
404 418
405 # Invoke this file directly for simple testing. 419 # Invoke this file directly for simple testing.
406 if __name__ == '__main__': 420 if __name__ == '__main__':
407 sys.exit(Main(sys.argv[1:])) 421 try:
422 rtn = Main(sys.argv[1:])
423 except Error, e:
binji 2012/08/01 04:06:46 How does this output differ from the default outpu
Sam Clegg 2012/08/01 17:21:03 When an Error (local class) is thrown it represent
binji 2012/08/01 18:05:20 sgtm
424 print "%s: %s" % (os.path.basename(__file__), e)
425 rtn = 1
426 sys.exit(rtn)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698