Index: third_party/grpc/tools/distrib/check_copyright.py |
diff --git a/third_party/grpc/tools/distrib/check_copyright.py b/third_party/grpc/tools/distrib/check_copyright.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..99771f9632cfaa67728dafd15e4dda50350d6072 |
--- /dev/null |
+++ b/third_party/grpc/tools/distrib/check_copyright.py |
@@ -0,0 +1,179 @@ |
+#!/usr/bin/env python2.7 |
+ |
+# Copyright 2015-2016, Google Inc. |
+# All rights reserved. |
+# |
+# Redistribution and use in source and binary forms, with or without |
+# modification, are permitted provided that the following conditions are |
+# met: |
+# |
+# * Redistributions of source code must retain the above copyright |
+# notice, this list of conditions and the following disclaimer. |
+# * Redistributions in binary form must reproduce the above |
+# copyright notice, this list of conditions and the following disclaimer |
+# in the documentation and/or other materials provided with the |
+# distribution. |
+# * Neither the name of Google Inc. nor the names of its |
+# contributors may be used to endorse or promote products derived from |
+# this software without specific prior written permission. |
+# |
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+import argparse |
+import datetime |
+import os |
+import re |
+import sys |
+import subprocess |
+ |
+# find our home |
+ROOT = os.path.abspath( |
+ os.path.join(os.path.dirname(sys.argv[0]), '../..')) |
+os.chdir(ROOT) |
+ |
+# parse command line |
+argp = argparse.ArgumentParser(description='copyright checker') |
+argp.add_argument('-o', '--output', |
+ default='details', |
+ choices=['list', 'details']) |
+argp.add_argument('-s', '--skips', |
+ default=0, |
+ action='store_const', |
+ const=1) |
+argp.add_argument('-a', '--ancient', |
+ default=0, |
+ action='store_const', |
+ const=1) |
+argp.add_argument('-f', '--fix', |
+ default=False, |
+ action='store_true'); |
+argp.add_argument('--precommit', |
+ default=False, |
+ action='store_true') |
+args = argp.parse_args() |
+ |
+# open the license text |
+with open('LICENSE') as f: |
+ LICENSE = f.read().splitlines() |
+ |
+# license format by file extension |
+# key is the file extension, value is a format string |
+# that given a line of license text, returns what should |
+# be in the file |
+LICENSE_PREFIX = { |
+ '.c': r'\s*(?://|\*)\s*', |
+ '.cc': r'\s*(?://|\*)\s*', |
+ '.h': r'\s*(?://|\*)\s*', |
+ '.m': r'\s*\*\s*', |
+ '.php': r'\s*\*\s*', |
+ '.js': r'\s*\*\s*', |
+ '.py': r'#\s*', |
+ '.pyx': r'#\s*', |
+ '.pxd': r'#\s*', |
+ '.pxi': r'#\s*', |
+ '.rb': r'#\s*', |
+ '.sh': r'#\s*', |
+ '.proto': r'//\s*', |
+ '.cs': r'//\s*', |
+ '.mak': r'#\s*', |
+ 'Makefile': r'#\s*', |
+ 'Dockerfile': r'#\s*', |
+ 'LICENSE': '', |
+} |
+ |
+KNOWN_BAD = set([ |
+ 'src/php/tests/bootstrap.php', |
+]) |
+ |
+ |
+RE_YEAR = r'Copyright (?P<first_year>[0-9]+\-)?(?P<last_year>[0-9]+), Google Inc\.' |
+RE_LICENSE = dict( |
+ (k, r'\n'.join( |
+ LICENSE_PREFIX[k] + |
+ (RE_YEAR if re.search(RE_YEAR, line) else re.escape(line)) |
+ for line in LICENSE)) |
+ for k, v in LICENSE_PREFIX.iteritems()) |
+ |
+if args.precommit: |
+ FILE_LIST_COMMAND = 'git diff --name-only HEAD | grep -v ^third_party/' |
+else: |
+ FILE_LIST_COMMAND = 'git ls-tree -r --name-only -r HEAD | grep -v ^third_party/' |
+ |
+def load(name): |
+ with open(name) as f: |
+ return f.read() |
+ |
+def save(name, text): |
+ with open(name, 'w') as f: |
+ f.write(text) |
+ |
+assert(re.search(RE_LICENSE['LICENSE'], load('LICENSE'))) |
+assert(re.search(RE_LICENSE['Makefile'], load('Makefile'))) |
+ |
+ |
+def log(cond, why, filename): |
+ if not cond: return |
+ if args.output == 'details': |
+ print '%s: %s' % (why, filename) |
+ else: |
+ print filename |
+ |
+ |
+# scan files, validate the text |
+ok = True |
+filename_list = [] |
+try: |
+ filename_list = subprocess.check_output(FILE_LIST_COMMAND, |
+ shell=True).splitlines() |
+except subprocess.CalledProcessError: |
+ sys.exit(0) |
+ |
+for filename in filename_list: |
+ if filename in KNOWN_BAD: continue |
+ ext = os.path.splitext(filename)[1] |
+ base = os.path.basename(filename) |
+ if ext in RE_LICENSE: |
+ re_license = RE_LICENSE[ext] |
+ elif base in RE_LICENSE: |
+ re_license = RE_LICENSE[base] |
+ else: |
+ log(args.skips, 'skip', filename) |
+ continue |
+ try: |
+ text = load(filename) |
+ except: |
+ continue |
+ m = re.search(re_license, text) |
+ if m: |
+ gdict = m.groupdict() |
+ last_modified = int(subprocess.check_output('git log -1 --format="%ad" --date=short -- ' + filename, shell=True)[0:4]) |
+ latest_claimed = int(gdict['last_year']) |
+ if last_modified > latest_claimed: |
+ print '%s modified %d but copyright only extends to %d' % (filename, last_modified, latest_claimed) |
+ ok = False |
+ if args.fix: |
+ span_start, span_end = m.span(2) |
+ if not gdict['first_year']: |
+ # prepend the old year to the current one. |
+ text = '{}-{}{}'.format(text[:span_end], last_modified, text[span_end:]) |
+ else: # already a year range |
+ # simply update the last year |
+ text = '{}{}{}'.format(text[:span_start], last_modified, text[span_end:]) |
+ save(filename, text) |
+ print 'Fixed!' |
+ ok = True |
+ elif 'DO NOT EDIT' not in text and 'AssemblyInfo.cs' not in filename and filename != 'src/boringssl/err_data.c': |
+ log(1, 'copyright missing', filename) |
+ ok = False |
+ |
+sys.exit(0 if ok else 1) |