Home | History | Annotate | Download | only in checklicenses
      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