File indexing completed on 2025-01-18 09:10:44
0001
0002 import argparse
0003 import os
0004 import sys
0005 from subprocess import check_output
0006 import re
0007 import difflib
0008 from datetime import datetime
0009 from fnmatch import fnmatch
0010
0011 EXCLUDE = []
0012
0013
0014 class bcolors:
0015 HEADER = "\033[95m"
0016 OKBLUE = "\033[94m"
0017 OKGREEN = "\033[92m"
0018 WARNING = "\033[93m"
0019 FAIL = "\033[91m"
0020 ENDC = "\033[0m"
0021 BOLD = "\033[1m"
0022 UNDERLINE = "\033[4m"
0023
0024
0025 CROSS_SYMBOL = "\u2717"
0026
0027
0028 def err(string):
0029 if sys.stdout.isatty():
0030 return bcolors.FAIL + bcolors.BOLD + string + bcolors.ENDC
0031 else:
0032 return string
0033
0034
0035 def main():
0036 p = argparse.ArgumentParser()
0037 p.add_argument("input", nargs="+")
0038 p.add_argument(
0039 "--fix", action="store_true", help="Attempt to fix any license issues found."
0040 )
0041 p.add_argument("--exclude", "-e", action="append", default=EXCLUDE)
0042
0043 args = p.parse_args()
0044 print(args.exclude)
0045
0046 extensions = ["cpp", "hpp", "ipp", "cuh", "cu", "C", "h"]
0047
0048 if len(args.input) == 1 and os.path.isdir(args.input[0]):
0049 find_command = ["find", args.input[0]]
0050 for ext in extensions:
0051 find_command.extend(["-iname", f"*.{ext}", "-or"])
0052
0053 find_command = find_command[:-1]
0054
0055 srcs = (
0056 str(
0057 check_output(find_command),
0058 "utf-8",
0059 )
0060 .strip()
0061 .split("\n")
0062 )
0063 srcs = filter(lambda p: not p.startswith("./build"), srcs)
0064 else:
0065 srcs = args.input
0066
0067 founding_year = 2016
0068 year_str = f"{founding_year}"
0069 year = year_str
0070
0071 raw = """// This file is part of the ACTS project.
0072 //
0073 // Copyright (C) {year} CERN for the benefit of the ACTS project
0074 //
0075 // This Source Code Form is subject to the terms of the Mozilla Public
0076 // License, v. 2.0. If a copy of the MPL was not distributed with this
0077 // file, You can obtain one at https://mozilla.org/MPL/2.0/."""
0078
0079 reg = (
0080 r"\A// This file is part of the ACTS project.\n"
0081 + r"//\n"
0082 + r"// Copyright \(C\) (?P<year>.*) CERN for the benefit of the ACTS project\n"
0083 + r"//\n"
0084 + r"// This Source Code Form is subject to the terms of the Mozilla Public\n"
0085 + r"// License, v\. 2\.0\. If a copy of the MPL was not distributed with this\n"
0086 + r"// file, You can obtain one at https://mozilla.org/MPL/2.0/.\Z"
0087 )
0088
0089 ref = re.compile(reg, re.M)
0090 clean_re = re.compile(r"(\(C\)) (.*) (CERN)", re.M)
0091
0092 def clean(s):
0093 return clean_re.sub(r"\1 XXXX \3", s)
0094
0095 def get_clean_lines(s):
0096 return [clean(l) + "\n" for l in s.split("\n")]
0097
0098 error_summary = ""
0099
0100 def eprint(string):
0101 nonlocal error_summary
0102 error_summary += string + "\n"
0103
0104 exit = 0
0105 srcs = list(srcs)
0106 nsrcs = len(srcs)
0107 step = max(int(nsrcs / 20), 1)
0108
0109 for i, src in enumerate(srcs):
0110 if any([fnmatch(src, e) for e in args.exclude]):
0111 continue
0112
0113
0114 if nsrcs > 1 and i % step == 0:
0115 string = f"{i}/{nsrcs} -> {i / float(nsrcs) * 100.0:.2f}%"
0116 if sys.stdout.isatty():
0117 sys.stdout.write(string + "\r")
0118 else:
0119 print(string)
0120
0121
0122 with open(src, "r+") as f:
0123 license = ""
0124 for _ in range(len(raw)):
0125 line = f.readline()
0126 if not line.startswith("//"):
0127 break
0128 license += line
0129 license = ("".join(license)).strip()
0130 m = ref.search(license)
0131
0132
0133 if m is None:
0134 eprint("Invalid / missing license in " + src + "")
0135
0136 exp = [l + "\n" for l in raw.format(year=year_str).split("\n")]
0137 act = get_clean_lines(license)
0138
0139 diff = difflib.unified_diff(exp, act)
0140 eprint("".join(diff))
0141 eprint("")
0142
0143 if args.fix:
0144 eprint("-> fixing file (prepend)")
0145 f.seek(0)
0146 file_content = f.read()
0147 f.seek(0)
0148 stmnt = raw.format(year=year_str)
0149 f.write(stmnt + "\n\n")
0150 f.write(file_content)
0151
0152 exit = 1
0153 continue
0154
0155
0156 year_act = m.group("year")
0157 if year_act != year_str:
0158 exit = 1
0159
0160 eprint(f"File: {src}")
0161 eprint(f"=> License should say {year_str}")
0162 eprint(err(f"{CROSS_SYMBOL} But says: {year_act}"))
0163
0164 if args.fix:
0165 eprint("-> fixing file (patch year)")
0166
0167 new_license = raw.format(year=year_str)
0168
0169
0170 old_license_len = len(license)
0171 f.seek(old_license_len)
0172 file_body = f.read()
0173 f.seek(0)
0174 f.truncate()
0175
0176 f.seek(0)
0177 f.write(new_license)
0178 f.write(file_body)
0179
0180 eprint("")
0181
0182 print(error_summary)
0183
0184 if exit != 0 and not args.fix:
0185 print("License problems found. You can try running again with --fix")
0186
0187 sys.exit(exit)
0188
0189
0190 if "__main__" == __name__:
0191 main()