Version 0.7.1, changes kindly provided by Patrick Fey <5fey@informatik.uni-hamburg.de>

This commit is contained in:
gerv%gerv.net 2007-02-12 16:40:53 +00:00
Родитель df59f4f494
Коммит 4a3bf2b665
1 изменённых файлов: 125 добавлений и 78 удалений

Просмотреть файл

@ -75,6 +75,8 @@
-f, --force Continue processing after an error. (Errors -f, --force Continue processing after an error. (Errors
are summarized at end.) are summarized at end.)
-q, --quick Quick scanning. Use only basic license checks
(only use in report mode).
-M, --MPL Replace NPL licenses with MPL ones. -M, --MPL Replace NPL licenses with MPL ones.
-a, --all Check all files (only skip CVS directories). -a, --all Check all files (only skip CVS directories).
--dry-run Go through motions but don't actually change --dry-run Go through motions but don't actually change
@ -145,7 +147,7 @@ log = logging.getLogger("relic")
#---- globals #---- globals
_version_ = (0, 7, 0) _version_ = (0, 7, 1)
# When processing files, 'relic' skips files and directories according # When processing files, 'relic' skips files and directories according
# to these settings. Note: files identified in .cvsignore files are also # to these settings. Note: files identified in .cvsignore files are also
@ -161,6 +163,11 @@ _g_skip_file_basenames = [
# Auto-generated from other files # Auto-generated from other files
"configure", "configure",
# license and readme files
"license",
"readme",
] ]
_g_skip_files = [ _g_skip_files = [
# TODO: update with MPL block - or CVS remove (check history) # TODO: update with MPL block - or CVS remove (check history)
@ -737,10 +744,12 @@ def _should_skip_dir(path):
return 0 return 0
def _get_license_info(filename, show_initial): def _get_license_info(filename, show_initial=0, quick=0):
"""Return license block information for the given file. """Return license block information for the given file.
"filename" is the path to the file to scan. "filename" is the path to the file to scan.
"show_initial" is a boolean that indicates if initial developer info
should be displayed.
"quick" is a boolean that can be set for a quick scan. In this "quick" is a boolean that can be set for a quick scan. In this
case, only the "parts" field of the return dictionary will case, only the "parts" field of the return dictionary will
be filled out. be filled out.
@ -780,6 +789,9 @@ def _get_license_info(filename, show_initial):
finally: finally:
fin.close() fin.close()
# Help me find filena
log.info("Next file is: %s", filename)
# do quick search to see if any of the desired licenses is in here # do quick search to see if any of the desired licenses is in here
# - if it looks like all the parts are there, good, done # - if it looks like all the parts are there, good, done
# - if some but not all parts, continue # - if some but not all parts, continue
@ -794,20 +806,32 @@ def _get_license_info(filename, show_initial):
re.VERBOSE) re.VERBOSE)
parts = [] # found license parts in this file parts = [] # found license parts in this file
start = 0 start = 0
blocks = 0
while 1: while 1:
match = parts_pattern.search(content, start) match = parts_pattern.search(content, start)
if match: if match:
parts = match.groupdict() # Skip this block, if the last license block is more than 10 lines
for part in parts: # away (file is probably used for autogeneration of files then).
if parts[part]: if blocks == 1 and (match.start()-start) > 10:
lic_info["parts"].append(part) break
log.info("%s license/delimeter found", part)
start = match.end()
break
else: else:
raise RelicError("unexpected license part: %r" % parts) parts = match.groupdict()
for part in parts:
if parts[part]:
lic_info["parts"].append(part)
log.info("%s license/delimeter found", part)
start = match.end()
if part == "block_end":
blocks = blocks + 1
else:
blocks = 0
break
else:
raise RelicError("unexpected license part: %r" % parts)
else: else:
break break
# no license block at all
if not parts: if not parts:
# - if not, check to see if License or Copyright shows up in the # - if not, check to see if License or Copyright shows up in the
# file; if so, then error out; if not, skip out # file; if so, then error out; if not, skip out
@ -820,9 +844,18 @@ def _get_license_info(filename, show_initial):
else: else:
log.info("no license found") log.info("no license found")
return lic_info return lic_info
elif (parts == ["block_begin", "mpl", "gpl", "lgpl", "block_end"] or
parts == ["block_begin", "npl", "gpl", "lgpl", "block_end"]): # license block with non-tri-license version headers
elif lic_info["parts"] == ["block_begin", "block_end"]:
lic_info["parts"].append("unknown")
log.info("unknown license found (license block with non-tri-license)")
return lic_info
# license block with tri-license version headers
elif (lic_info["parts"] == ["block_begin", "mpl", "gpl", "lgpl", "block_end"] or
lic_info["parts"] == ["block_begin", "npl", "gpl", "lgpl", "block_end"]):
log.info("license looks good, no changes necessary") log.info("license looks good, no changes necessary")
if quick:
return lic_info return lic_info
# Otherwise, the license needs to be fixed, so gather more detailed # Otherwise, the license needs to be fixed, so gather more detailed
@ -916,7 +949,7 @@ def _get_license_info(filename, show_initial):
else: else:
raise RelicError("couldn't find start line with this pattern (even " raise RelicError("couldn't find start line with this pattern (even "
"though it looks like there is a license block in " "though it looks like there is a license block in "
"this file): %s" % lic_begin_pattern.pattern) "%s): %s" % (filename, lic_begin_pattern.pattern))
log.info("comment delimiters: %s", comment_delims) log.info("comment delimiters: %s", comment_delims)
log.debug("beginline dict: %s", beginline) log.debug("beginline dict: %s", beginline)
lic_info["comment_delims"] = comment_delims lic_info["comment_delims"] = comment_delims
@ -1357,60 +1390,60 @@ def _get_license_info(filename, show_initial):
return lic_info return lic_info
def _report_on_file(path, (results, switch_to_mpl, show_initial, _errors)): def _report_on_file(path, (results, switch_to_mpl, show_initial, quick, _errors)):
log.debug("_report_on_file(path='%s', results)", path) log.debug("_report_on_file(path='%s', results)", path)
output = path + "\n" output = path + "\n"
if _is_binary(path): if _is_binary(path):
output += "... binary, skipping this file\n" output += "... binary, skipping this file\n"
return
try:
lic_info = _get_license_info(path, show_initial)
except RelicError, ex:
return _relicensing_error(ex, path, _errors)
if log.isEnabledFor(logging.DEBUG):
pprint.pprint(lic_info)
parts = lic_info["parts"]
if not parts:
output += "... no license found\n"
elif "unknown" in parts:
output += "... unknown license (possibly) found\n"
elif ((parts == ["block_begin", "mpl", "gpl", "lgpl", "block_end"] or
parts == ["block_begin", "npl", "gpl", "lgpl", "block_end"]) and
not lic_info.get("unindented_contributor_lines")):
if (switch_to_mpl and
parts == ["block_begin", "npl", "gpl", "lgpl", "block_end"]):
output += "... %s found (looks complete, but is not MPL)"\
% "/".join(parts) + "\n"
else:
output += "... %s found (looks complete)"\
% "/".join(parts) + "\n"
else: else:
output += "... %s found" % "/".join(parts) + "\n" try:
output += "... license block lines: %(begin_line)d-%(end_line)d"\ lic_info = _get_license_info(path, show_initial, quick)
% lic_info + "\n" except RelicError, ex:
if lic_info["original_code_is"]: return _relicensing_error(ex, path, _errors)
output += "... original code is: %(original_code_is)s"\
% lic_info + "\n" if log.isEnabledFor(logging.DEBUG):
if lic_info["original_code_date"]: pprint.pprint(lic_info)
output += "... original code date: %(original_code_date)s"\ parts = lic_info["parts"]
% lic_info + "\n" if not parts:
if lic_info["initial_developer"]: output += "... no license found\n"
output += "... initial developer: %(initial_developer)s"\ elif "unknown" in parts:
% lic_info + "\n" output += "... unknown license (possibly) found\n"
if lic_info["initial_copyright_date"]: elif ((parts == ["block_begin", "mpl", "gpl", "lgpl", "block_end"] or
output += "... initial copyright date: %(initial_copyright_date)s"\ parts == ["block_begin", "npl", "gpl", "lgpl", "block_end"]) and
% lic_info + "\n" not lic_info.get("unindented_contributor_lines")):
if lic_info["contributors"]: if (switch_to_mpl and
output += "... contributors: %s"\ parts == ["block_begin", "npl", "gpl", "lgpl", "block_end"]):
% ", ".join(lic_info["contributors"]) + "\n" output += "... %s found (looks complete, but is not MPL)"\
if lic_info.get("unindented_contributor_lines"): % "/".join(parts) + "\n"
output += "... one or more contributor lines were not indented properly"\ else:
+ "\n" output += "... %s found (looks complete)"\
% "/".join(parts) + "\n"
else:
output += "... %s found" % "/".join(parts) + "\n"
if (not show_initial): if not quick:
print output; if "begin_line" in lic_info and "end_line" in lic_info:
output += "... license block lines: %(begin_line)d-%(end_line)d"\
% lic_info + "\n"
if "original_code_is" in lic_info:
output += "... original code is: %(original_code_is)s"\
% lic_info + "\n"
if "original_code_date" in lic_info:
output += "... original code date: %(original_code_date)s"\
% lic_info + "\n"
if "initial_developer" in lic_info:
output += "... initial developer: %(initial_developer)s"\
% lic_info + "\n"
if "initial_copyright_date" in lic_info:
output += "... initial copyright date: %(initial_copyright_date)s"\
% lic_info + "\n"
if "contributors" in lic_info:
output += "... contributors: %s"\
% ", ".join(lic_info["contributors"]) + "\n"
if lic_info.get("unindented_contributor_lines"):
output += "... one or more contributor lines were not indented properly"\
+ "\n"
print output;
def _gather_info_on_file(path, (results, _errors)): def _gather_info_on_file(path, (results, _errors)):
@ -1426,7 +1459,7 @@ def _gather_info_on_file(path, (results, _errors)):
path, _errors) path, _errors)
try: try:
results[path] = _get_license_info(path, show_initial) results[path] = _get_license_info(path)
except RelicError, ex: except RelicError, ex:
return _relicensing_error(ex, path, _errors, 1) return _relicensing_error(ex, path, _errors, 1)
@ -1557,7 +1590,7 @@ def _relicense_file(original_path,
original_path, _errors) original_path, _errors)
try: try:
lic_info = _get_license_info(original_path, show_initial) lic_info = _get_license_info(original_path, 0)
except RelicError, ex: except RelicError, ex:
return _relicensing_error(ex, original_path, _errors) return _relicensing_error(ex, original_path, _errors)
@ -1668,13 +1701,20 @@ def _relicense_file(original_path,
trilicense += _g_trilicense_parts["gpl/lgpl for npl"] trilicense += _g_trilicense_parts["gpl/lgpl for npl"]
else: # trilicense_name == "MPL/GPL/LGPL" else: # trilicense_name == "MPL/GPL/LGPL"
trilicense += _g_trilicense_parts["gpl/lgpl for mpl"] trilicense += _g_trilicense_parts["gpl/lgpl for mpl"]
# get fallback comment subsequent prefix
fallback_prefix = _get_comment_delim_sets(original_path)
# - add the comment delimiters # - add the comment delimiters
lines = trilicense.splitlines() lines = trilicense.splitlines()
for i in range(len(lines)): for i in range(len(lines)):
if i == 0: if i == 0:
prefix = lic_info["first_prefix"] prefix = lic_info["first_prefix"]
else: else:
prefix = lic_info["subsequent_prefix"] if lic_info["subsequent_prefix"]:
prefix = lic_info["subsequent_prefix"]
else:
prefix = fallback_prefix[0][1]
if lines[i]: if lines[i]:
if len(lic_info["comment_delims"]) == 0: if len(lic_info["comment_delims"]) == 0:
lines[i] = prefix + lines[i] lines[i] = prefix + lines[i]
@ -1686,7 +1726,10 @@ def _relicense_file(original_path,
lines[-1] += ' ' + lic_info["last_suffix"] lines[-1] += ' ' + lic_info["last_suffix"]
for i in range(len(lines)): lines[i] += '\n' for i in range(len(lines)): lines[i] += '\n'
trilicense_lines = lines trilicense_lines = lines
#pprint.pprint(lines)
##### uncomment to debug license block
# pprint.pprint(lines)
# return
# Skip out now if doing a dry-run. # Skip out now if doing a dry-run.
if _g_dry_run: if _g_dry_run:
@ -2176,7 +2219,7 @@ def addlicense(paths,
print "-----------------------------------------------------------------" print "-----------------------------------------------------------------"
def report(paths, switch_to_mpl, show_initial, _errors): def report(paths, switch_to_mpl=0, show_initial=1, quick=0, _errors=None):
"""Report on the existing licenses in the given file(s). """Report on the existing licenses in the given file(s).
"paths" is either a list of files or directories, or it is an "paths" is either a list of files or directories, or it is an
@ -2184,26 +2227,32 @@ def report(paths, switch_to_mpl, show_initial, _errors):
"switch_to_mpl" (optional, default false) is a boolean "switch_to_mpl" (optional, default false) is a boolean
indicating if an NPL-based license should be converted to indicating if an NPL-based license should be converted to
MPL. MPL.
"show_initial" (optional, default true) is a boolean indicating
if the initial developer should be displayed for each file.
"quick" (optional, default false) is a boolean indicating if only
basic license checking should be applied.
"_errors" (optional) is a dictionary on which errors are reported "_errors" (optional) is a dictionary on which errors are reported
(keyed by file path) when the force option is in effect. (keyed by file path) when the force option is in effect.
This method does not return anything. It will raise RelicError if This method does not return anything. It will raise RelicError if
there is a problem. there is a problem.
""" """
log.debug("report(paths=%s)", paths) log.debug("report(paths=%s)", paths)
results = {} results = {}
_traverse(paths,\ _traverse(paths,\
_report_on_file,\ _report_on_file,\
(results, switch_to_mpl, show_initial, _errors)) (results, switch_to_mpl, show_initial, quick, _errors))
def statistics(paths, extended=0, _errors=None): def statistics(paths, extended=0, quick=0, _errors=None):
"""Show a summary table of licenses in files in the given path(s). """Show a summary table of licenses in files in the given path(s).
"paths" is either a list of files or directories, or it is an "paths" is either a list of files or directories, or it is an
input stream with a path on each line. input stream with a path on each line.
"extended" (optional) is a boolean indicating if extended "extended" (optional) is a boolean indicating if extended
statistics should be shown statistics should be shown
"quick" (optional) is a boolean indicating if quick scan mode should
be enabled.
"_errors" (optional) is a dictionary on which errors are reported "_errors" (optional) is a dictionary on which errors are reported
(keyed by file path) when the force option is in effect. (keyed by file path) when the force option is in effect.
@ -2307,11 +2356,11 @@ def statistics(paths, extended=0, _errors=None):
def main(argv): def main(argv):
try: try:
opts, args = getopt.getopt(argv[1:], "VvadhfML:sxry:i:o:D:ARI", opts, args = getopt.getopt(argv[1:], "VvadhqfML:sxry:i:o:D:ARI",
["version", "verbose", "all", "help", "debug", ["version", "verbose", "all", "help", "debug",
"dry-run", "force", "MPL", "license=", "dry-run", "force", "MPL", "license=",
"statistics", "relicense", "backup", "add", "defaults", "statistics", "relicense", "backup", "add", "defaults",
"force-relicense", "initial-developers"]) "force-relicense", "initial-developers", "quick"])
except getopt.GetoptError, ex: except getopt.GetoptError, ex:
log.error(str(ex)) log.error(str(ex))
log.error("Try `%s --help'.", argv[0]) log.error("Try `%s --help'.", argv[0])
@ -2321,6 +2370,7 @@ def main(argv):
mode = "report" mode = "report"
extended = 0 extended = 0
backup = 0 backup = 0
quick = 0
force_relicensing = 0 force_relicensing = 0
fallback_initial_copyright_date = None fallback_initial_copyright_date = None
fallback_initial_developer = None fallback_initial_developer = None
@ -2379,6 +2429,8 @@ def main(argv):
fallback_original_code_is = "mozilla.org Code" fallback_original_code_is = "mozilla.org Code"
fallback_initial_copyright_date = "2001" fallback_initial_copyright_date = "2001"
fallback_initial_developer = "Netscape Communications Corporation" fallback_initial_developer = "Netscape Communications Corporation"
elif opt in ("-q", "--quick"):
quick = 1
try: try:
# Prepare the input. # Prepare the input.
@ -2401,9 +2453,9 @@ def main(argv):
force_relicensing, force_relicensing,
_errors=_errors) _errors=_errors)
elif mode == "statistics": elif mode == "statistics":
statistics(paths, extended, _errors=_errors) statistics(paths, extended, quick, _errors=_errors)
elif mode == "report": elif mode == "report":
report(paths, switch_to_mpl, show_initial, _errors=_errors) report(paths, switch_to_mpl, show_initial, quick, _errors=_errors)
elif mode == "add": elif mode == "add":
addlicense(paths, addlicense(paths,
fallback_initial_copyright_date, fallback_initial_copyright_date,
@ -2440,8 +2492,3 @@ def main(argv):
if __name__ == "__main__": if __name__ == "__main__":
sys.exit( main(sys.argv) ) sys.exit( main(sys.argv) )