Chromium Code Reviews| 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 """Tool for automatically creating .nmf files from .nexe/.pexe/.bc executables. | 6 """Tool for automatically creating .nmf files from .nexe/.pexe/.bc executables. |
| 7 | 7 |
| 8 As well as creating the nmf file this tool can also find and stage | 8 As well as creating the nmf file this tool can also find and stage |
| 9 any shared libraries dependencies that the executables might have. | 9 any shared libraries dependencies that the executables might have. |
| 10 """ | 10 """ |
| 11 | 11 |
| 12 import errno | 12 import errno |
| 13 import json | 13 import json |
| 14 import optparse | 14 import argparse |
|
binji
2014/11/13 23:57:03
sort
Sam Clegg
2014/11/30 17:55:12
Done.
| |
| 15 import os | 15 import os |
| 16 import posixpath | 16 import posixpath |
| 17 import shutil | 17 import shutil |
| 18 import sys | 18 import sys |
| 19 | 19 |
| 20 import getos | 20 import getos |
| 21 | 21 |
| 22 if sys.version_info < (2, 6, 0): | 22 if sys.version_info < (2, 6, 0): |
| 23 sys.stderr.write("python 2.6 or later is required run this script\n") | 23 sys.stderr.write("python 2.6 or later is required run this script\n") |
| 24 sys.exit(1) | 24 sys.exit(1) |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 151 path: Full path to this file on the build system | 151 path: Full path to this file on the build system |
| 152 arch: Architecture of this file (e.g., x86-32) | 152 arch: Architecture of this file (e.g., x86-32) |
| 153 url: Relative path to file in the staged web directory. | 153 url: Relative path to file in the staged web directory. |
| 154 Used for specifying the "url" attribute in the nmf file.""" | 154 Used for specifying the "url" attribute in the nmf file.""" |
| 155 | 155 |
| 156 def __init__(self, name, path, url=None, arch=None): | 156 def __init__(self, name, path, url=None, arch=None): |
| 157 self.name = name | 157 self.name = name |
| 158 self.path = path | 158 self.path = path |
| 159 self.url = url | 159 self.url = url |
| 160 self.arch = arch | 160 self.arch = arch |
| 161 if not arch: | 161 if arch is None: |
| 162 self.arch = ParseElfHeader(path)[0] | 162 self.arch = ParseElfHeader(path)[0] |
| 163 | 163 |
| 164 def __repr__(self): | 164 def __repr__(self): |
| 165 return '<ArchFile %s>' % self.path | 165 return '<ArchFile %s>' % self.path |
| 166 | 166 |
| 167 def __str__(self): | 167 def __str__(self): |
| 168 """Return the file path when invoked with the str() function""" | 168 """Return the file path when invoked with the str() function""" |
| 169 return self.path | 169 return self.path |
| 170 | 170 |
| 171 | 171 |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 395 name = MAIN_NEXE | 395 name = MAIN_NEXE |
| 396 | 396 |
| 397 name = self.remap.get(name, name) | 397 name = self.remap.get(name, name) |
| 398 fileinfo = manifest[FILES_KEY].get(name, {}) | 398 fileinfo = manifest[FILES_KEY].get(name, {}) |
| 399 fileinfo[archinfo.arch] = urlinfo | 399 fileinfo[archinfo.arch] = urlinfo |
| 400 manifest[FILES_KEY][name] = fileinfo | 400 manifest[FILES_KEY][name] = fileinfo |
| 401 self.manifest = manifest | 401 self.manifest = manifest |
| 402 | 402 |
| 403 def GetManifest(self): | 403 def GetManifest(self): |
| 404 """Returns a JSON-formatted dict containing the NaCl dependencies""" | 404 """Returns a JSON-formatted dict containing the NaCl dependencies""" |
| 405 if not self.manifest: | 405 if self.manifest is None: |
| 406 if self.pnacl: | 406 if self.pnacl: |
| 407 self._GeneratePNaClManifest() | 407 self._GeneratePNaClManifest() |
| 408 else: | 408 else: |
| 409 self._GenerateManifest() | 409 self._GenerateManifest() |
| 410 return self.manifest | 410 return self.manifest |
| 411 | 411 |
| 412 def GetJson(self): | 412 def GetJson(self): |
| 413 """Returns the Manifest as a JSON-formatted string""" | 413 """Returns the Manifest as a JSON-formatted string""" |
| 414 pretty_string = json.dumps(self.GetManifest(), indent=2) | 414 pretty_string = json.dumps(self.GetManifest(), indent=2) |
| 415 # json.dumps sometimes returns trailing whitespace and does not put | 415 # json.dumps sometimes returns trailing whitespace and does not put |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 458 seen_error = True | 458 seen_error = True |
| 459 else: | 459 else: |
| 460 canonicalized.append([unquoted[0], unquoted[2], unquoted[4]]) | 460 canonicalized.append([unquoted[0], unquoted[2], unquoted[4]]) |
| 461 else: | 461 else: |
| 462 err.write('Bad key:arch:url tuple for --extra-files: ' + kv + '\n') | 462 err.write('Bad key:arch:url tuple for --extra-files: ' + kv + '\n') |
| 463 if seen_error: | 463 if seen_error: |
| 464 return None | 464 return None |
| 465 return canonicalized | 465 return canonicalized |
| 466 | 466 |
| 467 | 467 |
| 468 def IsValidSDKRoot(directory): | |
| 469 if not os.path.exists(os.path.join(directory, 'toolchain')): | |
|
binji
2014/11/13 23:57:03
just
return os.path.exists(...)
?
Sam Clegg
2014/11/30 17:55:12
Split into seperate CL
| |
| 470 return False | |
| 471 | |
| 472 return True | |
| 473 | |
| 474 | |
| 468 def GetSDKRoot(): | 475 def GetSDKRoot(): |
| 469 """Determine current NACL_SDK_ROOT, either via the environment variable | 476 """Determine current NACL_SDK_ROOT, either via the environment variable |
| 470 itself, or by attempting to derive it from the location of this script. | 477 itself, or by attempting to derive it from the location of this script. |
| 471 """ | 478 """ |
| 472 sdk_root = os.environ.get('NACL_SDK_ROOT') | 479 sdk_root = os.environ.get('NACL_SDK_ROOT') |
| 473 if not sdk_root: | 480 if sdk_root: |
|
binji
2014/11/13 23:57:03
Is this change necessary?
Sam Clegg
2014/11/30 17:55:12
Separate CL
| |
| 474 sdk_root = os.path.dirname(SCRIPT_DIR) | 481 if not IsValidSDKRoot(sdk_root): |
| 475 if not os.path.exists(os.path.join(sdk_root, 'toolchain')): | 482 raise Error('Invalid NACL_SDK_ROOT: %s' % sdk_root) |
| 476 return None | 483 return sdk_root |
| 477 | 484 |
| 478 return sdk_root | 485 # If no $NACL_SDK_ROOT set then use the current script location to |
| 486 # attempt to drive the root. | |
|
binji
2014/11/13 23:57:03
sp: derive
Sam Clegg
2014/11/30 17:55:12
Separate CL
| |
| 487 sdk_root = os.path.dirname(SCRIPT_DIR) | |
| 488 if IsValidSDKRoot(sdk_root): | |
| 489 return sdk_root | |
| 490 | |
| 491 return None | |
| 479 | 492 |
| 480 | 493 |
| 481 def FindObjdumpExecutable(): | 494 def FindObjdumpExecutable(): |
| 482 """Derive path to objdump executable to use for determining shared | 495 """Derive path to objdump executable to use for determining shared |
| 483 object dependencies. | 496 object dependencies. |
| 484 """ | 497 """ |
| 485 sdk_root = GetSDKRoot() | 498 sdk_root = GetSDKRoot() |
| 486 if not sdk_root: | 499 if sdk_root is None: |
| 487 return None | 500 return None |
| 488 | 501 |
| 489 osname = getos.GetPlatform() | 502 osname = getos.GetPlatform() |
| 490 toolchain = os.path.join(sdk_root, 'toolchain', '%s_x86_glibc' % osname) | 503 toolchain = os.path.join(sdk_root, 'toolchain', '%s_x86_glibc' % osname) |
| 491 objdump = os.path.join(toolchain, 'bin', 'x86_64-nacl-objdump') | 504 objdump = os.path.join(toolchain, 'bin', 'x86_64-nacl-objdump') |
| 492 if osname == 'win': | 505 if osname == 'win': |
| 493 objdump += '.exe' | 506 objdump += '.exe' |
| 494 | 507 |
| 495 if not os.path.exists(objdump): | 508 if not os.path.exists(objdump): |
| 496 sys.stderr.write('WARNING: failed to find objdump in default ' | 509 sys.stderr.write('WARNING: failed to find objdump in default ' |
| 497 'location: %s' % objdump) | 510 'location: %s' % objdump) |
| 498 return None | 511 return None |
| 499 | 512 |
| 500 return objdump | 513 return objdump |
| 501 | 514 |
| 502 | 515 |
| 503 def GetDefaultLibPath(config): | 516 def GetDefaultLibPath(config): |
| 504 """Derive default library path to use when searching for shared | 517 """Derive default library path to use when searching for shared |
| 505 objects. This currently include the toolchain library folders | 518 objects. This currently include the toolchain library folders |
| 506 as well as the top level SDK lib folder and the naclports lib | 519 as well as the top level SDK lib folder and the naclports lib |
| 507 folder. We include both 32-bit and 64-bit library paths. | 520 folder. We include both 32-bit and 64-bit library paths. |
| 508 """ | 521 """ |
| 509 assert(config in ('Debug', 'Release')) | 522 assert(config in ('Debug', 'Release')) |
| 510 sdk_root = GetSDKRoot() | 523 sdk_root = GetSDKRoot() |
| 511 if not sdk_root: | 524 if sdk_root is None: |
| 512 # TOOD(sbc): output a warning here? We would also need to suppress | 525 # TOOD(sbc): output a warning here? We would also need to suppress |
| 513 # the warning when run from the chromium build. | 526 # the warning when run from the chromium build. |
| 514 return [] | 527 return [] |
| 515 | 528 |
| 516 osname = getos.GetPlatform() | 529 osname = getos.GetPlatform() |
| 517 libpath = [ | 530 libpath = [ |
| 518 # Core toolchain libraries | 531 # Core toolchain libraries |
| 519 'toolchain/%s_x86_glibc/x86_64-nacl/lib' % osname, | 532 'toolchain/%s_x86_glibc/x86_64-nacl/lib' % osname, |
| 520 'toolchain/%s_x86_glibc/x86_64-nacl/lib32' % osname, | 533 'toolchain/%s_x86_glibc/x86_64-nacl/lib32' % osname, |
| 521 # naclports installed libraries | 534 # naclports installed libraries |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 535 '%s/arm-nacl/lib' % bionic_dir, | 548 '%s/arm-nacl/lib' % bionic_dir, |
| 536 '%s/arm-nacl/usr/lib' % bionic_dir, | 549 '%s/arm-nacl/usr/lib' % bionic_dir, |
| 537 'lib/bionic_arm/%s' % config, | 550 'lib/bionic_arm/%s' % config, |
| 538 ] | 551 ] |
| 539 libpath = [os.path.normpath(p) for p in libpath] | 552 libpath = [os.path.normpath(p) for p in libpath] |
| 540 libpath = [os.path.join(sdk_root, p) for p in libpath] | 553 libpath = [os.path.join(sdk_root, p) for p in libpath] |
| 541 return libpath | 554 return libpath |
| 542 | 555 |
| 543 | 556 |
| 544 def main(argv): | 557 def main(argv): |
| 545 parser = optparse.OptionParser( | 558 parser = argparse.ArgumentParser(description=__doc__) |
| 546 usage='Usage: %prog [options] nexe [extra_libs...]', description=__doc__) | 559 parser.add_argument('-o', '--output', dest='output', |
| 547 parser.add_option('-o', '--output', dest='output', | 560 help='Write manifest file to FILE (default is stdout)', |
| 548 help='Write manifest file to FILE (default is stdout)', | 561 metavar='FILE') |
| 549 metavar='FILE') | 562 parser.add_argument('-D', '--objdump', dest='objdump', |
| 550 parser.add_option('-D', '--objdump', dest='objdump', | 563 help='Override the default "objdump" tool used to find ' |
| 551 help='Override the default "objdump" tool used to find ' | 564 'shared object dependencies', |
| 552 'shared object dependencies', | 565 metavar='TOOL') |
| 553 metavar='TOOL') | 566 parser.add_argument('--no-default-libpath', action='store_true', |
| 554 parser.add_option('--no-default-libpath', action='store_true', | 567 help="Don't include the SDK default library paths") |
| 555 help="Don't include the SDK default library paths") | 568 parser.add_argument('--debug-libs', action='store_true', |
| 556 parser.add_option('--debug-libs', action='store_true', | 569 help='Use debug library paths when constructing default ' |
| 557 help='Use debug library paths when constructing default ' | 570 'library path.') |
| 558 'library path.') | 571 parser.add_argument('-L', '--library-path', dest='lib_path', |
| 559 parser.add_option('-L', '--library-path', dest='lib_path', | 572 action='append', default=[], |
| 560 action='append', default=[], | 573 help='Add DIRECTORY to library search path', |
| 561 help='Add DIRECTORY to library search path', | 574 metavar='DIRECTORY') |
| 562 metavar='DIRECTORY') | 575 parser.add_argument('-P', '--path-prefix', dest='path_prefix', default='', |
| 563 parser.add_option('-P', '--path-prefix', dest='path_prefix', default='', | 576 help='Deprecated. An alias for --lib-prefix.', |
| 564 help='Deprecated. An alias for --lib-prefix.', | 577 metavar='DIRECTORY') |
| 565 metavar='DIRECTORY') | 578 parser.add_argument('-p', '--lib-prefix', dest='lib_prefix', default='', |
| 566 parser.add_option('-p', '--lib-prefix', dest='lib_prefix', default='', | 579 help='A path to prepend to shared libraries in the .nmf', |
| 567 help='A path to prepend to shared libraries in the .nmf', | 580 metavar='DIRECTORY') |
| 568 metavar='DIRECTORY') | 581 parser.add_argument('-N', '--nexe-prefix', dest='nexe_prefix', default='', |
| 569 parser.add_option('-N', '--nexe-prefix', dest='nexe_prefix', default='', | 582 help='A path to prepend to nexes in the .nmf', |
| 570 help='A path to prepend to nexes in the .nmf', | 583 metavar='DIRECTORY') |
| 571 metavar='DIRECTORY') | 584 parser.add_argument('-s', '--stage-dependencies', dest='stage_dependencies', |
| 572 parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', | 585 help='Destination directory for staging libraries', |
| 573 help='Destination directory for staging libraries', | 586 metavar='DIRECTORY') |
| 574 metavar='DIRECTORY') | 587 parser.add_argument('--no-arch-prefix', action='store_true', |
| 575 parser.add_option('--no-arch-prefix', action='store_true', | 588 help='Don\'t put shared libraries in the lib32/lib64 ' |
| 576 help='Don\'t put shared libraries in the lib32/lib64 ' | 589 'directories. Instead, they will be put in the same ' |
| 577 'directories. Instead, they will be put in the same ' | 590 'directory as the .nexe that matches its architecture.') |
| 578 'directory as the .nexe that matches its architecture.') | 591 parser.add_argument('-t', '--toolchain', help='Legacy option, do not use') |
| 579 parser.add_option('-t', '--toolchain', help='Legacy option, do not use') | 592 parser.add_argument('-n', '--name', dest='name', |
| 580 parser.add_option('-n', '--name', dest='name', | 593 help='Rename FOO as BAR', |
| 581 help='Rename FOO as BAR', | 594 action='append', default=[], metavar='FOO,BAR') |
| 582 action='append', default=[], metavar='FOO,BAR') | 595 parser.add_argument('-x', '--extra-files', |
| 583 parser.add_option('-x', '--extra-files', | 596 help=('Add extra key:file tuple to the "files"' + |
| 584 help=('Add extra key:file tuple to the "files"' + | 597 ' section of the .nmf'), |
| 585 ' section of the .nmf'), | 598 action='append', default=[], metavar='FILE') |
| 586 action='append', default=[], metavar='FILE') | 599 parser.add_argument('-O', '--pnacl-optlevel', |
| 587 parser.add_option('-O', '--pnacl-optlevel', | 600 help='Set the optimization level to N in PNaCl manifests', |
| 588 help='Set the optimization level to N in PNaCl manifests', | 601 metavar='N') |
| 589 metavar='N') | 602 parser.add_argument('--pnacl-debug-optlevel', |
| 590 parser.add_option('--pnacl-debug-optlevel', | 603 help='Set the optimization level to N for debugging ' |
| 591 help='Set the optimization level to N for debugging ' | 604 'sections in PNaCl manifests', |
| 592 'sections in PNaCl manifests', | 605 metavar='N') |
| 593 metavar='N') | 606 parser.add_argument('-v', '--verbose', |
| 594 parser.add_option('-v', '--verbose', | 607 help='Verbose output', action='store_true') |
| 595 help='Verbose output', action='store_true') | 608 parser.add_argument('-d', '--debug-mode', |
| 596 parser.add_option('-d', '--debug-mode', | 609 help='Debug mode', action='store_true') |
| 597 help='Debug mode', action='store_true') | 610 parser.add_argument('executables', nargs='+') |
| 598 | 611 |
| 599 # To enable bash completion for this command first install optcomplete | 612 # To enable bash completion for this command first install optcomplete |
| 600 # and then add this line to your .bashrc: | 613 # and then add this line to your .bashrc: |
| 601 # complete -F _optcomplete create_nmf.py | 614 # complete -F _optcomplete create_nmf.py |
| 602 try: | 615 try: |
| 603 import optcomplete | 616 import optcomplete |
| 604 optcomplete.autocomplete(parser) | 617 optcomplete.autocomplete(parser) |
| 605 except ImportError: | 618 except ImportError: |
| 606 pass | 619 pass |
| 607 | 620 |
| 608 options, args = parser.parse_args(argv) | 621 options = parser.parse_args(argv) |
| 609 if options.verbose: | 622 if options.verbose: |
| 610 Trace.verbose = True | 623 Trace.verbose = True |
| 611 if options.debug_mode: | 624 if options.debug_mode: |
| 612 DebugPrint.debug_mode = True | 625 DebugPrint.debug_mode = True |
| 613 | 626 |
| 614 if options.toolchain is not None: | 627 if options.toolchain is not None: |
| 615 sys.stderr.write('warning: option -t/--toolchain is deprecated.\n') | 628 sys.stderr.write('warning: option -t/--toolchain is deprecated.\n') |
| 616 | 629 |
| 617 if len(args) < 1: | |
| 618 parser.error('No nexe files specified. See --help for more info') | |
| 619 | |
| 620 canonicalized = ParseExtraFiles(options.extra_files, sys.stderr) | 630 canonicalized = ParseExtraFiles(options.extra_files, sys.stderr) |
| 621 if canonicalized is None: | 631 if canonicalized is None: |
| 622 parser.error('Bad --extra-files (-x) argument syntax') | 632 parser.error('Bad --extra-files (-x) argument syntax') |
| 623 | 633 |
| 624 remap = {} | 634 remap = {} |
| 625 for ren in options.name: | 635 for ren in options.name: |
| 626 parts = ren.split(',') | 636 parts = ren.split(',') |
| 627 if len(parts) != 2: | 637 if len(parts) != 2: |
| 628 parser.error('Expecting --name=<orig_arch.so>,<new_name.so>') | 638 parser.error('Expecting --name=<orig_arch.so>,<new_name.so>') |
| 629 remap[parts[0]] = parts[1] | 639 remap[parts[0]] = parts[1] |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 654 if options.pnacl_debug_optlevel is not None: | 664 if options.pnacl_debug_optlevel is not None: |
| 655 pnacl_debug_optlevel = int(options.pnacl_debug_optlevel) | 665 pnacl_debug_optlevel = int(options.pnacl_debug_optlevel) |
| 656 else: | 666 else: |
| 657 pnacl_debug_optlevel = pnacl_optlevel | 667 pnacl_debug_optlevel = pnacl_optlevel |
| 658 | 668 |
| 659 nmf_root = None | 669 nmf_root = None |
| 660 if options.output: | 670 if options.output: |
| 661 nmf_root = os.path.dirname(options.output) | 671 nmf_root = os.path.dirname(options.output) |
| 662 | 672 |
| 663 nmf = NmfUtils(objdump=options.objdump, | 673 nmf = NmfUtils(objdump=options.objdump, |
| 664 main_files=args, | 674 main_files=options.executables, |
| 665 lib_path=options.lib_path, | 675 lib_path=options.lib_path, |
| 666 extra_files=canonicalized, | 676 extra_files=canonicalized, |
| 667 lib_prefix=options.lib_prefix, | 677 lib_prefix=options.lib_prefix, |
| 668 nexe_prefix=options.nexe_prefix, | 678 nexe_prefix=options.nexe_prefix, |
| 669 no_arch_prefix=options.no_arch_prefix, | 679 no_arch_prefix=options.no_arch_prefix, |
| 670 remap=remap, | 680 remap=remap, |
| 671 pnacl_optlevel=pnacl_optlevel, | 681 pnacl_optlevel=pnacl_optlevel, |
| 672 pnacl_debug_optlevel=pnacl_debug_optlevel, | 682 pnacl_debug_optlevel=pnacl_debug_optlevel, |
| 673 nmf_root=nmf_root) | 683 nmf_root=nmf_root) |
| 674 | 684 |
| 675 if not options.output: | 685 if options.output is None: |
| 676 sys.stdout.write(nmf.GetJson()) | 686 sys.stdout.write(nmf.GetJson()) |
| 677 else: | 687 else: |
| 678 with open(options.output, 'w') as output: | 688 with open(options.output, 'w') as output: |
| 679 output.write(nmf.GetJson()) | 689 output.write(nmf.GetJson()) |
| 680 | 690 |
| 681 if options.stage_dependencies and not nmf.pnacl: | 691 if options.stage_dependencies and not nmf.pnacl: |
| 682 Trace('Staging dependencies...') | 692 Trace('Staging dependencies...') |
| 683 nmf.StageDependencies(options.stage_dependencies) | 693 nmf.StageDependencies(options.stage_dependencies) |
| 684 | 694 |
| 685 return 0 | 695 return 0 |
| 686 | 696 |
| 687 | 697 |
| 688 if __name__ == '__main__': | 698 if __name__ == '__main__': |
| 689 try: | 699 try: |
| 690 rtn = main(sys.argv[1:]) | 700 rtn = main(sys.argv[1:]) |
| 691 except Error, e: | 701 except Error, e: |
| 692 sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e)) | 702 sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e)) |
| 693 rtn = 1 | 703 rtn = 1 |
| 694 except KeyboardInterrupt: | 704 except KeyboardInterrupt: |
| 695 sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__)) | 705 sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__)) |
| 696 rtn = 1 | 706 rtn = 1 |
| 697 sys.exit(rtn) | 707 sys.exit(rtn) |
| OLD | NEW |