Add an extension override bubble and warning box for proxy extensions. (2nd attempt...
[chromium-blink-merge.git] / tools / checklicenses / checklicenses.py
blobc657efff9b75616f7fb4989ba3b4250f1f1e9d38
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 # https://code.google.com/p/colorama/issues/detail?id=44
301 'tools/swarming_client/third_party/colorama': [
302 'UNKNOWN',
305 # http://crbug.com/334668
306 # MIT license.
307 'tools/swarming_client/third_party/httplib2': [
308 'UNKNOWN',
311 # http://crbug.com/334668
312 # Apache v2.0.
313 'tools/swarming_client/third_party/oauth2client': [
314 'UNKNOWN',
317 # https://github.com/kennethreitz/requests/issues/1610
318 'tools/swarming_client/third_party/requests': [
319 'UNKNOWN',
322 'third_party/swig/Lib/linkruntime.c': [ # http://crbug.com/98585
323 'UNKNOWN',
325 'third_party/talloc': [
326 'GPL (v3 or later)',
327 'UNKNOWN', # http://crbug.com/98588
329 'third_party/tcmalloc': [
330 'UNKNOWN', # http://crbug.com/98589
332 'third_party/tlslite': [
333 'UNKNOWN',
335 'third_party/webdriver': [ # http://crbug.com/98590
336 'UNKNOWN',
339 # https://github.com/html5lib/html5lib-python/issues/125
340 # https://github.com/KhronosGroup/WebGL/issues/435
341 'third_party/webgl/src': [
342 'UNKNOWN',
345 'third_party/webrtc': [ # http://crbug.com/98592
346 'UNKNOWN',
348 'third_party/xdg-utils': [ # http://crbug.com/98593
349 'UNKNOWN',
351 'third_party/yasm/source': [ # http://crbug.com/98594
352 'UNKNOWN',
354 'third_party/zlib/contrib/minizip': [
355 'UNKNOWN',
357 'third_party/zlib/trees.h': [
358 'UNKNOWN',
360 'tools/emacs': [ # http://crbug.com/98595
361 'UNKNOWN',
363 'tools/gyp/test': [
364 'UNKNOWN',
366 'tools/python/google/__init__.py': [
367 'UNKNOWN',
369 'tools/stats_viewer/Properties/AssemblyInfo.cs': [
370 'UNKNOWN',
372 'tools/symsrc/pefile.py': [
373 'UNKNOWN',
375 'tools/telemetry/third_party/pyserial': [
376 # https://sourceforge.net/p/pyserial/feature-requests/35/
377 'UNKNOWN',
379 'v8/test/cctest': [ # http://crbug.com/98597
380 'UNKNOWN',
385 def check_licenses(options, args):
386 # Figure out which directory we have to check.
387 if len(args) == 0:
388 # No directory to check specified, use the repository root.
389 start_dir = options.base_directory
390 elif len(args) == 1:
391 # Directory specified. Start here. It's supposed to be relative to the
392 # base directory.
393 start_dir = os.path.abspath(os.path.join(options.base_directory, args[0]))
394 else:
395 # More than one argument, we don't handle this.
396 PrintUsage()
397 return 1
399 print "Using base directory:", options.base_directory
400 print "Checking:", start_dir
401 print
403 licensecheck_path = os.path.abspath(os.path.join(options.base_directory,
404 'third_party',
405 'devscripts',
406 'licensecheck.pl'))
408 licensecheck = subprocess.Popen([licensecheck_path,
409 '-l', '100',
410 '-r', start_dir],
411 stdout=subprocess.PIPE,
412 stderr=subprocess.PIPE)
413 stdout, stderr = licensecheck.communicate()
414 if options.verbose:
415 print '----------- licensecheck stdout -----------'
416 print stdout
417 print '--------- end licensecheck stdout ---------'
418 if licensecheck.returncode != 0 or stderr:
419 print '----------- licensecheck stderr -----------'
420 print stderr
421 print '--------- end licensecheck stderr ---------'
422 print "\nFAILED\n"
423 return 1
425 used_suppressions = set()
426 errors = []
428 for line in stdout.splitlines():
429 filename, license = line.split(':', 1)
430 filename = os.path.relpath(filename.strip(), options.base_directory)
432 # All files in the build output directory are generated one way or another.
433 # There's no need to check them.
434 if filename.startswith('out/'):
435 continue
437 # For now we're just interested in the license.
438 license = license.replace('*No copyright*', '').strip()
440 # Skip generated files.
441 if 'GENERATED FILE' in license:
442 continue
444 if license in WHITELISTED_LICENSES:
445 continue
447 if not options.ignore_suppressions:
448 matched_prefixes = [
449 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES
450 if filename.startswith(prefix) and
451 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]]
452 if matched_prefixes:
453 used_suppressions.update(set(matched_prefixes))
454 continue
456 errors.append({'filename': filename, 'license': license})
458 if options.json:
459 with open(options.json, 'w') as f:
460 json.dump(errors, f)
462 if errors:
463 for error in errors:
464 print "'%s' has non-whitelisted license '%s'" % (
465 error['filename'], error['license'])
466 print "\nFAILED\n"
467 print "Please read",
468 print "http://www.chromium.org/developers/adding-3rd-party-libraries"
469 print "for more info how to handle the failure."
470 print
471 print "Please respect OWNERS of checklicenses.py. Changes violating"
472 print "this requirement may be reverted."
474 # Do not print unused suppressions so that above message is clearly
475 # visible and gets proper attention. Too much unrelated output
476 # would be distracting and make the important points easier to miss.
478 return 1
480 print "\nSUCCESS\n"
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)
490 return 0
493 def main():
494 default_root = os.path.abspath(
495 os.path.join(os.path.dirname(__file__), '..', '..'))
496 option_parser = optparse.OptionParser()
497 option_parser.add_option('--root', default=default_root,
498 dest='base_directory',
499 help='Specifies the repository root. This defaults '
500 'to "../.." relative to the script file, which '
501 'will normally be the repository root.')
502 option_parser.add_option('-v', '--verbose', action='store_true',
503 default=False, help='Print debug logging')
504 option_parser.add_option('--ignore-suppressions',
505 action='store_true',
506 default=False,
507 help='Ignore path-specific license whitelist.')
508 option_parser.add_option('--json', help='Path to JSON output file')
509 options, args = option_parser.parse_args()
510 return check_licenses(options, args)
513 if '__main__' == __name__:
514 sys.exit(main())