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 |