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

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: function attributes are not internal to the function so don't have "_" on them 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'
43 } 43 }
44 44
45 # These constants are used within nmf files. 45 # These constants are used within nmf files.
46 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader 46 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
47 MAIN_NEXE = 'main.nexe' # Name of entry point for execution 47 MAIN_NEXE = 'main.nexe' # Name of entry point for execution
48 PROGRAM_KEY = 'program' # Key of the program section in an nmf file 48 PROGRAM_KEY = 'program' # Key of the program section in an nmf file
49 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file 49 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file
50 FILES_KEY = 'files' # Key of the files section in an nmf file 50 FILES_KEY = 'files' # Key of the files section in an nmf file
51 51
52 # The proper name of the dynamic linker, as kept in the IRT. This is 52 # The proper name of the dynamic linker, as kept in the IRT. This is
53 # excluded from the nmf file by convention. 53 # excluded from the nmf file by convention.
54 LD_NACL_MAP = { 54 LD_NACL_MAP = {
55 'x86-32': 'ld-nacl-x86-32.so.1', 55 'x86-32': 'ld-nacl-x86-32.so.1',
56 'x86-64': 'ld-nacl-x86-64.so.1', 56 'x86-64': 'ld-nacl-x86-64.so.1',
57 } 57 }
58 58
59 _debug_mode = False # Set to True to enable extra debug prints 59
60 def DebugPrint(message):
61 if DebugPrint.debug_mode:
62 sys.stderr.write('%s\n' % message)
60 63
61 64
62 def DebugPrint(message): 65 DebugPrint.debug_mode = False # Set to True to enable extra debug prints
63 if _debug_mode: 66
64 sys.stderr.write('%s\n' % message) 67
65 sys.stderr.flush() 68 def MakeDir(dirname):
69 """Just like os.makedirs but doesn't generate errors when dirname
70 already exists.
71 """
72 if os.path.isdir(dirname):
73 return
74
75 Trace("mkdir: %s" % dirname)
76 try:
77 os.makedirs(dirname)
78 except OSError as exception_info:
79 if exception_info.errno != errno.EEXIST:
80 raise
66 81
67 82
68 class Error(Exception): 83 class Error(Exception):
69 '''Local Error class for this file.''' 84 '''Local Error class for this file.'''
70 pass 85 pass
71 86
72 87
73 class ArchFile(object): 88 class ArchFile(object):
74 '''Simple structure containing information about 89 '''Simple structure containing information about
75 90
76 Attributes: 91 Attributes:
77 arch: Architecture of this file (e.g., x86-32) 92 arch: Architecture of this file (e.g., x86-32)
78 filename: name of this file 93 filename: name of this file
79 path: Full path to this file on the build system 94 path: Full path to this file on the build system
80 url: Relative path to file in the staged web directory. 95 url: Relative path to file in the staged web directory.
81 Used for specifying the "url" attribute in the nmf file.''' 96 Used for specifying the "url" attribute in the nmf file.'''
82 def __init__(self, arch, name, path='', url=None): 97 def __init__(self, arch, name, path='', url=None):
83 self.arch = arch 98 self.arch = arch
84 self.name = name 99 self.name = name
85 self.path = path 100 self.path = path
86 self.url = url or '/'.join([arch, name]) 101 self.url = url or '/'.join([arch, name])
87 102
103 def __repr__(self):
104 return "<ArchFile %s>" % self.path
105
88 def __str__(self): 106 def __str__(self):
89 '''Return the file path when invoked with the str() function''' 107 '''Return the file path when invoked with the str() function'''
90 return self.path 108 return self.path
91 109
92 110
93 class NmfUtils(object): 111 class NmfUtils(object):
94 '''Helper class for creating and managing nmf files 112 '''Helper class for creating and managing nmf files
95 113
96 Attributes: 114 Attributes:
97 manifest: A JSON-structured dict containing the nmf structure 115 manifest: A JSON-structured dict containing the nmf structure
98 needed: A dict with key=filename and value=ArchFile (see GetNeeded) 116 needed: A dict with key=filename and value=ArchFile (see GetNeeded)
99 ''' 117 '''
100 118
101 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump', 119 def __init__(self, main_files=None, objdump='x86_64-nacl-objdump',
102 lib_path=None, extra_files=None, lib_prefix=None, 120 lib_path=None, extra_files=None, lib_prefix=None,
103 toolchain=None, remap={}): 121 toolchain=None, remap={}):
104 ''' Constructor 122 '''Constructor
105 123
106 Args: 124 Args:
107 main_files: List of main entry program files. These will be named 125 main_files: List of main entry program files. These will be named
108 files->main.nexe for dynamic nexes, and program for static nexes 126 files->main.nexe for dynamic nexes, and program for static nexes
109 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent) 127 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
110 lib_path: List of paths to library directories 128 lib_path: List of paths to library directories
111 extra_files: List of extra files to include in the nmf 129 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, 130 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. 131 both for staging the libraries and for inclusion into the nmf file.
114 Examples: ['..'], ['lib_dir'] 132 Examples: ['..'], ['lib_dir']
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 Returns: 217 Returns:
200 A dict with key=filename and value=ArchFile of input files. 218 A dict with key=filename and value=ArchFile of input files.
201 Includes the input files as well, with arch filled in if absent. 219 Includes the input files as well, with arch filled in if absent.
202 Example: { '/path/to/my.nexe': ArchFile(my.nexe), 220 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
203 '/path/to/libfoo.so': ArchFile(libfoo.so) }''' 221 '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
204 if self.needed: 222 if self.needed:
205 return self.needed 223 return self.needed
206 224
207 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 225 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
208 DebugPrint('GetNeeded(%s)' % self.main_files) 226 DebugPrint('GetNeeded(%s)' % self.main_files)
209 if runnable: 227 if runnable:
210 examined = set() 228 examined = set()
211 all_files, unexamined = self.GleanFromObjdump( 229 all_files, unexamined = self.GleanFromObjdump(
212 dict([(file, None) for file in self.main_files])) 230 dict([(file, None) for file in self.main_files]))
213 for name, arch_file in all_files.items(): 231 for name, arch_file in all_files.items():
214 arch_file.url = name 232 arch_file.url = name
215 if unexamined: 233 if unexamined:
216 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) 234 unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
217 while unexamined: 235 while unexamined:
218 files_to_examine = {} 236 files_to_examine = {}
219 for arch_name in unexamined: 237 for arch_name in unexamined:
(...skipping 12 matching lines...) Expand all
232 del all_files[name] 250 del all_files[name]
233 self.needed = all_files 251 self.needed = all_files
234 else: 252 else:
235 need = {} 253 need = {}
236 for filename in self.main_files: 254 for filename in self.main_files:
237 arch = filename.split('_')[-1] 255 arch = filename.split('_')[-1]
238 arch = NAME_ARCH_MAP[arch] 256 arch = NAME_ARCH_MAP[arch]
239 url = os.path.split(filename)[1] 257 url = os.path.split(filename)[1]
240 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename), 258 need[filename] = ArchFile(arch=arch, name=os.path.basename(filename),
241 path=filename, url=url) 259 path=filename, url=url)
242 self.needed = need 260 self.needed = need
261
243 return self.needed 262 return self.needed
244 263
245 def StageDependencies(self, destination_dir): 264 def StageDependencies(self, destination_dir):
246 '''Copies over the dependencies into a given destination directory 265 '''Copies over the dependencies into a given destination directory
247 266
248 Each library will be put into a subdirectory that corresponds to the arch. 267 Each library will be put into a subdirectory that corresponds to the arch.
249 268
250 Args: 269 Args:
251 destination_dir: The destination directory for staging the dependencies 270 destination_dir: The destination directory for staging the dependencies
252 ''' 271 '''
253 needed = self.GetNeeded() 272 needed = self.GetNeeded()
254 for source, arch_file in needed.items(): 273 for source, arch_file in needed.items():
255 destination = os.path.join(destination_dir, 274 urldest = urllib.url2pathname(arch_file.url)
256 urllib.url2pathname(arch_file.url)) 275 if source.endswith('.nexe') and source in self.main_files:
257 try: 276 urldest = os.path.basename(urldest)
258 os.makedirs(os.path.dirname(destination)) 277
259 except OSError as exception_info: 278 destination = os.path.join(destination_dir, urldest)
260 if exception_info.errno != errno.EEXIST: 279
261 raise
262 if (os.path.normcase(os.path.abspath(source)) != 280 if (os.path.normcase(os.path.abspath(source)) !=
263 os.path.normcase(os.path.abspath(destination))): 281 os.path.normcase(os.path.abspath(destination))):
282 # make sure target dir exists
283 MakeDir(os.path.dirname(destination))
284
285 Trace("copy: %s -> %s" % (source, destination))
264 shutil.copy2(source, destination) 286 shutil.copy2(source, destination)
265 287
266 def _GenerateManifest(self): 288 def _GenerateManifest(self):
267 '''Create a JSON formatted dict containing the files 289 '''Create a JSON formatted dict containing the files
268 290
269 NaCl will map url requests based on architecture. The startup NEXE 291 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 292 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 293 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 294 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 295 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 296 FILES key mapped as 'main.exe' instead of it's original name so that the
275 loader can find it.''' 297 loader can find it.'''
276 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } 298 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
277 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') 299 runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl')
278 300
279 needed = self.GetNeeded() 301 needed = self.GetNeeded()
280 for need in needed: 302 for need, archinfo in needed.items():
281 archinfo = needed[need]
282 urlinfo = { URL_KEY: archinfo.url } 303 urlinfo = { URL_KEY: archinfo.url }
283 name = archinfo.name 304 name = archinfo.name
284 305
285 # If starting with runnable-ld.so, make that the main executable. 306 # If starting with runnable-ld.so, make that the main executable.
286 if runnable: 307 if runnable:
287 if need.endswith(RUNNABLE_LD): 308 if need.endswith(RUNNABLE_LD):
288 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo 309 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
289 continue 310 continue
290 311
291 # For the main nexes: 312 # For the main nexes:
(...skipping 24 matching lines...) Expand all
316 337
317 def GetJson(self): 338 def GetJson(self):
318 '''Returns the Manifest as a JSON-formatted string''' 339 '''Returns the Manifest as a JSON-formatted string'''
319 pretty_string = json.dumps(self.GetManifest(), indent=2) 340 pretty_string = json.dumps(self.GetManifest(), indent=2)
320 # json.dumps sometimes returns trailing whitespace and does not put 341 # json.dumps sometimes returns trailing whitespace and does not put
321 # a newline at the end. This code fixes these problems. 342 # a newline at the end. This code fixes these problems.
322 pretty_lines = pretty_string.split('\n') 343 pretty_lines = pretty_string.split('\n')
323 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' 344 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
324 345
325 346
326 def ErrorOut(text):
327 sys.stderr.write(text + '\n')
328 sys.exit(1)
329
330
331 def DetermineToolchain(objdump): 347 def DetermineToolchain(objdump):
332 objdump = objdump.replace('\\', '/') 348 objdump = objdump.replace('\\', '/')
333 paths = objdump.split('/') 349 paths = objdump.split('/')
334 count = len(paths) 350 count = len(paths)
335 for index in range(count - 2, 0, -1): 351 for index in range(count - 2, 0, -1):
336 if paths[index] == 'toolchain': 352 if paths[index] == 'toolchain':
337 if paths[index + 1].endswith('newlib'): 353 if paths[index + 1].endswith('newlib'):
338 return 'newlib' 354 return 'newlib'
339 if paths[index + 1].endswith('glibc'): 355 if paths[index + 1].endswith('glibc'):
340 return 'glibc' 356 return 'glibc'
341 ErrorOut('Could not deternime which toolchain to use.') 357 raise Error('Could not deternime which toolchain to use.')
358
359
360 def Trace(msg):
361 if Trace.verbose:
362 sys.stderr.write(str(msg) + '\n')
363
364 Trace.verbose = False
342 365
343 366
344 def Main(argv): 367 def Main(argv):
345 parser = optparse.OptionParser( 368 parser = optparse.OptionParser(
346 usage='Usage: %prog [options] nexe [extra_libs...]') 369 usage='Usage: %prog [options] nexe [extra_libs...]')
347 parser.add_option('-o', '--output', dest='output', 370 parser.add_option('-o', '--output', dest='output',
348 help='Write manifest file to FILE (default is stdout)', 371 help='Write manifest file to FILE (default is stdout)',
349 metavar='FILE') 372 metavar='FILE')
350 parser.add_option('-D', '--objdump', dest='objdump', default='objdump', 373 parser.add_option('-D', '--objdump', dest='objdump', default='objdump',
351 help='Use TOOL as the "objdump" tool to run', 374 help='Use TOOL as the "objdump" tool to run',
352 metavar='TOOL') 375 metavar='TOOL')
353 parser.add_option('-L', '--library-path', dest='lib_path', 376 parser.add_option('-L', '--library-path', dest='lib_path',
354 action='append', default=[], 377 action='append', default=[],
355 help='Add DIRECTORY to library search path', 378 help='Add DIRECTORY to library search path',
356 metavar='DIRECTORY') 379 metavar='DIRECTORY')
357 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', 380 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
358 help='Destination directory for staging libraries', 381 help='Destination directory for staging libraries',
359 metavar='DIRECTORY') 382 metavar='DIRECTORY')
360 parser.add_option('-r', '--remove', dest='remove', 383 parser.add_option('-r', '--remove', dest='remove',
361 help='Remove the prefix from the files.', 384 help='Remove the prefix from the files.',
362 metavar='PATH') 385 metavar='PATH')
363 parser.add_option('-t', '--toolchain', dest='toolchain', 386 parser.add_option('-t', '--toolchain', dest='toolchain',
364 help='Add DIRECTORY to library search path', 387 help='Add DIRECTORY to library search path',
365 default=None, metavar='TOOLCHAIN') 388 default=None, metavar='TOOLCHAIN')
366 parser.add_option('-n', '--name', dest='name', 389 parser.add_option('-n', '--name', dest='name',
367 help='Rename FOO as BAR', 390 help='Rename FOO as BAR',
368 action='append', default=[], metavar='FOO,BAR') 391 action='append', default=[], metavar='FOO,BAR')
392 parser.add_option('-v', '--verbose',
393 help='Verbose output', action='store_true')
394 parser.add_option('-d', '--debug-mode',
395 help='Debug mode', action='store_true')
369 (options, args) = parser.parse_args(argv) 396 (options, args) = parser.parse_args(argv)
370 397 if options.verbose:
398 Trace.verbose = True
399 if options.debug_mode:
400 DebugPrint.debug_mode = True
401
402 if len(args) < 1:
403 raise Error("No nexe files specified. See --help for more info")
404
371 if not options.toolchain: 405 if not options.toolchain:
372 options.toolchain = DetermineToolchain(os.path.abspath(options.objdump)) 406 options.toolchain = DetermineToolchain(os.path.abspath(options.objdump))
373 407
374 if options.toolchain not in ['newlib', 'glibc', 'pnacl']: 408 if options.toolchain not in ['newlib', 'glibc', 'pnacl']:
375 ErrorOut('Unknown toolchain: ' + str(options.toolchain)) 409 raise Error('Unknown toolchain: ' + str(options.toolchain))
376
377 if len(args) < 1:
378 parser.print_usage()
379 sys.exit(1)
380 410
381 remap = {} 411 remap = {}
382 for ren in options.name: 412 for ren in options.name:
383 parts = ren.split(',') 413 parts = ren.split(',')
384 if len(parts) != 2: 414 if len(parts) != 2:
385 ErrorOut('Expecting --name=<orig_arch.so>,<new_name.so>') 415 raise Error('Expecting --name=<orig_arch.so>,<new_name.so>')
386 remap[parts[0]] = parts[1] 416 remap[parts[0]] = parts[1]
387 417
388 nmf = NmfUtils(objdump=options.objdump, 418 nmf = NmfUtils(objdump=options.objdump,
389 main_files=args, 419 main_files=args,
390 lib_path=options.lib_path, 420 lib_path=options.lib_path,
391 toolchain=options.toolchain, 421 toolchain=options.toolchain,
392 remap=remap) 422 remap=remap)
393 423
394 manifest = nmf.GetManifest() 424 manifest = nmf.GetManifest()
395 if options.output is None: 425 if options.output is None:
396 sys.stdout.write(nmf.GetJson()) 426 sys.stdout.write(nmf.GetJson())
397 else: 427 else:
398 with open(options.output, 'w') as output: 428 with open(options.output, 'w') as output:
399 output.write(nmf.GetJson()) 429 output.write(nmf.GetJson())
400 430
401 if options.stage_dependencies: 431 if options.stage_dependencies:
432 Trace("Staging dependencies...")
402 nmf.StageDependencies(options.stage_dependencies) 433 nmf.StageDependencies(options.stage_dependencies)
403 434
435 return 0
436
404 437
405 # Invoke this file directly for simple testing. 438 # Invoke this file directly for simple testing.
406 if __name__ == '__main__': 439 if __name__ == '__main__':
407 sys.exit(Main(sys.argv[1:])) 440 try:
441 rtn = Main(sys.argv[1:])
442 except Error, e:
443 print "%s: %s" % (os.path.basename(__file__), e)
444 rtn = 1
445 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