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 """Makes sure that all files contain proper licensing information.""" | 6 """Makes sure that all files contain proper licensing information.""" |
7 | 7 |
8 | 8 |
| 9 import json |
9 import optparse | 10 import optparse |
10 import os.path | 11 import os.path |
11 import subprocess | 12 import subprocess |
12 import sys | 13 import sys |
13 | 14 |
14 | 15 |
15 def PrintUsage(): | 16 def PrintUsage(): |
16 print """Usage: python checklicenses.py [--root <root>] [tocheck] | 17 print """Usage: python checklicenses.py [--root <root>] [tocheck] |
17 --root Specifies the repository root. This defaults to "../.." relative | 18 --root Specifies the repository root. This defaults to "../.." relative |
18 to the script file. This will be correct given the normal location | 19 to the script file. This will be correct given the normal location |
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 print stdout | 416 print stdout |
416 print '--------- end licensecheck stdout ---------' | 417 print '--------- end licensecheck stdout ---------' |
417 if licensecheck.returncode != 0 or stderr: | 418 if licensecheck.returncode != 0 or stderr: |
418 print '----------- licensecheck stderr -----------' | 419 print '----------- licensecheck stderr -----------' |
419 print stderr | 420 print stderr |
420 print '--------- end licensecheck stderr ---------' | 421 print '--------- end licensecheck stderr ---------' |
421 print "\nFAILED\n" | 422 print "\nFAILED\n" |
422 return 1 | 423 return 1 |
423 | 424 |
424 used_suppressions = set() | 425 used_suppressions = set() |
| 426 errors = [] |
425 | 427 |
426 success = True | |
427 for line in stdout.splitlines(): | 428 for line in stdout.splitlines(): |
428 filename, license = line.split(':', 1) | 429 filename, license = line.split(':', 1) |
429 filename = os.path.relpath(filename.strip(), options.base_directory) | 430 filename = os.path.relpath(filename.strip(), options.base_directory) |
430 | 431 |
431 # All files in the build output directory are generated one way or another. | 432 # All files in the build output directory are generated one way or another. |
432 # There's no need to check them. | 433 # There's no need to check them. |
433 if filename.startswith('out/'): | 434 if filename.startswith('out/'): |
434 continue | 435 continue |
435 | 436 |
436 # For now we're just interested in the license. | 437 # For now we're just interested in the license. |
437 license = license.replace('*No copyright*', '').strip() | 438 license = license.replace('*No copyright*', '').strip() |
438 | 439 |
439 # Skip generated files. | 440 # Skip generated files. |
440 if 'GENERATED FILE' in license: | 441 if 'GENERATED FILE' in license: |
441 continue | 442 continue |
442 | 443 |
443 if license in WHITELISTED_LICENSES: | 444 if license in WHITELISTED_LICENSES: |
444 continue | 445 continue |
445 | 446 |
446 if not options.ignore_suppressions: | 447 if not options.ignore_suppressions: |
447 matched_prefixes = [ | 448 matched_prefixes = [ |
448 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES | 449 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES |
449 if filename.startswith(prefix) and | 450 if filename.startswith(prefix) and |
450 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]] | 451 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]] |
451 if matched_prefixes: | 452 if matched_prefixes: |
452 used_suppressions.update(set(matched_prefixes)) | 453 used_suppressions.update(set(matched_prefixes)) |
453 continue | 454 continue |
454 | 455 |
455 print "'%s' has non-whitelisted license '%s'" % (filename, license) | 456 errors.append({'filename': filename, 'license': license}) |
456 success = False | |
457 | 457 |
458 if success: | 458 if options.json: |
459 print "\nSUCCESS\n" | 459 with open(options.json, 'w') as f: |
| 460 json.dump(errors, f) |
460 | 461 |
461 if not len(args): | 462 if errors: |
462 unused_suppressions = set( | 463 for error in errors: |
463 PATH_SPECIFIC_WHITELISTED_LICENSES.keys()).difference(used_suppressions) | 464 print "'%s' has non-whitelisted license '%s'" % ( |
464 if unused_suppressions: | 465 error['filename'], error['license']) |
465 print "\nNOTE: unused suppressions detected:\n" | |
466 print '\n'.join(unused_suppressions) | |
467 | |
468 return 0 | |
469 else: | |
470 print "\nFAILED\n" | 466 print "\nFAILED\n" |
471 print "Please read", | 467 print "Please read", |
472 print "http://www.chromium.org/developers/adding-3rd-party-libraries" | 468 print "http://www.chromium.org/developers/adding-3rd-party-libraries" |
473 print "for more info how to handle the failure." | 469 print "for more info how to handle the failure." |
474 print | 470 print |
475 print "Please respect OWNERS of checklicenses.py. Changes violating" | 471 print "Please respect OWNERS of checklicenses.py. Changes violating" |
476 print "this requirement may be reverted." | 472 print "this requirement may be reverted." |
477 | 473 |
478 # Do not print unused suppressions so that above message is clearly | 474 # Do not print unused suppressions so that above message is clearly |
479 # visible and gets proper attention. Too much unrelated output | 475 # visible and gets proper attention. Too much unrelated output |
480 # would be distracting and make the important points easier to miss. | 476 # would be distracting and make the important points easier to miss. |
481 | 477 |
482 return 1 | 478 return 1 |
483 | 479 |
| 480 print "\nSUCCESS\n" |
| 481 |
| 482 if not len(args): |
| 483 unused_suppressions = set( |
| 484 PATH_SPECIFIC_WHITELISTED_LICENSES.iterkeys()).difference( |
| 485 used_suppressions) |
| 486 if unused_suppressions: |
| 487 print "\nNOTE: unused suppressions detected:\n" |
| 488 print '\n'.join(unused_suppressions) |
| 489 |
| 490 return 0 |
| 491 |
484 | 492 |
485 def main(): | 493 def main(): |
486 default_root = os.path.abspath( | 494 default_root = os.path.abspath( |
487 os.path.join(os.path.dirname(__file__), '..', '..')) | 495 os.path.join(os.path.dirname(__file__), '..', '..')) |
488 option_parser = optparse.OptionParser() | 496 option_parser = optparse.OptionParser() |
489 option_parser.add_option('--root', default=default_root, | 497 option_parser.add_option('--root', default=default_root, |
490 dest='base_directory', | 498 dest='base_directory', |
491 help='Specifies the repository root. This defaults ' | 499 help='Specifies the repository root. This defaults ' |
492 'to "../.." relative to the script file, which ' | 500 'to "../.." relative to the script file, which ' |
493 'will normally be the repository root.') | 501 'will normally be the repository root.') |
494 option_parser.add_option('-v', '--verbose', action='store_true', | 502 option_parser.add_option('-v', '--verbose', action='store_true', |
495 default=False, help='Print debug logging') | 503 default=False, help='Print debug logging') |
496 option_parser.add_option('--ignore-suppressions', | 504 option_parser.add_option('--ignore-suppressions', |
497 action='store_true', | 505 action='store_true', |
498 default=False, | 506 default=False, |
499 help='Ignore path-specific license whitelist.') | 507 help='Ignore path-specific license whitelist.') |
| 508 option_parser.add_option('--json', help='Path to JSON output file') |
500 options, args = option_parser.parse_args() | 509 options, args = option_parser.parse_args() |
501 return check_licenses(options, args) | 510 return check_licenses(options, args) |
502 | 511 |
503 | 512 |
504 if '__main__' == __name__: | 513 if '__main__' == __name__: |
505 sys.exit(main()) | 514 sys.exit(main()) |
OLD | NEW |