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 """Utility for checking and processing licensing information in third_party | 6 """Utility for checking and processing licensing information in third_party |
7 directories. | 7 directories. |
8 | 8 |
9 Usage: licenses.py <command> | 9 Usage: licenses.py <command> |
10 | 10 |
11 Commands: | 11 Commands: |
12 scan scan third_party directories, verifying that we have licensing info | 12 scan scan third_party directories, verifying that we have licensing info |
13 credits generate about:credits on stdout | 13 credits generate about:credits on stdout |
14 | 14 |
15 (You can also import this as a module.) | 15 (You can also import this as a module.) |
16 """ | 16 """ |
17 | 17 |
18 import argparse | 18 import argparse |
19 import cgi | 19 import cgi |
20 import os | 20 import os |
21 import shutil | |
21 import subprocess | 22 import subprocess |
22 import sys | 23 import sys |
24 import tempfile | |
23 | 25 |
24 # TODO(agrieve): Move build_utils.WriteDepFile into a non-android directory. | 26 # TODO(agrieve): Move build_utils.WriteDepFile into a non-android directory. |
25 _REPOSITORY_ROOT = os.path.dirname(os.path.dirname(__file__)) | 27 _REPOSITORY_ROOT = os.path.dirname(os.path.dirname(__file__)) |
26 sys.path.append(os.path.join(_REPOSITORY_ROOT, 'build/android/gyp/util')) | 28 sys.path.append(os.path.join(_REPOSITORY_ROOT, 'build/android/gyp/util')) |
27 import build_utils | 29 import build_utils |
28 | 30 |
29 | 31 |
30 # Paths from the root of the tree to directories to skip. | 32 # Paths from the root of the tree to directories to skip. |
31 PRUNE_PATHS = set([ | 33 PRUNE_PATHS = set([ |
32 # Placeholder directory only, not third-party code. | 34 # Placeholder directory only, not third-party code. |
(...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
475 else: | 477 else: |
476 raise RuntimeError("Unsupported platform '%s'." % sys.platform) | 478 raise RuntimeError("Unsupported platform '%s'." % sys.platform) |
477 | 479 |
478 return os.path.join(_REPOSITORY_ROOT, 'buildtools', subdir, exe) | 480 return os.path.join(_REPOSITORY_ROOT, 'buildtools', subdir, exe) |
479 | 481 |
480 | 482 |
481 def FindThirdPartyDeps(gn_out_dir, gn_target): | 483 def FindThirdPartyDeps(gn_out_dir, gn_target): |
482 if not gn_out_dir: | 484 if not gn_out_dir: |
483 raise RuntimeError("--gn-out-dir is required if --gn-target is used.") | 485 raise RuntimeError("--gn-out-dir is required if --gn-target is used.") |
484 | 486 |
485 gn_deps = subprocess.check_output([_GnBinary(), "desc", gn_out_dir, | 487 # Generate gn project in temp directory and use it to find dependencies. |
486 gn_target, | 488 # Current gn directory cannot be used when we run this script in a gn action |
487 "deps", "--as=buildfile", "--all"]) | 489 # rule, because gn doesn't allow recursive invocations due to potential side |
490 # effects. | |
491 tmp_dir = None | |
492 try: | |
493 tmp_dir = tempfile.mkdtemp(dir = gn_out_dir) | |
494 shutil.copy(gn_out_dir + "/args.gn", tmp_dir) | |
Paweł Hajdan Jr.
2017/04/06 11:52:13
nit: Use os.path.join instead of "+".
Hiroshi Ichikawa
2017/04/07 01:47:16
Done.
| |
495 subprocess.check_output([_GnBinary(), "gen", tmp_dir]) | |
496 gn_deps = subprocess.check_output([ | |
497 _GnBinary(), "desc", tmp_dir, gn_target, | |
498 "deps", "--as=buildfile", "--all"]) | |
499 finally: | |
500 if tmp_dir and os.path.exists(tmp_dir): | |
501 shutil.rmtree(tmp_dir) | |
502 | |
488 third_party_deps = set() | 503 third_party_deps = set() |
489 for build_dep in gn_deps.split(): | 504 for build_dep in gn_deps.split(): |
490 if ("third_party" in build_dep and | 505 if ("third_party" in build_dep and |
491 os.path.basename(build_dep) == "BUILD.gn"): | 506 os.path.basename(build_dep) == "BUILD.gn"): |
492 third_party_deps.add(os.path.dirname(build_dep)) | 507 third_party_deps.add(os.path.dirname(build_dep)) |
493 return third_party_deps | 508 return third_party_deps |
494 | 509 |
495 | 510 |
496 def ScanThirdPartyDirs(root=None): | 511 def ScanThirdPartyDirs(root=None): |
497 """Scan a list of directories and report on any problems we find.""" | 512 """Scan a list of directories and report on any problems we find.""" |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
599 # practice however. | 614 # practice however. |
600 license_file_list = (entry['license_file'] for entry in entries) | 615 license_file_list = (entry['license_file'] for entry in entries) |
601 license_file_list = (os.path.relpath(p) for p in license_file_list) | 616 license_file_list = (os.path.relpath(p) for p in license_file_list) |
602 license_file_list = sorted(set(license_file_list)) | 617 license_file_list = sorted(set(license_file_list)) |
603 build_utils.WriteDepfile(depfile, output_file, | 618 build_utils.WriteDepfile(depfile, output_file, |
604 license_file_list + ['build.ninja']) | 619 license_file_list + ['build.ninja']) |
605 | 620 |
606 return True | 621 return True |
607 | 622 |
608 | 623 |
624 def _ReadFile(path): | |
625 """Reads a file from disk. | |
626 Args: | |
627 path: The path of the file to read, relative to the root of the | |
628 repository. | |
629 Returns: | |
630 The contents of the file as a string. | |
631 """ | |
632 with open(os.path.join(_REPOSITORY_ROOT, path), 'rb') as f: | |
633 return f.read() | |
634 | |
635 | |
636 def GenerateLicenseFile(output_file, gn_out_dir, gn_target): | |
637 """Generate a plain-text LICENSE file which can be used when you ship a part | |
638 of Chromium code (specified by gn_target) as a stand-alone library | |
639 (e.g., //ios/web_view). | |
640 | |
641 The LICENSE file contains licenses of both Chromium and third-party | |
642 libraries which gn_target depends on. """ | |
643 | |
644 third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target) | |
645 | |
646 # Start with Chromium's LICENSE file. | |
647 content = [_ReadFile('LICENSE')] | |
648 | |
649 # Add necessary third_party. | |
650 for directory in sorted(third_party_dirs): | |
651 metadata = ParseDir( | |
652 directory, _REPOSITORY_ROOT, require_license_file=True) | |
653 content.append('-' * 20) | |
654 content.append(directory.split("/")[-1]) | |
Paweł Hajdan Jr.
2017/04/06 11:52:13
nit: Use single quotes ' .
Hiroshi Ichikawa
2017/04/07 01:47:16
Done.
| |
655 content.append('-' * 20) | |
656 license_file = metadata['License File'] | |
657 if license_file and license_file != NOT_SHIPPED: | |
658 content.append(_ReadFile(license_file)) | |
659 | |
660 content_text = '\n'.join(content) | |
661 | |
662 if output_file: | |
663 with open(output_file, 'w') as output: | |
664 output.write(content_text) | |
665 else: | |
666 print content_text | |
667 | |
668 return True | |
669 | |
670 | |
609 def main(): | 671 def main(): |
610 parser = argparse.ArgumentParser() | 672 parser = argparse.ArgumentParser() |
611 parser.add_argument('--file-template', | 673 parser.add_argument('--file-template', |
612 help='Template HTML to use for the license page.') | 674 help='Template HTML to use for the license page.') |
613 parser.add_argument('--entry-template', | 675 parser.add_argument('--entry-template', |
614 help='Template HTML to use for each license.') | 676 help='Template HTML to use for each license.') |
615 parser.add_argument('--target-os', | 677 parser.add_argument('--target-os', |
616 help='OS that this build is targeting.') | 678 help='OS that this build is targeting.') |
617 parser.add_argument('--gn-out-dir', | 679 parser.add_argument('--gn-out-dir', |
618 help='GN output directory for scanning dependencies.') | 680 help='GN output directory for scanning dependencies.') |
619 parser.add_argument('--gn-target', | 681 parser.add_argument('--gn-target', |
620 help='GN target to scan for dependencies.') | 682 help='GN target to scan for dependencies.') |
621 parser.add_argument('command', choices=['help', 'scan', 'credits']) | 683 parser.add_argument('command', |
684 choices=['help', 'scan', 'credits', 'license_file']) | |
622 parser.add_argument('output_file', nargs='?') | 685 parser.add_argument('output_file', nargs='?') |
623 build_utils.AddDepfileOption(parser) | 686 build_utils.AddDepfileOption(parser) |
624 args = parser.parse_args() | 687 args = parser.parse_args() |
625 | 688 |
626 if args.command == 'scan': | 689 if args.command == 'scan': |
627 if not ScanThirdPartyDirs(): | 690 if not ScanThirdPartyDirs(): |
628 return 1 | 691 return 1 |
629 elif args.command == 'credits': | 692 elif args.command == 'credits': |
630 if not GenerateCredits(args.file_template, args.entry_template, | 693 if not GenerateCredits(args.file_template, args.entry_template, |
631 args.output_file, args.target_os, | 694 args.output_file, args.target_os, |
632 args.gn_out_dir, args.gn_target, args.depfile): | 695 args.gn_out_dir, args.gn_target, args.depfile): |
633 return 1 | 696 return 1 |
697 elif args.command == 'license_file': | |
698 if not GenerateLicenseFile( | |
699 args.output_file, args.gn_out_dir, args.gn_target): | |
700 return 1 | |
634 else: | 701 else: |
635 print __doc__ | 702 print __doc__ |
636 return 1 | 703 return 1 |
637 | 704 |
638 | 705 |
639 if __name__ == '__main__': | 706 if __name__ == '__main__': |
640 sys.exit(main()) | 707 sys.exit(main()) |
OLD | NEW |