Fix lemon FSF address and add license exception for the generated mate grammar
[metze/wireshark/wip.git] / tools / checklicenses.py
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #    * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #    * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #    * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Makes sure that all files contain proper licensing information."""
31
32
33 import optparse
34 import os.path
35 import subprocess
36 import sys
37
38
39 def PrintUsage():
40   print """Usage: python checklicenses.py [--root <root>] [tocheck]
41   --root   Specifies the repository root. This defaults to ".." relative
42            to the script file. This will be correct given the normal location
43            of the script in "<root>/tools".
44
45   --ignore-suppressions  Ignores path-specific license whitelist. Useful when
46                          trying to remove a suppression/whitelist entry.
47
48   tocheck  Specifies the directory, relative to root, to check. This defaults
49            to "." so it checks everything.
50
51 Examples:
52   python checklicenses.py
53   python checklicenses.py --root ~/chromium/src third_party"""
54
55
56 WHITELISTED_LICENSES = [
57     'Apache (v2.0)',
58     'Apache (v2.0) BSD (2 clause)',
59     'Apache (v2.0) GPL (v2)',
60     'Apple MIT',  # https://fedoraproject.org/wiki/Licensing/Apple_MIT_License
61     'APSL (v2)',
62     'APSL (v2) BSD (4 clause)',
63     'BSD',
64     'BSD (2 clause)',
65     'BSD (2 clause) GPL (v2 or later)',
66     'BSD (2 clause) MIT/X11 (BSD like)',
67     'BSD (3 clause)',
68     'BSD (3 clause) ISC',
69     'BSD (3 clause) LGPL (v2 or later)',
70     'BSD (3 clause) LGPL (v2.1 or later)',
71     'BSD (3 clause) MIT/X11 (BSD like)',
72     'BSD (4 clause)',
73     'BSD-like',
74
75     # TODO(phajdan.jr): Make licensecheck not print BSD-like twice.
76     'BSD-like MIT/X11 (BSD like)',
77
78     'BSL (v1.0)',
79     'GPL (v2 or later)',
80     'GPL (v2 or later) with Bison parser exception',
81     'GPL (v2 or later) with libtool exception',
82     'GPL (v3 or later) with Bison parser exception',
83     'GPL with Bison parser exception',
84     'ISC',
85     'ISC GPL (v2 or later)',
86     'LGPL',
87     'LGPL (v2)',
88     'LGPL (v2 or later)',
89     'LGPL (v2.1)',
90     'LGPL (v3 or later)',
91
92     # TODO(phajdan.jr): Make licensecheck convert that comma to a dot.
93     'LGPL (v2,1 or later)',
94
95     'LGPL (v2.1 or later)',
96     'MPL (v1.0) LGPL (v2 or later)',
97     'MPL (v1.1)',
98     'MPL (v1.1) BSD-like',
99     'MPL (v1.1) BSD-like GPL (unversioned/unknown version)',
100     'MPL (v1.1,) BSD (3 clause) GPL (unversioned/unknown version) '
101         'LGPL (v2.1 or later)',
102     'MPL (v1.1) GPL (unversioned/unknown version)',
103     'MPL (v2.0)',
104
105     # TODO(phajdan.jr): Make licensecheck not print the comma after 1.1.
106     'MPL (v1.1,) GPL (unversioned/unknown version) LGPL (v2 or later)',
107     'MPL (v1.1,) GPL (unversioned/unknown version) LGPL (v2.1 or later)',
108
109     'MIT/X11 (BSD like)',
110     'Ms-PL',
111     'Public domain',
112     'Public domain BSD',
113     'Public domain BSD (3 clause)',
114     'Public domain BSD-like',
115     'Public domain GPL (v2 or later)',
116     'Public domain LGPL (v2.1 or later)',
117     'libpng',
118     'zlib/libpng',
119     'zlib/libpng GPL (v2 or later)',
120     'SGI Free Software License B',
121     'University of Illinois/NCSA Open Source License (BSD like)',
122 ]
123
124
125 PATH_SPECIFIC_WHITELISTED_LICENSES = {
126     'dtds': [
127         'UNKNOWN',
128     ],
129     'fix': [
130         'UNKNOWN',
131     ],
132     'tools/pidl': [
133         'UNKNOWN',
134     ],
135     'wsutil/g711.c': [
136         'UNKNOWN',
137     ],
138     'wiretap/ascend.c': [
139         'GPL (v3 or later)',
140     ],
141     'wiretap/ascend.h': [
142         'GPL (v3 or later)',
143     ],
144     'packaging/macosx': [
145         'UNKNOWN',
146     ],
147     'svnversion.h': [
148         'UNKNOWN',
149     ],
150     'tools/lemon': [
151         'UNKNOWN',
152     ],
153     'epan/except.c': [
154         'UNKNOWN',
155     ],
156     'epan/except.h': [
157         'UNKNOWN',
158     ],
159     'plugins/mate/mate_grammar.c': [
160         'GPL (v2 or later) LGPL (v2 or later)', # licensecheck bug?
161     ],
162     'plugins/mate/mate_grammar.h': [
163         'UNKNOWN',
164     ],
165     'cmake/TestFileOffsetBits.c': [
166         'UNKNOWN',
167     ],
168 }
169
170
171 def check_licenses(options, args):
172   # Figure out which directory we have to check.
173   if len(args) == 0:
174     # No directory to check specified, use the repository root.
175     start_dir = options.base_directory
176   elif len(args) == 1:
177     # Directory specified. Start here. It's supposed to be relative to the
178     # base directory.
179     start_dir = os.path.abspath(os.path.join(options.base_directory, args[0]))
180   else:
181     # More than one argument, we don't handle this.
182     PrintUsage()
183     return 1
184
185   print "Using base directory:", options.base_directory
186   print "Checking:", start_dir
187   print
188
189   #licensecheck_path = os.path.abspath(os.path.join(options.base_directory,
190   #                                                 'third_party',
191   #                                                 'devscripts',
192   #                                                 'licensecheck.pl'))
193   licensecheck_path = 'licensecheck'
194
195   licensecheck = subprocess.Popen([licensecheck_path,
196                                    '-l', '100',
197                                    '-r', start_dir],
198                                   stdout=subprocess.PIPE,
199                                   stderr=subprocess.PIPE)
200   stdout, stderr = licensecheck.communicate()
201   if options.verbose:
202     print '----------- licensecheck stdout -----------'
203     print stdout
204     print '--------- end licensecheck stdout ---------'
205   if licensecheck.returncode != 0 or stderr:
206     print '----------- licensecheck stderr -----------'
207     print stderr
208     print '--------- end licensecheck stderr ---------'
209     print "\nFAILED\n"
210     return 1
211
212   success = True
213   for line in stdout.splitlines():
214     filename, license = line.split(':', 1)
215     filename = os.path.relpath(filename.strip(), options.base_directory)
216
217     # All files in the build output directory are generated one way or another.
218     # There's no need to check them.
219     if filename.startswith('out/') or filename.startswith('sconsbuild/'):
220       continue
221
222     # For now we're just interested in the license.
223     license = license.replace('*No copyright*', '').strip()
224
225     # Skip generated files.
226     if 'GENERATED FILE' in license:
227       continue
228
229     if license in WHITELISTED_LICENSES:
230       continue
231
232     if not options.ignore_suppressions:
233       found_path_specific = False
234       for prefix in PATH_SPECIFIC_WHITELISTED_LICENSES:
235         if (filename.startswith(prefix) and
236             license in PATH_SPECIFIC_WHITELISTED_LICENSES[prefix]):
237           found_path_specific = True
238           break
239       if found_path_specific:
240         continue
241
242     print "'%s' has non-whitelisted license '%s'" % (filename, license)
243     success = False
244
245   if success:
246     print "\nSUCCESS\n"
247     return 0
248   else:
249     print "\nFAILED\n"
250     print "Please read",
251     print "http://www.chromium.org/developers/adding-3rd-party-libraries"
252     print "for more info how to handle the failure."
253     print
254     print "Please respect OWNERS of checklicenses.py. Changes violating"
255     print "this requirement may be reverted."
256     return 1
257
258
259 def main():
260   default_root = os.path.abspath(
261       os.path.join(os.path.dirname(__file__), '..'))
262   option_parser = optparse.OptionParser()
263   option_parser.add_option('--root', default=default_root,
264                            dest='base_directory',
265                            help='Specifies the repository root. This defaults '
266                            'to "../.." relative to the script file, which '
267                            'will normally be the repository root.')
268   option_parser.add_option('-v', '--verbose', action='store_true',
269                            default=False, help='Print debug logging')
270   option_parser.add_option('--ignore-suppressions',
271                            action='store_true',
272                            default=False,
273                            help='Ignore path-specific license whitelist.')
274   options, args = option_parser.parse_args()
275   return check_licenses(options, args)
276
277
278 if '__main__' == __name__:
279   sys.exit(main())