1#!/usr/bin/env python 2# Copyright (c) 2015 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6'''Makes sure that all files contain proper licensing information.''' 7 8 9import json 10import optparse 11import os.path 12import subprocess 13import sys 14 15import logging 16 17def PrintUsage(): 18 print '''Usage: python checklicenses.py [--root <root>] [tocheck] 19 --root Specifies the repository root. This defaults to '../..' relative 20 to the script file. This will be correct given the normal location 21 of the script in '<root>/tools/checklicenses'. 22 23 tocheck Specifies the directory, relative to root, to check. This defaults 24 to '.' so it checks everything. 25 26Examples: 27 python checklicenses.py 28 python checklicenses.py --root ~/chromium/src third_party''' 29 30 31WHITELISTED_LICENSES = [ 32 'Apache (v2.0)', 33 'BSD (3 clause)', 34 'BSD-like', 35 'MIT/X11 (BSD like)', 36 'zlib/libpng', 37] 38 39 40PATH_SPECIFIC_WHITELISTED_LICENSES = { 41 'tracing/third_party/devscripts': [ 42 'GPL (v2 or later)', 43 ], 44} 45 46 47def check_licenses(base_directory, target_directory=None): 48 # Figure out which directory we have to check. 49 if not target_directory: 50 # No directory to check specified, use the repository root. 51 start_dir = base_directory 52 else: 53 # Directory specified. Start here. It's supposed to be relative to the 54 # base directory. 55 start_dir = os.path.abspath(os.path.join(base_directory, target_directory)) 56 57 logging.info('Using base directory: %s' % base_directory) 58 logging.info('Checking: %s' % start_dir) 59 logging.info('') 60 61 licensecheck_path = os.path.abspath(os.path.join(base_directory, 62 'tracing', 63 'third_party', 64 'devscripts', 65 'licensecheck.pl')) 66 67 licensecheck = subprocess.Popen([licensecheck_path, 68 '-l', '100', 69 '-r', start_dir], 70 stdout=subprocess.PIPE, 71 stderr=subprocess.PIPE) 72 stdout, stderr = licensecheck.communicate() 73 logging.info('----------- licensecheck stdout -----------') 74 logging.info(stdout) 75 logging.info('--------- end licensecheck stdout ---------') 76 if licensecheck.returncode != 0 or stderr: 77 print '----------- licensecheck stderr -----------' 78 print stderr 79 print '--------- end licensecheck stderr ---------' 80 return 1 81 82 used_suppressions = set() 83 errors = [] 84 85 for line in stdout.splitlines(): 86 filename, license = line.split(':', 1) 87 filename = os.path.relpath(filename.strip(), base_directory) 88 89 # All files in the build output directory are generated one way or another. 90 # There's no need to check them. 91 if filename.startswith('out/'): 92 continue 93 94 # For now we're just interested in the license. 95 license = license.replace('*No copyright*', '').strip() 96 97 # Skip generated files. 98 if 'GENERATED FILE' in license: 99 continue 100 101 if license in WHITELISTED_LICENSES: 102 continue 103 104 matched_prefixes = [ 105 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES 106 if filename.startswith(prefix) and 107 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]] 108 if matched_prefixes: 109 used_suppressions.update(set(matched_prefixes)) 110 continue 111 112 errors.append({'filename': filename, 'license': license}) 113 114 if errors: 115 for error in errors: 116 print "'%s' has non-whitelisted license '%s'" % ( 117 error['filename'], error['license']) 118 print '\nFAILED\n' 119 print 'Please read', 120 print 'http://www.chromium.org/developers/adding-3rd-party-libraries' 121 print 'for more info how to handle the failure.' 122 print 123 print 'Please respect OWNERS of checklicenses.py. Changes violating' 124 print 'this requirement may be reverted.' 125 126 # Do not print unused suppressions so that above message is clearly 127 # visible and gets proper attention. Too much unrelated output 128 # would be distracting and make the important points easier to miss. 129 130 return 1 131 132 133 return 0 134 135 136def main(): 137 default_root = os.path.abspath( 138 os.path.join(os.path.dirname(__file__), '..')) 139 option_parser = optparse.OptionParser() 140 option_parser.add_option('--root', default=default_root, 141 dest='base_directory', 142 help='Specifies the repository root. This defaults ' 143 "to '..' relative to the script file, which " 144 'will normally be the repository root.') 145 options, args = option_parser.parse_args() 146 147 target_directory = None 148 if len(args) == 1: 149 target_directory = args[0] 150 elif len(args) > 1: 151 PrintUsage() 152 return 1 153 results = check_licenses(options.base_directory, target_directory) 154 if not results: 155 print 'SUCCESS' 156 return results 157 158 159if '__main__' == __name__: 160 sys.exit(main()) 161