Add unit test for the Settings API Bubble.
[chromium-blink-merge.git] / tools / checklicenses / checklicenses.py
blob38d433153b9db70ddbd247f57a7e979b4f904eff
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 optparse
10 import os.path
11 import subprocess
12 import sys
15 def PrintUsage():
16 print """Usage: python checklicenses.py [--root <root>] [tocheck]
17 --root Specifies the repository root. This defaults to "../.." relative
18 to the script file. This will be correct given the normal location
19 of the script in "<root>/tools/checklicenses".
21 --ignore-suppressions Ignores path-specific license whitelist. Useful when
22 trying to remove a suppression/whitelist entry.
24 tocheck Specifies the directory, relative to root, to check. This defaults
25 to "." so it checks everything.
27 Examples:
28 python checklicenses.py
29 python checklicenses.py --root ~/chromium/src third_party"""
32 WHITELISTED_LICENSES = [
33 'Apache (v2.0)',
34 'Apache (v2.0) BSD (2 clause)',
35 'Apache (v2.0) GPL (v2)',
36 'Apple MIT', # https://fedoraproject.org/wiki/Licensing/Apple_MIT_License
37 'APSL (v2)',
38 'APSL (v2) BSD (4 clause)',
39 'BSD',
40 'BSD (2 clause)',
41 'BSD (2 clause) ISC',
42 'BSD (2 clause) MIT/X11 (BSD like)',
43 'BSD (3 clause)',
44 'BSD (3 clause) GPL (v2)',
45 'BSD (3 clause) ISC',
46 'BSD (3 clause) LGPL (v2 or later)',
47 'BSD (3 clause) LGPL (v2.1 or later)',
48 'BSD (3 clause) MIT/X11 (BSD like)',
49 'BSD (4 clause)',
50 'BSD-like',
52 # TODO(phajdan.jr): Make licensecheck not print BSD-like twice.
53 'BSD-like MIT/X11 (BSD like)',
55 'BSL (v1.0)',
56 'GPL (v2) LGPL (v2.1 or later)',
57 'GPL (v2 or later) with Bison parser exception',
58 'GPL (v2 or later) with libtool exception',
59 'GPL (v3 or later) with Bison parser exception',
60 'GPL with Bison parser exception',
61 'ISC',
62 'LGPL (unversioned/unknown version)',
63 'LGPL (v2)',
64 'LGPL (v2 or later)',
65 'LGPL (v2.1)',
66 'LGPL (v2.1 or later)',
67 'LGPL (v3 or later)',
68 'MIT/X11 (BSD like)',
69 'MIT/X11 (BSD like) LGPL (v2.1 or later)',
70 'MPL (v1.0) LGPL (v2 or later)',
71 'MPL (v1.1)',
72 'MPL (v1.1) BSD (3 clause) GPL (v2) LGPL (v2.1 or later)',
73 'MPL (v1.1) BSD (3 clause) LGPL (v2.1 or later)',
74 'MPL (v1.1) BSD-like',
75 'MPL (v1.1) BSD-like GPL (unversioned/unknown version)',
76 'MPL (v1.1) BSD-like GPL (v2) LGPL (v2.1 or later)',
77 'MPL (v1.1) GPL (v2)',
78 'MPL (v1.1) GPL (v2) LGPL (v2 or later)',
79 'MPL (v1.1) GPL (v2) LGPL (v2.1 or later)',
80 'MPL (v1.1) GPL (unversioned/unknown version)',
81 'MPL (v1.1) LGPL (v2 or later)',
82 'MPL (v1.1) LGPL (v2.1 or later)',
83 'MPL (v2.0)',
84 'Ms-PL',
85 'Public domain',
86 'Public domain BSD',
87 'Public domain BSD (3 clause)',
88 'Public domain BSD-like',
89 'Public domain LGPL (v2.1 or later)',
90 'libpng',
91 'zlib/libpng',
92 'SGI Free Software License B',
93 'University of Illinois/NCSA Open Source License (BSD like)',
94 ('University of Illinois/NCSA Open Source License (BSD like) '
95 'MIT/X11 (BSD like)'),
99 PATH_SPECIFIC_WHITELISTED_LICENSES = {
100 'base/third_party/icu': [ # http://crbug.com/98087
101 'UNKNOWN',
104 # http://code.google.com/p/google-breakpad/issues/detail?id=450
105 'breakpad/src': [
106 'UNKNOWN',
109 'chrome/common/extensions/docs/examples': [ # http://crbug.com/98092
110 'UNKNOWN',
112 'chrome/test/data/gpu/vt': [
113 'UNKNOWN',
115 'courgette/third_party/bsdiff_create.cc': [ # http://crbug.com/98095
116 'UNKNOWN',
118 'data/tab_switching': [
119 'UNKNOWN',
121 'native_client': [ # http://crbug.com/98099
122 'UNKNOWN',
124 'native_client/toolchain': [
125 'BSD GPL (v2 or later)',
126 'BSD (2 clause) GPL (v2 or later)',
127 'BSD (3 clause) GPL (v2 or later)',
128 'BSL (v1.0) GPL',
129 'BSL (v1.0) GPL (v3.1)',
130 'GPL',
131 'GPL (unversioned/unknown version)',
132 'GPL (v2)',
133 'GPL (v2 or later)',
134 'GPL (v3.1)',
135 'GPL (v3 or later)',
137 'net/tools/spdyshark': [
138 'GPL (v2 or later)',
139 'UNKNOWN',
141 'third_party/WebKit': [
142 'UNKNOWN',
145 # http://code.google.com/p/angleproject/issues/detail?id=217
146 'third_party/angle': [
147 'UNKNOWN',
150 # http://crbug.com/222828
151 # http://bugs.python.org/issue17514
152 'third_party/chromite/third_party/argparse.py': [
153 'UNKNOWN',
156 # http://crbug.com/326117
157 # https://bitbucket.org/chrisatlee/poster/issue/21
158 'third_party/chromite/third_party/poster': [
159 'UNKNOWN',
162 # http://crbug.com/333508
163 'third_party/clang_format/script': [
164 'UNKNOWN',
166 'third_party/clang_format/scripts': [
167 'UNKNOWN',
170 # Not used. http://crbug.com/156020
171 # Using third_party/cros_dbus_cplusplus/cros_dbus_cplusplus.gyp instead.
172 'third_party/cros_dbus_cplusplus/source/autogen.sh': [
173 'UNKNOWN',
175 # Included in the source tree but not built. http://crbug.com/156020
176 'third_party/cros_dbus_cplusplus/source/examples': [
177 'UNKNOWN',
179 'third_party/devscripts': [
180 'GPL (v2 or later)',
182 'third_party/expat/files/lib': [ # http://crbug.com/98121
183 'UNKNOWN',
185 'third_party/ffmpeg': [
186 'GPL',
187 'GPL (v2)',
188 'GPL (v2 or later)',
189 'UNKNOWN', # http://crbug.com/98123
191 'third_party/fontconfig': [
192 # https://bugs.freedesktop.org/show_bug.cgi?id=73401
193 'UNKNOWN',
195 'third_party/freetype2': [ # http://crbug.com/177319
196 'UNKNOWN',
198 'third_party/gles2_conform/GTF_ES': [ # http://crbug.com/98131
199 'UNKNOWN',
201 'third_party/hunspell': [ # http://crbug.com/98134
202 'UNKNOWN',
204 'third_party/iccjpeg': [ # http://crbug.com/98137
205 'UNKNOWN',
207 'third_party/icu': [ # http://crbug.com/98301
208 'UNKNOWN',
210 'third_party/jemalloc': [ # http://crbug.com/98302
211 'UNKNOWN',
213 'third_party/JSON': [
214 'Perl', # Build-only.
215 # License missing upstream on 3 minor files.
216 'UNKNOWN', # https://rt.cpan.org/Public/Bug/Display.html?id=85915
218 'third_party/lcov': [ # http://crbug.com/98304
219 'UNKNOWN',
221 'third_party/lcov/contrib/galaxy/genflat.pl': [
222 'GPL (v2 or later)',
224 'third_party/libc++/trunk/include/support/solaris': [
225 # http://llvm.org/bugs/show_bug.cgi?id=18291
226 'UNKNOWN',
228 'third_party/libc++/trunk/src/support/solaris/xlocale.c': [
229 # http://llvm.org/bugs/show_bug.cgi?id=18291
230 'UNKNOWN',
232 'third_party/libc++/trunk/test': [
233 # http://llvm.org/bugs/show_bug.cgi?id=18291
234 'UNKNOWN',
236 'third_party/libevent': [ # http://crbug.com/98309
237 'UNKNOWN',
239 'third_party/libjingle/source/talk': [ # http://crbug.com/98310
240 'UNKNOWN',
242 'third_party/libjpeg': [ # http://crbug.com/98313
243 'UNKNOWN',
245 'third_party/libjpeg_turbo': [ # http://crbug.com/98314
246 'UNKNOWN',
248 'third_party/libpng': [ # http://crbug.com/98318
249 'UNKNOWN',
252 # The following files lack license headers, but are trivial.
253 'third_party/libusb/src/libusb/os/poll_posix.h': [
254 'UNKNOWN',
257 'third_party/libvpx/source': [ # http://crbug.com/98319
258 'UNKNOWN',
260 'third_party/libvpx/source/libvpx/examples/includes': [
261 'GPL (v2 or later)',
263 'third_party/libxml': [
264 'UNKNOWN',
266 'third_party/libxslt': [
267 'UNKNOWN',
269 'third_party/lzma_sdk': [
270 'UNKNOWN',
272 'third_party/mesa/src': [
273 'GPL (v2)',
274 'GPL (v3 or later)',
275 'MIT/X11 (BSD like) GPL (v3 or later) with Bison parser exception',
276 'UNKNOWN', # http://crbug.com/98450
278 'third_party/modp_b64': [
279 'UNKNOWN',
281 'third_party/openmax_dl/dl' : [
282 'Khronos Group',
284 'third_party/openssl': [ # http://crbug.com/98451
285 'UNKNOWN',
287 'third_party/ots/tools/ttf-checksum.py': [ # http://code.google.com/p/ots/issues/detail?id=2
288 'UNKNOWN',
290 'third_party/molokocacao': [ # http://crbug.com/98453
291 'UNKNOWN',
293 'third_party/npapi/npspy': [
294 'UNKNOWN',
296 'third_party/ocmock/OCMock': [ # http://crbug.com/98454
297 'UNKNOWN',
299 'third_party/ply/__init__.py': [
300 'UNKNOWN',
302 'third_party/protobuf': [ # http://crbug.com/98455
303 'UNKNOWN',
306 # http://crbug.com/222831
307 # https://bitbucket.org/eliben/pyelftools/issue/12
308 'third_party/pyelftools': [
309 'UNKNOWN',
312 'third_party/scons-2.0.1/engine/SCons': [ # http://crbug.com/98462
313 'UNKNOWN',
315 'third_party/simplejson': [
316 'UNKNOWN',
318 'third_party/skia': [ # http://crbug.com/98463
319 'UNKNOWN',
321 'third_party/snappy/src': [ # http://crbug.com/98464
322 'UNKNOWN',
324 'third_party/smhasher/src': [ # http://crbug.com/98465
325 'UNKNOWN',
327 'third_party/speech-dispatcher/libspeechd.h': [
328 'GPL (v2 or later)',
330 'third_party/sqlite': [
331 'UNKNOWN',
334 # https://code.google.com/p/colorama/issues/detail?id=44
335 'tools/swarming_client/third_party/colorama': [
336 'UNKNOWN',
339 # http://crbug.com/334668
340 # MIT license.
341 'tools/swarming_client/third_party/httplib2': [
342 'UNKNOWN',
345 # http://crbug.com/334668
346 # Apache v2.0.
347 'tools/swarming_client/third_party/oauth2client': [
348 'UNKNOWN',
351 # https://github.com/kennethreitz/requests/issues/1610
352 'tools/swarming_client/third_party/requests': [
353 'UNKNOWN',
356 'third_party/swig/Lib/linkruntime.c': [ # http://crbug.com/98585
357 'UNKNOWN',
359 'third_party/talloc': [
360 'GPL (v3 or later)',
361 'UNKNOWN', # http://crbug.com/98588
363 'third_party/tcmalloc': [
364 'UNKNOWN', # http://crbug.com/98589
366 'third_party/tlslite': [
367 'UNKNOWN',
369 'third_party/webdriver': [ # http://crbug.com/98590
370 'UNKNOWN',
373 # https://github.com/html5lib/html5lib-python/issues/125
374 # https://github.com/KhronosGroup/WebGL/issues/435
375 'third_party/webgl/src': [
376 'UNKNOWN',
379 'third_party/webrtc': [ # http://crbug.com/98592
380 'UNKNOWN',
382 'third_party/xdg-utils': [ # http://crbug.com/98593
383 'UNKNOWN',
385 'third_party/yasm/source': [ # http://crbug.com/98594
386 'UNKNOWN',
388 'third_party/zlib/contrib/minizip': [
389 'UNKNOWN',
391 'third_party/zlib/trees.h': [
392 'UNKNOWN',
394 'tools/emacs': [ # http://crbug.com/98595
395 'UNKNOWN',
397 'tools/gyp/test': [
398 'UNKNOWN',
400 'tools/histograms': [
401 'UNKNOWN',
403 'tools/python/google/__init__.py': [
404 'UNKNOWN',
406 'tools/stats_viewer/Properties/AssemblyInfo.cs': [
407 'UNKNOWN',
409 'tools/symsrc/pefile.py': [
410 'UNKNOWN',
412 'tools/telemetry/third_party/pyserial': [
413 # https://sourceforge.net/p/pyserial/feature-requests/35/
414 'UNKNOWN',
416 'v8/test/cctest': [ # http://crbug.com/98597
417 'UNKNOWN',
422 def check_licenses(options, args):
423 # Figure out which directory we have to check.
424 if len(args) == 0:
425 # No directory to check specified, use the repository root.
426 start_dir = options.base_directory
427 elif len(args) == 1:
428 # Directory specified. Start here. It's supposed to be relative to the
429 # base directory.
430 start_dir = os.path.abspath(os.path.join(options.base_directory, args[0]))
431 else:
432 # More than one argument, we don't handle this.
433 PrintUsage()
434 return 1
436 print "Using base directory:", options.base_directory
437 print "Checking:", start_dir
438 print
440 licensecheck_path = os.path.abspath(os.path.join(options.base_directory,
441 'third_party',
442 'devscripts',
443 'licensecheck.pl'))
445 licensecheck = subprocess.Popen([licensecheck_path,
446 '-l', '100',
447 '-r', start_dir],
448 stdout=subprocess.PIPE,
449 stderr=subprocess.PIPE)
450 stdout, stderr = licensecheck.communicate()
451 if options.verbose:
452 print '----------- licensecheck stdout -----------'
453 print stdout
454 print '--------- end licensecheck stdout ---------'
455 if licensecheck.returncode != 0 or stderr:
456 print '----------- licensecheck stderr -----------'
457 print stderr
458 print '--------- end licensecheck stderr ---------'
459 print "\nFAILED\n"
460 return 1
462 used_suppressions = set()
464 success = True
465 for line in stdout.splitlines():
466 filename, license = line.split(':', 1)
467 filename = os.path.relpath(filename.strip(), options.base_directory)
469 # All files in the build output directory are generated one way or another.
470 # There's no need to check them.
471 if filename.startswith('out/'):
472 continue
474 # For now we're just interested in the license.
475 license = license.replace('*No copyright*', '').strip()
477 # Skip generated files.
478 if 'GENERATED FILE' in license:
479 continue
481 if license in WHITELISTED_LICENSES:
482 continue
484 if not options.ignore_suppressions:
485 matched_prefixes = [
486 prefix for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES
487 if filename.startswith(prefix) and
488 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]]
489 if matched_prefixes:
490 used_suppressions.update(set(matched_prefixes))
491 continue
493 print "'%s' has non-whitelisted license '%s'" % (filename, license)
494 success = False
496 if success:
497 print "\nSUCCESS\n"
499 unused_suppressions = set(
500 PATH_SPECIFIC_WHITELISTED_LICENSES.keys()).difference(used_suppressions)
501 if unused_suppressions:
502 print "\nNOTE: unused suppressions detected:\n"
503 print '\n'.join(unused_suppressions)
505 return 0
506 else:
507 print "\nFAILED\n"
508 print "Please read",
509 print "http://www.chromium.org/developers/adding-3rd-party-libraries"
510 print "for more info how to handle the failure."
511 print
512 print "Please respect OWNERS of checklicenses.py. Changes violating"
513 print "this requirement may be reverted."
515 # Do not print unused suppressions so that above message is clearly
516 # visible and gets proper attention. Too much unrelated output
517 # would be distracting and make the important points easier to miss.
519 return 1
522 def main():
523 default_root = os.path.abspath(
524 os.path.join(os.path.dirname(__file__), '..', '..'))
525 option_parser = optparse.OptionParser()
526 option_parser.add_option('--root', default=default_root,
527 dest='base_directory',
528 help='Specifies the repository root. This defaults '
529 'to "../.." relative to the script file, which '
530 'will normally be the repository root.')
531 option_parser.add_option('-v', '--verbose', action='store_true',
532 default=False, help='Print debug logging')
533 option_parser.add_option('--ignore-suppressions',
534 action='store_true',
535 default=False,
536 help='Ignore path-specific license whitelist.')
537 options, args = option_parser.parse_args()
538 return check_licenses(options, args)
541 if '__main__' == __name__:
542 sys.exit(main())