5 from subprocess
import check_output
8 from datetime
import datetime
9 from fnmatch
import fnmatch
25 CROSS_SYMBOL =
u"\u2717"
29 if sys.stdout.isatty():
30 return bcolors.FAIL + bcolors.BOLD + string + bcolors.ENDC
45 check_output([
"git",
"log",
"--format={{{%an|%ad|%s|%b}}}",
"--", src])
51 commits = re.findall(
r"{{{((?:.|\n)*?)}}}", output)
52 commits = [c
for c
in commits
if "[ignore-license]" not in c]
53 commits = [c.split(
"|")
for c
in commits]
59 madd = re.match(
r".*\d{2}:\d{2}:\d{2} (\d{4})", add[1])
60 assert madd !=
None,
"Regex did not match git log output"
61 mmod = re.match(
r".*\d{2}:\d{2}:\d{2} (\d{4})", mod[1])
62 assert mmod !=
None,
"Regex did not match git log output"
65 addcommit.date = add[1]
66 addcommit.year =
int(madd.group(1))
67 addcommit.author = add[0]
68 addcommit.subject = add[2]
69 addcommit.body = add[3]
72 modcommit.date = mod[1]
73 modcommit.year =
int(mmod.group(1))
74 modcommit.author = mod[0]
75 modcommit.subject = mod[2]
76 modcommit.body = mod[3]
78 return addcommit, modcommit
82 p = argparse.ArgumentParser()
83 p.add_argument(
"input")
85 "--fix", action=
"store_true", help=
"Attempt to fix any license issues found."
90 help=
"Check the license year info using git info for each file.",
93 "--fail-year-mismatch",
95 help=
"Fail if year in license statement is not valid.",
97 p.add_argument(
"--exclude",
"-e", action=
"append", default=EXCLUDE)
102 if os.path.isdir(args.input):
124 srcs =
filter(
lambda p:
not p.startswith(
"./build"), srcs)
128 year =
int(datetime.now().strftime(
"%Y"))
130 raw =
"""// This file is part of the Acts project.
132 // Copyright (C) {year} CERN for the benefit of the Acts project
134 // This Source Code Form is subject to the terms of the Mozilla Public
135 // License, v. 2.0. If a copy of the MPL was not distributed with this
136 // file, You can obtain one at http://mozilla.org/MPL/2.0/."""
139 r"\A// This file is part of the Acts project.\n"
141 +
r"// Copyright \(C\) (?P<year>.*) CERN for the benefit of the Acts project\n"
143 +
r"// This Source Code Form is subject to the terms of the Mozilla Public\n"
144 +
r"// License, v\. 2\.0\. If a copy of the MPL was not distributed with this\n"
145 +
r"// file, You can obtain one at http://mozilla.org/MPL/2.0/.\Z"
148 ref = re.compile(reg, re.M)
149 clean_re = re.compile(
r"(\(C\)) (.*) (Acts)", re.M)
150 year_re = re.compile(
r"^(?P<year1>20\d{2}|(?P<year2>20\d{2})-(?P<year3>20\d{2}))$")
151 extract_re = re.compile(
r"(20\d{2})-?(20\d{2})?")
154 return clean_re.sub(
r"\1 XXXX \3", s)
156 def get_clean_lines(s):
157 return [clean(l) +
"\n" for l
in s.split(
"\n")]
159 def validate_years(year1, year2):
165 if year1 > year
or year2 > year:
168 theyear =
int(year1
if year1
else year2)
177 nonlocal error_summary
178 error_summary +=
" ".join(map(str, args)) +
"\n"
180 def year_print(*pargs):
181 nonlocal error_summary
182 nonlocal info_summary
183 string =
" ".join(map(str, pargs)) +
"\n"
184 if args.fail_year_mismatch:
185 error_summary += string
187 info_summary += string
192 step =
int(nsrcs / 20)
193 for i, src
in enumerate(srcs):
195 if any([fnmatch(src, e)
for e
in args.exclude]):
198 if nsrcs > 1
and i % step == 0:
199 string =
"{}/{} -> {:.2f}%".format(i, nsrcs, i / float(nsrcs) * 100.0)
200 if sys.stdout.isatty():
201 sys.stdout.write(string +
"\r")
205 with
open(src,
"r+")
as f:
207 for x
in range(len(raw)):
209 if not line.startswith(
"//"):
212 license = (
"".join(license)).strip()
213 m = ref.search(license)
216 eprint(
"Invalid / missing license in " + src +
"")
218 exp = [l +
"\n" for l
in raw.format(year=
"XXXX").
split(
"\n")]
219 act = get_clean_lines(license)
221 diff = difflib.unified_diff(exp, act)
222 eprint(
"".join(diff))
226 eprint(
"-> fixing file (prepend)")
228 file_content = f.read()
230 stmnt = raw.format(year=year)
231 f.write(stmnt +
"\n\n")
232 f.write(file_content)
240 git_add_year = git_add_commit.year
241 git_mod_year = git_mod_commit.year
242 year_act = m.group(
"year")
243 ym = year_re.match(year_act)
246 eprint(
"Year string does not match format in {}".format(src))
247 eprint(
"Expected: YYYY or YYYY-YYYY (year or year range)")
248 eprint(
"Actual: {}\n".format(year_act))
251 extract = extract_re.search(year_act)
252 year1 = extract.group(1)
253 year2 = extract.group(2)
259 extract = extract_re.search(year_act)
260 year1 = extract.group(1)
261 year2 = extract.group(2)
263 if not validate_years(year1, year2):
264 eprint(
"Year string is not valid in {}".format(src))
265 eprint(
"Year string is: " + year_act +
"\n")
271 if git_add_year != git_mod_year:
273 if not (year1
and year2):
274 year_print(
"File: {}".format(src))
278 "- File was added in {}".format(git_add_year)
281 "- File was modified on {} by {}:\n{}".format(
283 git_mod_commit.author,
284 git_mod_commit.subject + git_mod_commit.body,
288 "=> License should say {}-{}".format(
289 git_add_year, git_mod_year
293 act_year = year1
if year1
else year2
296 "{} But says: {}".format(CROSS_SYMBOL, act_year)
300 if args.fail_year_mismatch:
304 year_print(
"This is not treated as an error\n")
308 int(year1) != git_add_year
309 or int(year2) != git_mod_year
312 year_print(
"File: {}".format(src))
314 "Year range {}-{} does not match range from git {}-{}".format(
315 year1, year2, git_add_year, git_mod_year
319 "- File was added in {}".format(git_add_year)
322 "- File was modified on {} by {}:\n{}".format(
324 git_mod_commit.author,
325 git_mod_commit.subject
326 + git_mod_commit.body,
330 "=> License should say {}-{}".format(
331 git_add_year, git_mod_year
336 "{} But says: {}-{}".format(
337 CROSS_SYMBOL, year1, year2
341 if args.fail_year_mismatch:
345 year_print(
"This is not treated as an error\n")
349 if int(year1) < git_mod_year:
350 year_print(
"File: {}".format(src))
352 "- Year {} does not match git modification year {}".format(
357 "- File was modified on {} by {}:\n{}".format(
359 git_mod_commit.author,
360 git_mod_commit.subject + git_mod_commit.body,
364 "=> License should say {}".format(git_mod_year)
367 err(
"{} But says: {}".format(CROSS_SYMBOL, year1))
369 if args.fail_year_mismatch:
373 year_print(
"This is not treated as an error\n")
376 if args.fix
and not valid:
377 eprint(
"-> fixing file (patch year)")
378 year_str =
"2016-{}".format(year)
380 if git_add_year == git_mod_year:
381 year_str =
"{}".format(git_add_year)
383 year_str =
"{}-{}".format(git_add_year, git_mod_year)
384 new_license = raw.format(year=year_str)
388 old_license_len = len(license)
389 f.seek(old_license_len)
400 print(
"\n--- INFO ---\n")
402 print(
"\n--- ERROR ---\n")
405 if exit != 0
and not args.fix:
406 print(
"License problems found. You can try running again with --fix")
411 if "__main__" == __name__: