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 sys | 21 import sys |
22 | 22 |
| 23 # TODO(agrieve): Move build_utils.WriteDepFile into a non-android directory. |
| 24 _REPOSITORY_ROOT = os.path.dirname(os.path.dirname(__file__)) |
| 25 sys.path.append(os.path.join(_REPOSITORY_ROOT, 'build/android/gyp/util')) |
| 26 import build_utils |
| 27 |
| 28 |
23 # Paths from the root of the tree to directories to skip. | 29 # Paths from the root of the tree to directories to skip. |
24 PRUNE_PATHS = set([ | 30 PRUNE_PATHS = set([ |
25 # Placeholder directory only, not third-party code. | 31 # Placeholder directory only, not third-party code. |
26 os.path.join('third_party','adobe'), | 32 os.path.join('third_party','adobe'), |
27 | 33 |
28 # Already covered by //third_party/android_tools. | 34 # Already covered by //third_party/android_tools. |
29 os.path.join('third_party','android_tools_internal'), | 35 os.path.join('third_party','android_tools_internal'), |
30 | 36 |
31 # Apache 2.0 license. See crbug.com/140478 | 37 # Apache 2.0 license. See crbug.com/140478 |
32 os.path.join('third_party','bidichecker'), | 38 os.path.join('third_party','bidichecker'), |
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
469 errors.append((path, e.args[0])) | 475 errors.append((path, e.args[0])) |
470 continue | 476 continue |
471 | 477 |
472 for path, error in sorted(errors): | 478 for path, error in sorted(errors): |
473 print path + ": " + error | 479 print path + ": " + error |
474 | 480 |
475 return len(errors) == 0 | 481 return len(errors) == 0 |
476 | 482 |
477 | 483 |
478 def GenerateCredits( | 484 def GenerateCredits( |
479 file_template_file, entry_template_file, output_file, target_os): | 485 file_template_file, entry_template_file, output_file, target_os, |
| 486 depfile=None): |
480 """Generate about:credits.""" | 487 """Generate about:credits.""" |
481 | 488 |
482 def EvaluateTemplate(template, env, escape=True): | 489 def EvaluateTemplate(template, env, escape=True): |
483 """Expand a template with variables like {{foo}} using a | 490 """Expand a template with variables like {{foo}} using a |
484 dictionary of expansions.""" | 491 dictionary of expansions.""" |
485 for key, val in env.items(): | 492 for key, val in env.items(): |
486 if escape: | 493 if escape: |
487 val = cgi.escape(val) | 494 val = cgi.escape(val) |
488 template = template.replace('{{%s}}' % key, val) | 495 template = template.replace('{{%s}}' % key, val) |
489 return template | 496 return template |
490 | 497 |
491 root = os.path.join(os.path.dirname(__file__), '..') | 498 third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT) |
492 third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root) | |
493 | 499 |
494 if not file_template_file: | 500 if not file_template_file: |
495 file_template_file = os.path.join(root, 'components', 'about_ui', | 501 file_template_file = os.path.join(_REPOSITORY_ROOT, 'components', |
496 'resources', 'about_credits.tmpl') | 502 'about_ui', 'resources', |
| 503 'about_credits.tmpl') |
497 if not entry_template_file: | 504 if not entry_template_file: |
498 entry_template_file = os.path.join(root, 'components', 'about_ui', | 505 entry_template_file = os.path.join(_REPOSITORY_ROOT, 'components', |
499 'resources', | 506 'about_ui', 'resources', |
500 'about_credits_entry.tmpl') | 507 'about_credits_entry.tmpl') |
501 | 508 |
502 entry_template = open(entry_template_file).read() | 509 entry_template = open(entry_template_file).read() |
503 entries = [] | 510 entries = [] |
504 for path in third_party_dirs: | 511 for path in third_party_dirs: |
505 try: | 512 try: |
506 metadata = ParseDir(path, root) | 513 metadata = ParseDir(path, _REPOSITORY_ROOT) |
507 except LicenseError: | 514 except LicenseError: |
508 # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240). | 515 # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240). |
509 continue | 516 continue |
510 if metadata['License File'] == NOT_SHIPPED: | 517 if metadata['License File'] == NOT_SHIPPED: |
511 continue | 518 continue |
512 if target_os == 'ios': | 519 if target_os == 'ios': |
513 # Skip over files that are known not to be used on iOS. | 520 # Skip over files that are known not to be used on iOS. |
514 if path in KNOWN_NON_IOS_LIBRARIES: | 521 if path in KNOWN_NON_IOS_LIBRARIES: |
515 continue | 522 continue |
516 env = { | 523 env = { |
517 'name': metadata['Name'], | 524 'name': metadata['Name'], |
518 'url': metadata['URL'], | 525 'url': metadata['URL'], |
519 'license': open(metadata['License File'], 'rb').read(), | 526 'license': open(metadata['License File'], 'rb').read(), |
520 } | 527 } |
521 entry = { | 528 entry = { |
522 'name': metadata['Name'], | 529 'name': metadata['Name'], |
523 'content': EvaluateTemplate(entry_template, env), | 530 'content': EvaluateTemplate(entry_template, env), |
| 531 'license_file': metadata['License File'], |
524 } | 532 } |
525 entries.append(entry) | 533 entries.append(entry) |
526 | 534 |
527 entries.sort(key=lambda entry: (entry['name'], entry['content'])) | 535 entries.sort(key=lambda entry: (entry['name'], entry['content'])) |
528 entries_contents = '\n'.join([entry['content'] for entry in entries]) | 536 entries_contents = '\n'.join([entry['content'] for entry in entries]) |
529 file_template = open(file_template_file).read() | 537 file_template = open(file_template_file).read() |
530 template_contents = "<!-- Generated by licenses.py; do not edit. -->" | 538 template_contents = "<!-- Generated by licenses.py; do not edit. -->" |
531 template_contents += EvaluateTemplate(file_template, | 539 template_contents += EvaluateTemplate(file_template, |
532 {'entries': entries_contents}, | 540 {'entries': entries_contents}, |
533 escape=False) | 541 escape=False) |
534 | 542 |
535 if output_file: | 543 if output_file: |
536 with open(output_file, 'w') as output: | 544 with open(output_file, 'w') as output: |
537 output.write(template_contents) | 545 output.write(template_contents) |
538 else: | 546 else: |
539 print template_contents | 547 print template_contents |
540 | 548 |
| 549 if depfile: |
| 550 assert output_file |
| 551 # Add in build.ninja so that the target will be considered dirty whenever |
| 552 # gn gen is run. Otherwise, it will fail to notice new files being added. |
| 553 # This is still no perfect, as it will fail if no build files are changed, |
| 554 # but a new README.chromium / LICENSE is added. This shouldn't happen in |
| 555 # practice however. |
| 556 license_file_list = (entry['license_file'] for entry in entries) |
| 557 license_file_list = (os.path.relpath(p) for p in license_file_list) |
| 558 license_file_list = sorted(set(license_file_list)) |
| 559 build_utils.WriteDepfile(depfile, output_file, |
| 560 license_file_list + ['build.ninja']) |
| 561 |
541 return True | 562 return True |
542 | 563 |
543 | 564 |
544 def main(): | 565 def main(): |
545 parser = argparse.ArgumentParser() | 566 parser = argparse.ArgumentParser() |
546 parser.add_argument('--file-template', | 567 parser.add_argument('--file-template', |
547 help='Template HTML to use for the license page.') | 568 help='Template HTML to use for the license page.') |
548 parser.add_argument('--entry-template', | 569 parser.add_argument('--entry-template', |
549 help='Template HTML to use for each license.') | 570 help='Template HTML to use for each license.') |
550 parser.add_argument('--target-os', | 571 parser.add_argument('--target-os', |
551 help='OS that this build is targeting.') | 572 help='OS that this build is targeting.') |
552 parser.add_argument('command', choices=['help', 'scan', 'credits']) | 573 parser.add_argument('command', choices=['help', 'scan', 'credits']) |
553 parser.add_argument('output_file', nargs='?') | 574 parser.add_argument('output_file', nargs='?') |
| 575 build_utils.AddDepfileOption(parser) |
554 args = parser.parse_args() | 576 args = parser.parse_args() |
555 | 577 |
556 if args.command == 'scan': | 578 if args.command == 'scan': |
557 if not ScanThirdPartyDirs(): | 579 if not ScanThirdPartyDirs(): |
558 return 1 | 580 return 1 |
559 elif args.command == 'credits': | 581 elif args.command == 'credits': |
560 if not GenerateCredits(args.file_template, args.entry_template, | 582 if not GenerateCredits(args.file_template, args.entry_template, |
561 args.output_file, args.target_os): | 583 args.output_file, args.target_os, args.depfile): |
562 return 1 | 584 return 1 |
563 else: | 585 else: |
564 print __doc__ | 586 print __doc__ |
565 return 1 | 587 return 1 |
566 | 588 |
567 | 589 |
568 if __name__ == '__main__': | 590 if __name__ == '__main__': |
569 sys.exit(main()) | 591 sys.exit(main()) |
OLD | NEW |