| OLD | NEW |
| 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 |
| 11 import re | 11 import re |
| 12 import shutil | 12 import shutil |
| 13 import struct | 13 import struct |
| 14 import subprocess | 14 import subprocess |
| 15 import sys | 15 import sys |
| 16 import urllib | 16 import urllib |
| 17 | 17 |
| 18 import quote |
| 19 |
| 18 try: | 20 try: |
| 19 import json | 21 import json |
| 20 except ImportError: | 22 except ImportError: |
| 21 import simplejson as json | 23 import simplejson as json |
| 22 | 24 |
| 23 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') | 25 NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$') |
| 24 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') | 26 FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$') |
| 25 | 27 |
| 26 OBJDUMP_ARCH_MAP = { | 28 OBJDUMP_ARCH_MAP = { |
| 27 # Names returned by Linux's objdump: | 29 # Names returned by Linux's objdump: |
| (...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 Trace("copy: %s -> %s" % (source, destination)) | 374 Trace("copy: %s -> %s" % (source, destination)) |
| 373 shutil.copy2(source, destination) | 375 shutil.copy2(source, destination) |
| 374 | 376 |
| 375 def _GenerateManifest(self): | 377 def _GenerateManifest(self): |
| 376 '''Create a JSON formatted dict containing the files | 378 '''Create a JSON formatted dict containing the files |
| 377 | 379 |
| 378 NaCl will map url requests based on architecture. The startup NEXE | 380 NaCl will map url requests based on architecture. The startup NEXE |
| 379 can always be found under the top key PROGRAM. Additional files are under | 381 can always be found under the top key PROGRAM. Additional files are under |
| 380 the FILES key further mapped by file name. In the case of 'runnable' the | 382 the FILES key further mapped by file name. In the case of 'runnable' the |
| 381 PROGRAM key is populated with urls pointing the runnable-ld.so which acts | 383 PROGRAM key is populated with urls pointing the runnable-ld.so which acts |
| 382 as the startup nexe. The application itself, is then placed under the | 384 as the startup nexe. The application itself is then placed under the |
| 383 FILES key mapped as 'main.exe' instead of it's original name so that the | 385 FILES key mapped as 'main.exe' instead of the original name so that the |
| 384 loader can find it.''' | 386 loader can find it. ''' |
| 385 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } | 387 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } |
| 386 | 388 |
| 387 needed = self.GetNeeded() | 389 needed = self.GetNeeded() |
| 388 | 390 |
| 389 runnable = any(n.endswith(RUNNABLE_LD) for n in needed) | 391 runnable = any(n.endswith(RUNNABLE_LD) for n in needed) |
| 390 | 392 |
| 391 for need, archinfo in needed.items(): | 393 extra_files_kv = [(key, ArchFile(name=key, |
| 394 arch=arch, |
| 395 path=url, |
| 396 url=url)) |
| 397 for key, arch, url in self.extra_files] |
| 398 |
| 399 for need, archinfo in needed.items() + extra_files_kv: |
| 392 urlinfo = { URL_KEY: archinfo.url } | 400 urlinfo = { URL_KEY: archinfo.url } |
| 393 name = archinfo.name | 401 name = archinfo.name |
| 394 | 402 |
| 395 # If starting with runnable-ld.so, make that the main executable. | 403 # If starting with runnable-ld.so, make that the main executable. |
| 396 if runnable: | 404 if runnable: |
| 397 if need.endswith(RUNNABLE_LD): | 405 if need.endswith(RUNNABLE_LD): |
| 398 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo | 406 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo |
| 399 continue | 407 continue |
| 400 | 408 |
| 401 # For the main nexes: | 409 # For the main nexes: |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' | 441 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' |
| 434 | 442 |
| 435 | 443 |
| 436 def Trace(msg): | 444 def Trace(msg): |
| 437 if Trace.verbose: | 445 if Trace.verbose: |
| 438 sys.stderr.write(str(msg) + '\n') | 446 sys.stderr.write(str(msg) + '\n') |
| 439 | 447 |
| 440 Trace.verbose = False | 448 Trace.verbose = False |
| 441 | 449 |
| 442 | 450 |
| 451 def ParseExtraFiles(encoded_list, err): |
| 452 """Parse the extra-files list and return a canonicalized list of |
| 453 [key, arch, url] triples. The |encoded_list| should be a list of |
| 454 strings of the form 'key:url' or 'key:arch:url', where an omitted |
| 455 'arch' is taken to mean 'portable'. |
| 456 |
| 457 All entries in |encoded_list| are checked for syntax errors before |
| 458 returning. Error messages are written to |err| (typically |
| 459 sys.stderr) so that the user has actionable feedback for fixing all |
| 460 errors, rather than one at a time. If there are any errors, None is |
| 461 returned instead of a list, since an empty list is a valid return |
| 462 value. |
| 463 """ |
| 464 seen_error = False |
| 465 canonicalized = [] |
| 466 for ix in range(len(encoded_list)): |
| 467 kv = encoded_list[ix] |
| 468 unquoted = quote.unquote(kv, ':') |
| 469 if len(unquoted) == 3: |
| 470 if unquoted[1] != ':': |
| 471 err.write('Syntax error for key:value tuple ' + |
| 472 'for --extra-files argument: ' + kv + '\n') |
| 473 seen_error = True |
| 474 else: |
| 475 canonicalized.append([unquoted[0], 'portable', unquoted[2]]) |
| 476 elif len(unquoted) == 5: |
| 477 if unquoted[1] != ':' or unquoted[3] != ':': |
| 478 err.write('Syntax error for key:arch:url tuple ' + |
| 479 'for --extra-files argument: ' + |
| 480 kv + '\n') |
| 481 seen_error = True |
| 482 else: |
| 483 canonicalized.append([unquoted[0], unquoted[2], unquoted[4]]) |
| 484 else: |
| 485 err.write('Bad key:arch:url tuple for --extra-files: ' + kv + '\n') |
| 486 if seen_error: |
| 487 return None |
| 488 return canonicalized |
| 489 |
| 490 |
| 443 def main(argv): | 491 def main(argv): |
| 444 parser = optparse.OptionParser( | 492 parser = optparse.OptionParser( |
| 445 usage='Usage: %prog [options] nexe [extra_libs...]') | 493 usage='Usage: %prog [options] nexe [extra_libs...]') |
| 446 parser.add_option('-o', '--output', dest='output', | 494 parser.add_option('-o', '--output', dest='output', |
| 447 help='Write manifest file to FILE (default is stdout)', | 495 help='Write manifest file to FILE (default is stdout)', |
| 448 metavar='FILE') | 496 metavar='FILE') |
| 449 parser.add_option('-D', '--objdump', dest='objdump', | 497 parser.add_option('-D', '--objdump', dest='objdump', |
| 450 help='Use TOOL as the "objdump" tool to run', | 498 help='Use TOOL as the "objdump" tool to run', |
| 451 metavar='TOOL') | 499 metavar='TOOL') |
| 452 parser.add_option('-L', '--library-path', dest='lib_path', | 500 parser.add_option('-L', '--library-path', dest='lib_path', |
| 453 action='append', default=[], | 501 action='append', default=[], |
| 454 help='Add DIRECTORY to library search path', | 502 help='Add DIRECTORY to library search path', |
| 455 metavar='DIRECTORY') | 503 metavar='DIRECTORY') |
| 456 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='', | 504 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='', |
| 457 help='A path to prepend to shared libraries in the .nmf', | 505 help='A path to prepend to shared libraries in the .nmf', |
| 458 metavar='DIRECTORY') | 506 metavar='DIRECTORY') |
| 459 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', | 507 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', |
| 460 help='Destination directory for staging libraries', | 508 help='Destination directory for staging libraries', |
| 461 metavar='DIRECTORY') | 509 metavar='DIRECTORY') |
| 462 parser.add_option('-r', '--remove', dest='remove', | 510 parser.add_option('-r', '--remove', dest='remove', |
| 463 help='Remove the prefix from the files.', | 511 help='Remove the prefix from the files.', |
| 464 metavar='PATH') | 512 metavar='PATH') |
| 465 parser.add_option('-t', '--toolchain', help='Legacy option, do not use') | 513 parser.add_option('-t', '--toolchain', help='Legacy option, do not use') |
| 466 parser.add_option('-n', '--name', dest='name', | 514 parser.add_option('-n', '--name', dest='name', |
| 467 help='Rename FOO as BAR', | 515 help='Rename FOO as BAR', |
| 468 action='append', default=[], metavar='FOO,BAR') | 516 action='append', default=[], metavar='FOO,BAR') |
| 517 parser.add_option('-x', '--extra-files', |
| 518 help=('Add extra key:file tuple to the "files"' + |
| 519 ' section of the .nmf'), |
| 520 action='append', default=[], metavar='FILE') |
| 469 parser.add_option('-v', '--verbose', | 521 parser.add_option('-v', '--verbose', |
| 470 help='Verbose output', action='store_true') | 522 help='Verbose output', action='store_true') |
| 471 parser.add_option('-d', '--debug-mode', | 523 parser.add_option('-d', '--debug-mode', |
| 472 help='Debug mode', action='store_true') | 524 help='Debug mode', action='store_true') |
| 473 options, args = parser.parse_args(argv) | 525 options, args = parser.parse_args(argv) |
| 474 if options.verbose: | 526 if options.verbose: |
| 475 Trace.verbose = True | 527 Trace.verbose = True |
| 476 if options.debug_mode: | 528 if options.debug_mode: |
| 477 DebugPrint.debug_mode = True | 529 DebugPrint.debug_mode = True |
| 478 | 530 |
| 479 if options.toolchain is not None: | 531 if options.toolchain is not None: |
| 480 print "warning: option -t/--toolchain is deprecated." | 532 print "warning: option -t/--toolchain is deprecated." |
| 481 | 533 |
| 482 if len(args) < 1: | 534 if len(args) < 1: |
| 483 raise Error("No nexe files specified. See --help for more info") | 535 raise Error("No nexe files specified. See --help for more info") |
| 484 | 536 |
| 537 canonicalized = ParseExtraFiles(options.extra_files, sys.stderr) |
| 538 if canonicalized is None: |
| 539 parser.error("Bad --extra-files (-x) argument syntax") |
| 540 |
| 485 remap = {} | 541 remap = {} |
| 486 for ren in options.name: | 542 for ren in options.name: |
| 487 parts = ren.split(',') | 543 parts = ren.split(',') |
| 488 if len(parts) != 2: | 544 if len(parts) != 2: |
| 489 raise Error('Expecting --name=<orig_arch.so>,<new_name.so>') | 545 raise Error('Expecting --name=<orig_arch.so>,<new_name.so>') |
| 490 remap[parts[0]] = parts[1] | 546 remap[parts[0]] = parts[1] |
| 491 | 547 |
| 492 if options.path_prefix: | 548 if options.path_prefix: |
| 493 path_prefix = options.path_prefix.split('/') | 549 path_prefix = options.path_prefix.split('/') |
| 494 else: | 550 else: |
| 495 path_prefix = [] | 551 path_prefix = [] |
| 496 | 552 |
| 497 nmf = NmfUtils(objdump=options.objdump, | 553 nmf = NmfUtils(objdump=options.objdump, |
| 498 main_files=args, | 554 main_files=args, |
| 499 lib_path=options.lib_path, | 555 lib_path=options.lib_path, |
| 556 extra_files=canonicalized, |
| 500 lib_prefix=path_prefix, | 557 lib_prefix=path_prefix, |
| 501 remap=remap) | 558 remap=remap) |
| 502 | 559 |
| 503 nmf.GetManifest() | 560 nmf.GetManifest() |
| 504 if options.output is None: | 561 if options.output is None: |
| 505 sys.stdout.write(nmf.GetJson()) | 562 sys.stdout.write(nmf.GetJson()) |
| 506 else: | 563 else: |
| 507 with open(options.output, 'w') as output: | 564 with open(options.output, 'w') as output: |
| 508 output.write(nmf.GetJson()) | 565 output.write(nmf.GetJson()) |
| 509 | 566 |
| 510 if options.stage_dependencies: | 567 if options.stage_dependencies: |
| 511 Trace("Staging dependencies...") | 568 Trace("Staging dependencies...") |
| 512 nmf.StageDependencies(options.stage_dependencies) | 569 nmf.StageDependencies(options.stage_dependencies) |
| 513 | 570 |
| 514 return 0 | 571 return 0 |
| 515 | 572 |
| 516 | 573 |
| 517 # Invoke this file directly for simple testing. | 574 # Invoke this file directly for simple testing. |
| 518 if __name__ == '__main__': | 575 if __name__ == '__main__': |
| 519 try: | 576 try: |
| 520 rtn = main(sys.argv[1:]) | 577 rtn = main(sys.argv[1:]) |
| 521 except Error, e: | 578 except Error, e: |
| 522 sys.stderr.write("%s: %s\n" % (os.path.basename(__file__), e)) | 579 sys.stderr.write("%s: %s\n" % (os.path.basename(__file__), e)) |
| 523 rtn = 1 | 580 rtn = 1 |
| 524 sys.exit(rtn) | 581 sys.exit(rtn) |
| OLD | NEW |