| 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 |