Extract protobuf database into a new 'leveldb_proto' component
[chromium-blink-merge.git] / tools / checklicenses / checklicenses.py
blob2fc0ae8336d385abb68dfe865549636c68697f3b
1 #!/usr/bin/env python
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
4 # found in the LICENSE file.
6 """Makes sure that all files contain proper licensing information."""
9 import json
10 import optparse
11 import os.path
12 import subprocess
13 import sys
16 def PrintUsage():
17 print """Usage: python checklicenses.py [--root <root>] [tocheck]
18 --root Specifies the repository root. This defaults to "../.." relative
19 to the script file. This will be correct given the normal location
20 of the script in "<root>/tools/checklicenses".
22 --ignore-suppressions Ignores path-specific license whitelist. Useful when
23 trying to remove a suppression/whitelist entry.
25 tocheck Specifies the directory, relative to root, to check. This defaults
26 to "." so it checks everything.
28 Examples:
29 python checklicenses.py
30 python checklicenses.py --root ~/chromium/src third_party"""
33 WHITELISTED_LICENSES = [
34 'Anti-Grain Geometry',
35 'Apache (v2.0)',
36 'Apache (v2.0) BSD (2 clause)',
37 'Apache (v2.0) GPL (v2)',
38 'Apple MIT', # https://fedoraproject.org/wiki/Licensing/Apple_MIT_License
39 'APSL (v2)',
40 'APSL (v2) BSD (4 clause)',
41 'BSD',
42 'BSD (2 clause)',
43 'BSD (2 clause) ISC',
44 'BSD (2 clause) MIT/X11 (BSD like)',
45 'BSD (3 clause)',
46 'BSD (3 clause) GPL (v2)',
47 'BSD (3 clause) ISC',
48 'BSD (3 clause) LGPL (v2 or later)',
49 'BSD (3 clause) LGPL (v2.1 or later)',
50 'BSD (3 clause) MIT/X11 (BSD like)',
51 'BSD (4 clause)',
52 'BSD-like',
54 # TODO(phajdan.jr): Make licensecheck not print BSD-like twice.
55 'BSD-like MIT/X11 (BSD like)',
57 'BSL (v1.0)',
58 'FreeType (BSD like)',
59 'FreeType (BSD like) with patent clause',
60 'GPL (v2) LGPL (v2.1 or later)',
61 'GPL (v2 or later) with Bison parser exception',
62 'GPL (v2 or later) with libtool exception',
63 'GPL (v3 or later) with Bison parser exception',
64 'GPL with Bison parser exception',
65 'Independent JPEG Group License',
66 'ISC',
67 'LGPL (unversioned/unknown version)',
68 'LGPL (v2)',
69 'LGPL (v2 or later)',
70 'LGPL (v2.1)',
71 'LGPL (v2.1 or later)',
72 'LGPL (v3 or later)',
73 'MIT/X11 (BSD like)',
74 'MIT/X11 (BSD like) LGPL (v2.1 or later)',
75 'MPL (v1.0) LGPL (v2 or later)',
76 'MPL (v1.1)',
77 'MPL (v1.1) BSD (3 clause) GPL (v2) LGPL (v2.1 or later)',
78 'MPL (v1.1) BSD (3 clause) LGPL (v2.1 or later)',
79 'MPL (v1.1) BSD-like',
80 'MPL (v1.1) BSD-like GPL (unversioned/unknown version)',
81 'MPL (v1.1) BSD-like GPL (v2) LGPL (v2.1 or later)',
82 'MPL (v1.1) GPL (v2)',
83 'MPL (v1.1) GPL (v2) LGPL (v2 or later)',
84 'MPL (v1.1) GPL (v2) LGPL (v2.1 or later)',
85 'MPL (v1.1) GPL (unversioned/unknown version)',
86 'MPL (v1.1) LGPL (v2 or later)',
87 'MPL (v1.1) LGPL (v2.1 or later)',
88 'MPL (v2.0)',
89 'Ms-PL',
90 'Public domain',
91 'Public domain BSD',
92 'Public domain BSD (3 clause)',
93 'Public domain BSD-like',
94 'Public domain LGPL (v2.1 or later)',
95 'libpng',
96 'zlib/libpng',
97 'SGI Free Software License B',
98 'University of Illinois/NCSA Open Source License (BSD like)',
99 ('University of Illinois/NCSA Open Source License (BSD like) '
100 'MIT/X11 (BSD like)'),
104 PATH_SPECIFIC_WHITELISTED_LICENSES = {
105 'base/third_party/icu': [ # http://crbug.com/98087
106 'UNKNOWN',
109 # http://code.google.com/p/google-breakpad/issues/detail?id=450
110 'breakpad/src': [
111 'UNKNOWN',
114 'chrome/common/extensions/docs/examples': [ # http://crbug.com/98092
115 'UNKNOWN',
117 'courgette/third_party/bsdiff_create.cc': [ # http://crbug.com/98095
118 'UNKNOWN',
120 'native_client': [ # http://crbug.com/98099
121 'UNKNOWN',
123 'native_client/toolchain': [
124 'BSD GPL (v2 or later)',
125 'BSD (2 clause) GPL (v2 or later)',
126 'BSD (3 clause) GPL (v2 or later)',
127 'BSL (v1.0) GPL',
128 'BSL (v1.0) GPL (v3.1)',
129 'GPL',
130 'GPL (unversioned/unknown version)',
131 'GPL (v2)',
132 'GPL (v2 or later)',
133 'GPL (v3.1)',
134 'GPL (v3 or later)',
136 'third_party/WebKit': [
137 'UNKNOWN',
140 # http://code.google.com/p/angleproject/issues/detail?id=217
141 'third_party/angle': [
142 'UNKNOWN',
145 # http://crbug.com/222828
146 # http://bugs.python.org/issue17514
147 'third_party/chromite/third_party/argparse.py': [
148 'UNKNOWN',
151 # http://crbug.com/326117
152 # https://bitbucket.org/chrisatlee/poster/issue/21
153 'third_party/chromite/third_party/poster': [
154 'UNKNOWN',
157 # http://crbug.com/333508
158 'third_party/clang_format/script': [
159 'UNKNOWN',
162 'third_party/devscripts': [
163 'GPL (v2 or later)',
165 'third_party/expat/files/lib': [ # http://crbug.com/98121
166 'UNKNOWN',
168 'third_party/ffmpeg': [
169 'GPL',
170 'GPL (v2)',
171 'GPL (v2 or later)',
172 'UNKNOWN', # http://crbug.com/98123
174 'third_party/fontconfig': [
175 # https://bugs.freedesktop.org/show_bug.cgi?id=73401
176 'UNKNOWN',
178 'third_party/freetype2': [ # http://crbug.com/177319
179 'UNKNOWN',
181 'third_party/hunspell': [ # http://crbug.com/98134
182 'UNKNOWN',
184 'third_party/iccjpeg': [ # http://crbug.com/98137
185 'UNKNOWN',
187 'third_party/icu': [ # http://crbug.com/98301
188 'UNKNOWN',
190 'third_party/lcov': [ # http://crbug.com/98304
191 'UNKNOWN',
193 'third_party/lcov/contrib/galaxy/genflat.pl': [
194 'GPL (v2 or later)',
196 'third_party/libc++/trunk/include/support/solaris': [
197 # http://llvm.org/bugs/show_bug.cgi?id=18291
198 'UNKNOWN',
200 'third_party/libc++/trunk/src/support/solaris/xlocale.c': [
201 # http://llvm.org/bugs/show_bug.cgi?id=18291
202 'UNKNOWN',
204 'third_party/libc++/trunk/test': [
205 # http://llvm.org/bugs/show_bug.cgi?id=18291
206 'UNKNOWN',
208 'third_party/libevent': [ # http://crbug.com/98309
209 'UNKNOWN',
211 'third_party/libjingle/source/talk': [ # http://crbug.com/98310
212 'UNKNOWN',
214 'third_party/libjpeg_turbo': [ # http://crbug.com/98314
215 'UNKNOWN',
217 'third_party/libpng': [ # http://crbug.com/98318
218 'UNKNOWN',
221 # The following files lack license headers, but are trivial.
222 'third_party/libusb/src/libusb/os/poll_posix.h': [
223 'UNKNOWN',
226 'third_party/libvpx/source': [ # http://crbug.com/98319
227 'UNKNOWN',
229 'third_party/libxml': [
230 'UNKNOWN',
232 'third_party/libxslt': [
233 'UNKNOWN',
235 'third_party/lzma_sdk': [
236 'UNKNOWN',
238 'third_party/mesa/src': [
239 'GPL (v2)',
240 'GPL (v3 or later)',
241 'MIT/X11 (BSD like) GPL (v3 or later) with Bison parser exception',
242 'UNKNOWN', # http://crbug.com/98450
244 'third_party/modp_b64': [
245 'UNKNOWN',
247 'third_party/openmax_dl/dl' : [
248 'Khronos Group',
250 'third_party/openssl': [ # http://crbug.com/98451
251 'UNKNOWN',
253 'third_party/ots/tools/ttf-checksum.py': [ # http://code.google.com/p/ots/issues/detail?id=2
254 'UNKNOWN',
256 'third_party/molokocacao': [ # http://crbug.com/98453
257 'UNKNOWN',
259 'third_party/npapi/npspy': [
260 'UNKNOWN',
262 'third_party/ocmock/OCMock': [ # http://crbug.com/98454
263 'UNKNOWN',
265 'third_party/ply/__init__.py': [
266 'UNKNOWN',
268 'third_party/protobuf': [ # http://crbug.com/98455
269 'UNKNOWN',
272 # http://crbug.com/222831
273 # https://bitbucket.org/eliben/pyelftools/issue/12
274 'third_party/pyelftools': [
275 'UNKNOWN',
278 'third_party/scons-2.0.1/engine/SCons': [ # http://crbug.com/98462
279 'UNKNOWN',
281 'third_party/simplejson': [
282 'UNKNOWN',
284 'third_party/skia': [ # http://crbug.com/98463
285 'UNKNOWN',
287 'third_party/snappy/src': [ # http://crbug.com/98464
288 'UNKNOWN',
290 'third_party/smhasher/src': [ # http://crbug.com/98465
291 'UNKNOWN',
293 'third_party/speech-dispatcher/libspeechd.h': [
294 'GPL (v2 or later)',
296 'third_party/sqlite': [
297 'UNKNOWN',
300 # http://crbug.com/334668
301 # MIT license.
302 'tools/swarming_client/third_party/httplib2': [
303 'UNKNOWN',
306 # http://crbug.com/334668
307 # Apache v2.0.
308 'tools/swarming_client/third_party/oauth2client': [
309 'UNKNOWN',
312 # https://github.com/kennethreitz/requests/issues/1610
313 'tools/swarming_client/third_party/requests': [
314 'UNKNOWN',
317 'third_party/swig/Lib/linkruntime.c': [ # http://crbug.com/98585
318 'UNKNOWN',
320 'third_party/talloc': [
321 'GPL (v3 or later)',
322 'UNKNOWN', # http://crbug.com/98588
324 'third_party/tcmalloc': [
325 'UNKNOWN', # http://crbug.com/98589
327 'third_party/tlslite': [
328 'UNKNOWN',
330 'third_party/webdriver': [ # http://crbug.com/98590
331 'UNKNOWN',
334 # https://github.com/html5lib/html5lib-python/issues/125
335 # https://github.com/KhronosGroup/WebGL/issues/435
336 'third_party/webgl/src': [
337 'UNKNOWN',
340 'third_party/webrtc': [ # http://crbug.com/98592
341 'UNKNOWN',
343 'third_party/xdg-utils': [ # http://crbug.com/98593
344 'UNKNOWN',
346 'third_party/yasm/source': [ # http://crbug.com/98594
347 'UNKNOWN',
349 'third_party/zlib/contrib/minizip': [
350 'UNKNOWN',
352 'third_party/zlib/trees.h': [
353 'UNKNOWN',
355 'tools/emacs': [ # http://crbug.com/98595
356 'UNKNOWN',
358 'tools/gyp/test': [
359 'UNKNOWN',
361 'tools/python/google/__init__.py': [
362 'UNKNOWN',
364 'tools/stats_viewer/Properties/AssemblyInfo.cs': [
365 'UNKNOWN',
367 'tools/symsrc/pefile.py': [
368 'UNKNOWN',
370 'tools/telemetry/third_party/pyserial': [
371 # https://sourceforge.net/p/pyserial/feature-requests/35/
372 'UNKNOWN',
374 'v8/test/cctest': [ # http://crbug.com/98597
375 'UNKNOWN',
380 def check_licenses(options, args):
381 # Figure out which directory we have to check.
382 if len(args) == 0:
383 # No directory to check specified, use the repository root.
384 start_dir = options.base_directory
385 elif len(args) == 1:
386 # Directory specified. Start here. It's supposed to be relative to the
387 # base directory.
388 start_dir = os.path.abspath(os.path.join(options.base_directory, args[0]))
389 else:
390 # More than one argument, we don't handle this.
391 PrintUsage()
392 return 1
394 print "Using base directory:", options.base_directory
395 print "Checking:", start_dir
396 print
398 licensecheck_path = os.path.abspath(os.path.join(options.base_directory,
399 'third_party',
400 'devscripts',
401 'licensecheck.pl'))
403 licensecheck = subprocess.Popen([licensecheck_path,
404 '-l', '100',
405 '-r', start_dir],
406 stdout=subprocess.PIPE,
407 stderr=subprocess.PIPE)
408 stdout, stderr = licensecheck.communicate()
409 if options.verbose:
410 print '----------- licensecheck stdout -----------'
411 print stdout
412 print '--------- end licensecheck stdout ---------'
413 if licensecheck.returncode != 0 or stderr:
414 print '----------- licensecheck stderr -----------'
415 print stderr
416 print '--------- end licensecheck stderr ---------'
417 print "\nFAILED\n"
418 return 1
420 used_suppressions = set()
421 errors = []
423 for line in stdout.splitlines():
424 filename, license = line.split(':', 1)
425 filename = os.path.relpath(filename.strip(), options.base_directory)
427 # All files in the build output directory are generated one way or another.
428 # There's no need to check them.
429 if filename.startswith('out/'):
430 continue
432 # For now we're just interested in the license.
433 license = license.replace('*No copyright*', '').strip()
435 # Skip generated files.
436 if 'GENERATED FILE' in license:
437 continue
439 if license in WHITELISTED_LICENSES:
440 continue
442 if not options.ignore_suppressions:
443 matched_prefixes = [
444 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES
445 if filename.startswith(prefix) and
446 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]]
447 if matched_prefixes:
448 used_suppressions.update(set(matched_prefixes))
449 continue
451 errors.append({'filename': filename, 'license': license})
453 if options.json:
454 with open(options.json, 'w') as f:
455 json.dump(errors, f)
457 if errors:
458 for error in errors:
459 print "'%s' has non-whitelisted license '%s'" % (
460 error['filename'], error['license'])
461 print "\nFAILED\n"
462 print "Please read",
463 print "http://www.chromium.org/developers/adding-3rd-party-libraries"
464 print "for more info how to handle the failure."
465 print
466 print "Please respect OWNERS of checklicenses.py. Changes violating"
467 print "this requirement may be reverted."
469 # Do not print unused suppressions so that above message is clearly
470 # visible and gets proper attention. Too much unrelated output
471 # would be distracting and make the important points easier to miss.
473 return 1
475 print "\nSUCCESS\n"
477 if not len(args):
478 unused_suppressions = set(
479 PATH_SPECIFIC_WHITELISTED_LICENSES.iterkeys()).difference(
480 used_suppressions)
481 if unused_suppressions:
482 print "\nNOTE: unused suppressions detected:\n"
483 print '\n'.join(unused_suppressions)
485 return 0
488 def main():
489 default_root = os.path.abspath(
490 os.path.join(os.path.dirname(__file__), '..', '..'))
491 option_parser = optparse.OptionParser()
492 option_parser.add_option('--root', default=default_root,
493 dest='base_directory',
494 help='Specifies the repository root. This defaults '
495 'to "../.." relative to the script file, which '
496 'will normally be the repository root.')
497 option_parser.add_option('-v', '--verbose', action='store_true',
498 default=False, help='Print debug logging')
499 option_parser.add_option('--ignore-suppressions',
500 action='store_true',
501 default=False,
502 help='Ignore path-specific license whitelist.')
503 option_parser.add_option('--json', help='Path to JSON output file')
504 options, args = option_parser.parse_args()
505 return check_licenses(options, args)
508 if '__main__' == __name__:
509 sys.exit(main())