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. 5 6 """Makes sure that all files contain proper licensing information.""" 7 8 9 import optparse 10 import os.path 11 import subprocess 12 import sys 13 14 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". 20 21 --ignore-suppressions Ignores path-specific license whitelist. Useful when 22 trying to remove a suppression/whitelist entry. 23 24 tocheck Specifies the directory, relative to root, to check. This defaults 25 to "." so it checks everything. 26 27 Examples: 28 python checklicenses.py 29 python checklicenses.py --root ~/chromium/src third_party""" 30 31 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', 51 52 # TODO(phajdan.jr): Make licensecheck not print BSD-like twice. 53 'BSD-like MIT/X11 (BSD like)', 54 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 'MPL (v1.0) LGPL (v2 or later)', 70 'MPL (v1.1)', 71 'MPL (v1.1) BSD (3 clause) GPL (v2) LGPL (v2.1 or later)', 72 'MPL (v1.1) BSD (3 clause) LGPL (v2.1 or later)', 73 'MPL (v1.1) BSD-like', 74 'MPL (v1.1) BSD-like GPL (unversioned/unknown version)', 75 'MPL (v1.1) BSD-like GPL (v2) LGPL (v2.1 or later)', 76 'MPL (v1.1) GPL (v2)', 77 'MPL (v1.1) GPL (v2) LGPL (v2 or later)', 78 'MPL (v1.1) GPL (v2) LGPL (v2.1 or later)', 79 'MPL (v1.1) GPL (unversioned/unknown version)', 80 'MPL (v1.1) LGPL (v2 or later)', 81 'MPL (v1.1) LGPL (v2.1 or later)', 82 'MPL (v2.0)', 83 'Ms-PL', 84 'Public domain', 85 'Public domain BSD', 86 'Public domain BSD (3 clause)', 87 'Public domain BSD-like', 88 'Public domain LGPL (v2.1 or later)', 89 'libpng', 90 'zlib/libpng', 91 'SGI Free Software License B', 92 'University of Illinois/NCSA Open Source License (BSD like)', 93 ] 94 95 96 PATH_SPECIFIC_WHITELISTED_LICENSES = { 97 'base/hash.cc': [ # http://crbug.com/98100 98 'UNKNOWN', 99 ], 100 'base/third_party/icu': [ # http://crbug.com/98087 101 'UNKNOWN', 102 ], 103 104 # http://code.google.com/p/google-breakpad/issues/detail?id=450 105 'breakpad/src': [ 106 'UNKNOWN', 107 ], 108 109 'chrome/common/extensions/docs/examples': [ # http://crbug.com/98092 110 'UNKNOWN', 111 ], 112 'chrome/test/data/gpu/vt': [ 113 'UNKNOWN', 114 ], 115 'chrome/test/data/layout_tests/LayoutTests': [ 116 'UNKNOWN', 117 ], 118 'courgette/third_party/bsdiff_create.cc': [ # http://crbug.com/98095 119 'UNKNOWN', 120 ], 121 'data/mozilla_js_tests': [ 122 'UNKNOWN', 123 ], 124 'data/page_cycler': [ 125 'UNKNOWN', 126 'GPL (v2 or later)', 127 ], 128 'data/tab_switching': [ 129 'UNKNOWN', 130 ], 131 'native_client': [ # http://crbug.com/98099 132 'UNKNOWN', 133 ], 134 'native_client/toolchain': [ 135 'BSD GPL (v2 or later)', 136 'BSD (2 clause) GPL (v2 or later)', 137 'BSD (3 clause) GPL (v2 or later)', 138 'BSL (v1.0) GPL', 139 'BSL (v1.0) GPL (v3.1)', 140 'GPL', 141 'GPL (unversioned/unknown version)', 142 'GPL (v2)', 143 'GPL (v2 or later)', 144 'GPL (v3.1)', 145 'GPL (v3 or later)', 146 ], 147 'net/tools/spdyshark': [ 148 'GPL (v2 or later)', 149 'UNKNOWN', 150 ], 151 'third_party/WebKit': [ 152 'UNKNOWN', 153 ], 154 'third_party/WebKit/Websites/webkit.org/blog/wp-content/plugins/' 155 'akismet/akismet.php': [ 156 'GPL (v2 or later)' 157 ], 158 'third_party/WebKit/Source/JavaScriptCore/tests/mozilla': [ 159 'GPL', 160 'GPL (v2 or later)', 161 'GPL (unversioned/unknown version)', 162 ], 163 'third_party/active_doc': [ # http://crbug.com/98113 164 'UNKNOWN', 165 ], 166 167 # http://code.google.com/p/angleproject/issues/detail?id=217 168 'third_party/angle': [ 169 'UNKNOWN', 170 ], 171 172 'third_party/bsdiff/mbsdiff.cc': [ 173 'UNKNOWN', 174 ], 175 'third_party/bzip2': [ 176 'UNKNOWN', 177 ], 178 179 # http://crbug.com/222828 180 # http://bugs.python.org/issue17514 181 'third_party/chromite/third_party/argparse.py': [ 182 'UNKNOWN', 183 ], 184 185 # Not used. http://crbug.com/156020 186 # Using third_party/cros_dbus_cplusplus/cros_dbus_cplusplus.gyp instead. 187 'third_party/cros_dbus_cplusplus/source/autogen.sh': [ 188 'UNKNOWN', 189 ], 190 # Included in the source tree but not built. http://crbug.com/156020 191 'third_party/cros_dbus_cplusplus/source/examples': [ 192 'UNKNOWN', 193 ], 194 'third_party/devscripts': [ 195 'GPL (v2 or later)', 196 ], 197 'third_party/expat/files/lib': [ # http://crbug.com/98121 198 'UNKNOWN', 199 ], 200 'third_party/ffmpeg': [ 201 'GPL', 202 'GPL (v2)', 203 'GPL (v2 or later)', 204 'UNKNOWN', # http://crbug.com/98123 205 ], 206 'third_party/findbugs/doc': [ # http://crbug.com/157206 207 'UNKNOWN', 208 ], 209 'third_party/freetype2': [ # http://crbug.com/177319 210 'UNKNOWN', 211 ], 212 'third_party/gles2_book': [ # http://crbug.com/98130 213 'UNKNOWN', 214 ], 215 'third_party/gles2_conform/GTF_ES': [ # http://crbug.com/98131 216 'UNKNOWN', 217 ], 218 'third_party/harfbuzz': [ # http://crbug.com/98133 219 'UNKNOWN', 220 ], 221 'third_party/hunspell': [ # http://crbug.com/98134 222 'UNKNOWN', 223 ], 224 'third_party/hyphen/hyphen.tex': [ # http://crbug.com/157375 225 'UNKNOWN', 226 ], 227 'third_party/iccjpeg': [ # http://crbug.com/98137 228 'UNKNOWN', 229 ], 230 'third_party/icu': [ # http://crbug.com/98301 231 'UNKNOWN', 232 ], 233 'third_party/jemalloc': [ # http://crbug.com/98302 234 'UNKNOWN', 235 ], 236 'third_party/JSON': [ 237 'Perl', # Build-only. 238 # License missing upstream on 3 minor files. 239 'UNKNOWN', # https://rt.cpan.org/Public/Bug/Display.html?id=85915 240 ], 241 'third_party/lcov': [ # http://crbug.com/98304 242 'UNKNOWN', 243 ], 244 'third_party/lcov/contrib/galaxy/genflat.pl': [ 245 'GPL (v2 or later)', 246 ], 247 'third_party/lcov-1.9/contrib/galaxy/genflat.pl': [ 248 'GPL (v2 or later)', 249 ], 250 'third_party/libevent': [ # http://crbug.com/98309 251 'UNKNOWN', 252 ], 253 'third_party/libjingle/source/talk': [ # http://crbug.com/98310 254 'UNKNOWN', 255 ], 256 'third_party/libjingle/source_internal/talk': [ # http://crbug.com/98310 257 'UNKNOWN', 258 ], 259 'third_party/libjpeg': [ # http://crbug.com/98313 260 'UNKNOWN', 261 ], 262 'third_party/libjpeg_turbo': [ # http://crbug.com/98314 263 'UNKNOWN', 264 ], 265 'third_party/libpng': [ # http://crbug.com/98318 266 'UNKNOWN', 267 ], 268 269 # The following files lack license headers, but are trivial. 270 'third_party/libusb/src/libusb/os/poll_posix.h': [ 271 'UNKNOWN', 272 ], 273 'third_party/libusb/src/libusb/version.h': [ 274 'UNKNOWN', 275 ], 276 'third_party/libusb/src/autogen.sh': [ 277 'UNKNOWN', 278 ], 279 'third_party/libusb/src/config.h': [ 280 'UNKNOWN', 281 ], 282 'third_party/libusb/src/msvc/config.h': [ 283 'UNKNOWN', 284 ], 285 286 'third_party/libvpx/source': [ # http://crbug.com/98319 287 'UNKNOWN', 288 ], 289 'third_party/libvpx/source/libvpx/examples/includes': [ 290 'GPL (v2 or later)', 291 ], 292 'third_party/libxml': [ 293 'UNKNOWN', 294 ], 295 'third_party/libxslt': [ 296 'UNKNOWN', 297 ], 298 'third_party/lzma_sdk': [ 299 'UNKNOWN', 300 ], 301 'third_party/mesa/src': [ 302 'GPL (v2)', 303 'GPL (v3 or later)', 304 'MIT/X11 (BSD like) GPL (v3 or later) with Bison parser exception', 305 'UNKNOWN', # http://crbug.com/98450 306 ], 307 'third_party/modp_b64': [ 308 'UNKNOWN', 309 ], 310 'third_party/npapi/npspy/extern/java': [ 311 'GPL (unversioned/unknown version)', 312 ], 313 'third_party/openmax_dl/dl' : [ 314 'Khronos Group', 315 ], 316 'third_party/openssl': [ # http://crbug.com/98451 317 'UNKNOWN', 318 ], 319 'third_party/ots/tools/ttf-checksum.py': [ # http://code.google.com/p/ots/issues/detail?id=2 320 'UNKNOWN', 321 ], 322 'third_party/molokocacao': [ # http://crbug.com/98453 323 'UNKNOWN', 324 ], 325 'third_party/npapi/npspy': [ 326 'UNKNOWN', 327 ], 328 'third_party/ocmock/OCMock': [ # http://crbug.com/98454 329 'UNKNOWN', 330 ], 331 'third_party/ply/__init__.py': [ 332 'UNKNOWN', 333 ], 334 'third_party/protobuf': [ # http://crbug.com/98455 335 'UNKNOWN', 336 ], 337 338 # http://crbug.com/222831 339 # https://bitbucket.org/eliben/pyelftools/issue/12 340 'third_party/pyelftools': [ 341 'UNKNOWN', 342 ], 343 344 'third_party/pylib': [ 345 'UNKNOWN', 346 ], 347 'third_party/scons-2.0.1/engine/SCons': [ # http://crbug.com/98462 348 'UNKNOWN', 349 ], 350 'third_party/simplejson': [ 351 'UNKNOWN', 352 ], 353 'third_party/skia': [ # http://crbug.com/98463 354 'UNKNOWN', 355 ], 356 'third_party/snappy/src': [ # http://crbug.com/98464 357 'UNKNOWN', 358 ], 359 'third_party/smhasher/src': [ # http://crbug.com/98465 360 'UNKNOWN', 361 ], 362 'third_party/speech-dispatcher/libspeechd.h': [ 363 'GPL (v2 or later)', 364 ], 365 'third_party/sqlite': [ 366 'UNKNOWN', 367 ], 368 'third_party/swig/Lib/linkruntime.c': [ # http://crbug.com/98585 369 'UNKNOWN', 370 ], 371 'third_party/talloc': [ 372 'GPL (v3 or later)', 373 'UNKNOWN', # http://crbug.com/98588 374 ], 375 'third_party/tcmalloc': [ 376 'UNKNOWN', # http://crbug.com/98589 377 ], 378 'third_party/tlslite': [ 379 'UNKNOWN', 380 ], 381 'third_party/webdriver': [ # http://crbug.com/98590 382 'UNKNOWN', 383 ], 384 'third_party/webrtc': [ # http://crbug.com/98592 385 'UNKNOWN', 386 ], 387 'third_party/xdg-utils': [ # http://crbug.com/98593 388 'UNKNOWN', 389 ], 390 'third_party/yasm/source': [ # http://crbug.com/98594 391 'UNKNOWN', 392 ], 393 'third_party/zlib/contrib/minizip': [ 394 'UNKNOWN', 395 ], 396 'third_party/zlib/trees.h': [ 397 'UNKNOWN', 398 ], 399 'tools/dromaeo_benchmark_runner/dromaeo_benchmark_runner.py': [ 400 'UNKNOWN', 401 ], 402 'tools/emacs': [ # http://crbug.com/98595 403 'UNKNOWN', 404 ], 405 'tools/grit/grit/node/custom/__init__.py': [ 406 'UNKNOWN', 407 ], 408 'tools/gyp/test': [ 409 'UNKNOWN', 410 ], 411 'tools/histograms': [ 412 'UNKNOWN', 413 ], 414 'tools/memory_watcher': [ 415 'UNKNOWN', 416 ], 417 'tools/playback_benchmark': [ 418 'UNKNOWN', 419 ], 420 'tools/python/google/__init__.py': [ 421 'UNKNOWN', 422 ], 423 'tools/site_compare': [ 424 'UNKNOWN', 425 ], 426 'tools/stats_viewer/Properties/AssemblyInfo.cs': [ 427 'UNKNOWN', 428 ], 429 'tools/symsrc/pefile.py': [ 430 'UNKNOWN', 431 ], 432 'v8/test/cctest': [ # http://crbug.com/98597 433 'UNKNOWN', 434 ], 435 'webkit/data/ico_decoder': [ 436 'UNKNOWN', 437 ], 438 } 439 440 441 def check_licenses(options, args): 442 # Figure out which directory we have to check. 443 if len(args) == 0: 444 # No directory to check specified, use the repository root. 445 start_dir = options.base_directory 446 elif len(args) == 1: 447 # Directory specified. Start here. It's supposed to be relative to the 448 # base directory. 449 start_dir = os.path.abspath(os.path.join(options.base_directory, args[0])) 450 else: 451 # More than one argument, we don't handle this. 452 PrintUsage() 453 return 1 454 455 print "Using base directory:", options.base_directory 456 print "Checking:", start_dir 457 print 458 459 licensecheck_path = os.path.abspath(os.path.join(options.base_directory, 460 'third_party', 461 'devscripts', 462 'licensecheck.pl')) 463 464 licensecheck = subprocess.Popen([licensecheck_path, 465 '-l', '100', 466 '-r', start_dir], 467 stdout=subprocess.PIPE, 468 stderr=subprocess.PIPE) 469 stdout, stderr = licensecheck.communicate() 470 if options.verbose: 471 print '----------- licensecheck stdout -----------' 472 print stdout 473 print '--------- end licensecheck stdout ---------' 474 if licensecheck.returncode != 0 or stderr: 475 print '----------- licensecheck stderr -----------' 476 print stderr 477 print '--------- end licensecheck stderr ---------' 478 print "\nFAILED\n" 479 return 1 480 481 success = True 482 for line in stdout.splitlines(): 483 filename, license = line.split(':', 1) 484 filename = os.path.relpath(filename.strip(), options.base_directory) 485 486 # All files in the build output directory are generated one way or another. 487 # There's no need to check them. 488 if filename.startswith('out/') or filename.startswith('sconsbuild/'): 489 continue 490 491 # For now we're just interested in the license. 492 license = license.replace('*No copyright*', '').strip() 493 494 # Skip generated files. 495 if 'GENERATED FILE' in license: 496 continue 497 498 if license in WHITELISTED_LICENSES: 499 continue 500 501 if not options.ignore_suppressions: 502 found_path_specific = False 503 for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES: 504 if (filename.startswith(prefix) and 505 license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]): 506 found_path_specific = True 507 break 508 if found_path_specific: 509 continue 510 511 print "'%s' has non-whitelisted license '%s'" % (filename, license) 512 success = False 513 514 if success: 515 print "\nSUCCESS\n" 516 return 0 517 else: 518 print "\nFAILED\n" 519 print "Please read", 520 print "http://www.chromium.org/developers/adding-3rd-party-libraries" 521 print "for more info how to handle the failure." 522 print 523 print "Please respect OWNERS of checklicenses.py. Changes violating" 524 print "this requirement may be reverted." 525 return 1 526 527 528 def main(): 529 default_root = os.path.abspath( 530 os.path.join(os.path.dirname(__file__), '..', '..')) 531 option_parser = optparse.OptionParser() 532 option_parser.add_option('--root', default=default_root, 533 dest='base_directory', 534 help='Specifies the repository root. This defaults ' 535 'to "../.." relative to the script file, which ' 536 'will normally be the repository root.') 537 option_parser.add_option('-v', '--verbose', action='store_true', 538 default=False, help='Print debug logging') 539 option_parser.add_option('--ignore-suppressions', 540 action='store_true', 541 default=False, 542 help='Ignore path-specific license whitelist.') 543 options, args = option_parser.parse_args() 544 return check_licenses(options, args) 545 546 547 if '__main__' == __name__: 548 sys.exit(main()) 549