普通文本  |  161行  |  4.99 KB

#!/usr/bin/env python
# Copyright (c) 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

'''Makes sure that all files contain proper licensing information.'''


import json
import optparse
import os.path
import subprocess
import sys

import logging

def PrintUsage():
  print '''Usage: python checklicenses.py [--root <root>] [tocheck]
  --root   Specifies the repository root. This defaults to '../..' relative
           to the script file. This will be correct given the normal location
           of the script in '<root>/tools/checklicenses'.

  tocheck  Specifies the directory, relative to root, to check. This defaults
           to '.' so it checks everything.

Examples:
  python checklicenses.py
  python checklicenses.py --root ~/chromium/src third_party'''


WHITELISTED_LICENSES = [
    'Apache (v2.0)',
    'BSD (3 clause)',
    'BSD-like',
    'MIT/X11 (BSD like)',
    'zlib/libpng',
]


PATH_SPECIFIC_WHITELISTED_LICENSES = {
    'tracing/third_party/devscripts': [
        'GPL (v2 or later)',
    ],
}


def check_licenses(base_directory, target_directory=None):
  # Figure out which directory we have to check.
  if not target_directory:
    # No directory to check specified, use the repository root.
    start_dir = base_directory
  else:
    # Directory specified. Start here. It's supposed to be relative to the
    # base directory.
    start_dir = os.path.abspath(os.path.join(base_directory, target_directory))

  logging.info('Using base directory: %s' % base_directory)
  logging.info('Checking: %s' % start_dir)
  logging.info('')

  licensecheck_path = os.path.abspath(os.path.join(base_directory,
                                                   'tracing',
                                                   'third_party',
                                                   'devscripts',
                                                   'licensecheck.pl'))

  licensecheck = subprocess.Popen([licensecheck_path,
                                   '-l', '100',
                                   '-r', start_dir],
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
  stdout, stderr = licensecheck.communicate()
  logging.info('----------- licensecheck stdout -----------')
  logging.info(stdout)
  logging.info('--------- end licensecheck stdout ---------')
  if licensecheck.returncode != 0 or stderr:
    print '----------- licensecheck stderr -----------'
    print stderr
    print '--------- end licensecheck stderr ---------'
    return 1

  used_suppressions = set()
  errors = []

  for line in stdout.splitlines():
    filename, license = line.split(':', 1)
    filename = os.path.relpath(filename.strip(), base_directory)

    # All files in the build output directory are generated one way or another.
    # There's no need to check them.
    if filename.startswith('out/'):
      continue

    # For now we're just interested in the license.
    license = license.replace('*No copyright*', '').strip()

    # Skip generated files.
    if 'GENERATED FILE' in license:
      continue

    if license in WHITELISTED_LICENSES:
      continue

    matched_prefixes = [
        prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES
        if filename.startswith(prefix) and
        license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]]
    if matched_prefixes:
      used_suppressions.update(set(matched_prefixes))
      continue

    errors.append({'filename': filename, 'license': license})

  if errors:
    for error in errors:
      print "'%s' has non-whitelisted license '%s'" % (
          error['filename'], error['license'])
    print '\nFAILED\n'
    print 'Please read',
    print 'http://www.chromium.org/developers/adding-3rd-party-libraries'
    print 'for more info how to handle the failure.'
    print
    print 'Please respect OWNERS of checklicenses.py. Changes violating'
    print 'this requirement may be reverted.'

    # Do not print unused suppressions so that above message is clearly
    # visible and gets proper attention. Too much unrelated output
    # would be distracting and make the important points easier to miss.

    return 1


  return 0


def main():
  default_root = os.path.abspath(
      os.path.join(os.path.dirname(__file__), '..'))
  option_parser = optparse.OptionParser()
  option_parser.add_option('--root', default=default_root,
                           dest='base_directory',
                           help='Specifies the repository root. This defaults '
                           "to '..' relative to the script file, which "
                           'will normally be the repository root.')
  options, args = option_parser.parse_args()

  target_directory = None
  if len(args) == 1:
    target_directory = args[0]
  elif len(args) > 1:
    PrintUsage()
    return 1
  results = check_licenses(options.base_directory, target_directory)
  if not results:
    print 'SUCCESS'
  return results


if '__main__' == __name__:
  sys.exit(main())