summaryrefslogtreecommitdiffstats
path: root/tools/checklicenses.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtools/checklicenses.py262
1 files changed, 262 insertions, 0 deletions
diff --git a/tools/checklicenses.py b/tools/checklicenses.py
new file mode 100755
index 0000000..192fecb
--- /dev/null
+++ b/tools/checklicenses.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python3
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+"""Makes sure that all files contain proper licensing information."""
+
+
+import optparse
+import os.path
+import subprocess
+import sys
+
+
+def PrintUsage():
+ print("""Usage: python checklicenses.py [--root <root>] [tocheck]
+ --root Specifies the repository root. This defaults to ".." relative
+ to the script file. This will be correct given the normal location
+ of the script in "<root>/tools".
+
+ --ignore-suppressions Ignores path-specific allowed license. Useful when
+ trying to remove a suppression/allowed entry.
+
+ --list-allowed Print a list of allowed licenses and exit.
+
+ tocheck Specifies the directory, relative to root, to check. This defaults
+ to "." so it checks everything.
+
+Examples:
+ python checklicenses.py
+ python checklicenses.py --root ~/chromium/src third_party""")
+
+
+ALLOWED_LICENSES = [
+ 'BSD (1 clause)',
+ 'BSD (2 clause)',
+ 'BSD (2 clause) GPL (v2 or later)',
+ 'BSD (3 clause)',
+ 'GPL (v2 or later)',
+ 'GPL (v3 or later) (with Bison parser exception)',
+ 'ISC',
+ 'ISC GPL (v2 or later)',
+ 'LGPL (v2 or later)',
+ 'LGPL (v2.1 or later)',
+ 'MIT/X11 (BSD like)',
+ 'Public domain',
+ 'Public domain GPL (v2 or later)',
+ 'Public domain MIT/X11 (BSD like)',
+ 'zlib/libpng',
+ 'zlib/libpng GPL (v2 or later)',
+]
+
+
+PATH_SPECIFIC_ALLOWED_LICENSES = {
+ 'caputils/airpcap.h': [
+ 'BSD-3-Clause',
+ ],
+ 'wsutil/strnatcmp.c': [
+ 'Zlib',
+ ],
+ 'wsutil/strnatcmp.h': [
+ 'Zlib',
+ ],
+ 'resources/protocols/dtds': [
+ 'UNKNOWN',
+ ],
+ 'resources/protocols/diameter/dictionary.dtd': [
+ 'UNKNOWN',
+ ],
+ 'resources/protocols/wimaxasncp/dictionary.dtd': [
+ 'UNKNOWN',
+ ],
+ 'doc/': [
+ 'UNKNOWN',
+ ],
+ 'docbook/custom_layer_chm.xsl': [
+ 'UNKNOWN',
+ ],
+ 'docbook/custom_layer_single_html.xsl': [
+ 'UNKNOWN',
+ ],
+ 'docbook/ws.css' : [
+ 'UNKNOWN'
+ ],
+ 'fix': [
+ 'UNKNOWN',
+ ],
+ 'wsutil/g711.c': [
+ 'UNKNOWN',
+ ],
+ 'packaging/macosx': [
+ 'UNKNOWN',
+ ],
+ 'epan/except.c': [
+ 'UNKNOWN',
+ ],
+ 'epan/except.h': [
+ 'UNKNOWN',
+ ],
+ # Generated header files by lex/lemon/whatever
+ 'epan/dtd_grammar.h': [
+ 'UNKNOWN',
+ ],
+ 'epan/dfilter/grammar.h': [
+ 'UNKNOWN',
+ ],
+ 'epan/dfilter/grammar.c': [
+ 'UNKNOWN',
+ ],
+ 'epan/dissectors/packet-ieee80211-radiotap-iter.': [ # Using ISC license only
+ 'ISC GPL (v2)'
+ ],
+ # Mentions BSD-3-clause twice due to embedding of code:
+ 'epan/dissectors/packet-communityid.c': [
+ 'BSD (3 clause) BSD (3 clause)',
+ ],
+ 'plugins/mate/mate_grammar.h': [
+ 'UNKNOWN',
+ ],
+ 'vcs_version.h': [
+ 'UNKNOWN',
+ ],
+ # Special IDL license that appears to be compatible as far as I (not a
+ # lawyer) can tell. See
+ # https://www.wireshark.org/lists/wireshark-dev/201310/msg00234.html
+ 'epan/dissectors/pidl/idl_types.h': [
+ 'UNKNOWN',
+ ],
+ # The following tools are under incompatible licenses (mostly GPLv3 or
+ # GPLv3+), but this is OK since they are not actually linked into Wireshark
+ 'tools/pidl': [
+ 'UNKNOWN',
+ ],
+ 'tools/lemon': [
+ 'UNKNOWN',
+ ],
+ 'tools/licensecheck.pl': [
+ 'GPL (v2)'
+ ],
+ '.gitlab/': [
+ 'UNKNOWN',
+ ],
+ 'wsutil/safe-math.h': [ # Public domain (CC0)
+ 'UNKNOWN',
+ ],
+}
+
+def check_licenses(options, args):
+ if options.list_allowed:
+ print('\n'.join(ALLOWED_LICENSES))
+ sys.exit(0)
+
+ # Figure out which directory we have to check.
+ if len(args) == 0:
+ # No directory to check specified, use the repository root.
+ start_dir = options.base_directory
+ elif len(args) == 1:
+ # Directory specified. Start here. It's supposed to be relative to the
+ # base directory.
+ start_dir = os.path.abspath(os.path.join(options.base_directory, args[0]))
+ else:
+ # More than one argument, we don't handle this.
+ PrintUsage()
+ return 1
+
+ print("Using base directory: %s" % options.base_directory)
+ print("Checking: %s" % start_dir)
+ print("")
+
+ licensecheck_path = os.path.abspath(os.path.join(options.base_directory,
+ 'tools',
+ 'licensecheck.pl'))
+
+ licensecheck = subprocess.Popen([licensecheck_path,
+ '-l', '150',
+ '-r', start_dir],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = licensecheck.communicate()
+ stdout = stdout.decode('utf-8')
+ stderr = stderr.decode('utf-8')
+ if options.verbose:
+ print('----------- licensecheck stdout -----------')
+ print(stdout)
+ print('--------- end licensecheck stdout ---------')
+ if licensecheck.returncode != 0 or stderr:
+ print('----------- licensecheck stderr -----------')
+ print(stderr)
+ print('--------- end licensecheck stderr ---------')
+ print("\nFAILED\n")
+ return 1
+
+ success = True
+ exit_status = 0
+ for line in stdout.splitlines():
+ filename, license = line.split(':', 1)
+ filename = os.path.relpath(filename.strip(), options.base_directory)
+
+ # All files in the build output directory are generated one way or another.
+ # There's no need to check them.
+ if os.path.dirname(filename).startswith('build'):
+ continue
+
+ # For now we're just interested in the license.
+ license = license.replace('*No copyright*', '').strip()
+
+ # Skip generated files.
+ if 'GENERATED FILE' in license:
+ continue
+
+ # Support files which provide a choice between licenses.
+ if any(item in ALLOWED_LICENSES for item in license.split(';')):
+ continue
+
+ if not options.ignore_suppressions:
+ found_path_specific = False
+ for prefix in PATH_SPECIFIC_ALLOWED_LICENSES:
+ if (filename.startswith(prefix) and
+ license in PATH_SPECIFIC_ALLOWED_LICENSES[prefix]):
+ found_path_specific = True
+ break
+ if found_path_specific:
+ continue
+
+ reason = "License '%s' for '%s' is not allowed." % (license, filename)
+ success = False
+ print(reason)
+ exit_status = 1
+
+ if success:
+ print("\nSUCCESS\n")
+ return 0
+ else:
+ print("\nFAILED\n")
+ return exit_status
+
+
+def main():
+ default_root = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..'))
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('--root', default=default_root,
+ dest='base_directory',
+ help='Specifies the repository root. This defaults '
+ 'to "../.." relative to the script file, which '
+ 'will normally be the repository root.')
+ option_parser.add_option('-v', '--verbose', action='store_true',
+ default=False, help='Print debug logging')
+ option_parser.add_option('--list-allowed',
+ action='store_true',
+ default=False,
+ help='Print a list of allowed licenses and exit.')
+ option_parser.add_option('--ignore-suppressions',
+ action='store_true',
+ default=False,
+ help='Ignore path-specific allowed license.')
+ options, args = option_parser.parse_args()
+ return check_licenses(options, args)
+
+
+if '__main__' == __name__:
+ sys.exit(main())