diff options
| author | Tobias Markmann <tm@ayena.de> | 2015-06-07 15:24:38 (GMT) |
|---|---|---|
| committer | Kevin Smith <kevin.smith@isode.com> | 2015-06-14 15:40:12 (GMT) |
| commit | 8c6e8db1838271efa7d1b00cf1a55bc8a73ca553 (patch) | |
| tree | a7c0e6c2b6c2c63ee7ada3a0724a6cc54dacc232 | |
| parent | a510bfe657067999904a7ff8d2cabab7b8b508d2 (diff) | |
| download | swift-8c6e8db1838271efa7d1b00cf1a55bc8a73ca553.zip swift-8c6e8db1838271efa7d1b00cf1a55bc8a73ca553.tar.bz2 | |
Add support for updating copyright on files in Copyrighter.py script
Refactored the code to handle the following cases nicely:
- no copyright for current user present yet
- outdated copyright for current user present (with and without range)
- copyright present and up to date
Test-Information:
Tested with SWIFT_LICENSE_CONFIG set to "Isode Limited|default".
Change-Id: I4df475f7ecd55aebe892411b2323da50fcbca525
| -rwxr-xr-x | BuildTools/Copyrighter.py | 206 |
1 files changed, 126 insertions, 80 deletions
diff --git a/BuildTools/Copyrighter.py b/BuildTools/Copyrighter.py index 56fcf01..a3b6379 100755 --- a/BuildTools/Copyrighter.py +++ b/BuildTools/Copyrighter.py | |||
| @@ -7,17 +7,20 @@ DEFAULT_LICENSE = "gpl3" | |||
| 7 | CONTRIBUTOR_LICENSE = "mit" | 7 | CONTRIBUTOR_LICENSE = "mit" |
| 8 | LICENSE_DIR = "Documentation/Licenses" | 8 | LICENSE_DIR = "Documentation/Licenses" |
| 9 | 9 | ||
| 10 | # The following regex parses license comment blocks and its part out of a complete source file. | ||
| 11 | reParseLicenseCommentBlocks = re.compile(ur'(\/\*\n\s\*\sCopyright \(c\) (?P<startYear>\d\d\d\d)(-(?P<endYear>\d\d\d\d))? (?P<author>[^\n\.]*)\.?\n.\* (?P<license>[^\n]*)\n \* (?P<seeMore>[^\n]+)\n *\*\/)') | ||
| 12 | |||
| 10 | class License : | 13 | class License : |
| 11 | def __init__(self, name, file) : | 14 | def __init__(self, name, file) : |
| 12 | self.name = name | 15 | self.name = name |
| 13 | self.file = file | 16 | self.file = file |
| 14 | 17 | ||
| 15 | licenses = { | 18 | licenses = { |
| 16 | "gpl3" : License("GNU General Public License v3", "GPLv3.txt"), | 19 | "default": License("All rights reserved.", "See the COPYING file for more information."), |
| 17 | "mit" : License("MIT License", "MIT.txt"), | 20 | "gpl3" : License("Licensed under the GNU General Public License v3.", "See " + LICENSE_DIR + "/" + "GPLv3.txt" + " for more information."), |
| 21 | "mit" : License("Licensed under the MIT License.", "See " + LICENSE_DIR + "/" + "MIT.txt" + " for more information."), | ||
| 18 | } | 22 | } |
| 19 | 23 | ||
| 20 | |||
| 21 | class Copyright : | 24 | class Copyright : |
| 22 | def __init__(self, author, year, license) : | 25 | def __init__(self, author, year, license) : |
| 23 | self.author = author | 26 | self.author = author |
| @@ -28,64 +31,52 @@ class Copyright : | |||
| 28 | return "\n".join([ | 31 | return "\n".join([ |
| 29 | comment_chars[0], | 32 | comment_chars[0], |
| 30 | comment_chars[1] + " Copyright (c) %(year)s %(name)s" % {"year" : self.year, "name" : self.author }, | 33 | comment_chars[1] + " Copyright (c) %(year)s %(name)s" % {"year" : self.year, "name" : self.author }, |
| 31 | comment_chars[1] + " Licensed under the " + licenses[self.license].name + ".", | 34 | comment_chars[1] + licenses[self.license].name, |
| 32 | comment_chars[1] + " See " + LICENSE_DIR + "/" + licenses[self.license].file + " for more information.", | 35 | comment_chars[1] + licenses[self.license].file, |
| 33 | comment_chars[2], | 36 | comment_chars[2], |
| 34 | "\n"]) | 37 | "\n"]) |
| 38 | def __str__(self): | ||
| 39 | return """/* | ||
| 40 | * Copyright (c) %s %s. | ||
| 41 | * %s | ||
| 42 | * %s | ||
| 43 | */ | ||
| 44 | """ % (self.year, self.author, licenses[self.license].name, licenses[self.license].file) | ||
| 45 | |||
| 46 | class ContentRef : | ||
| 47 | def __init__(self, begin, end, content): | ||
| 48 | self.begin = begin | ||
| 49 | self.end = end | ||
| 50 | self.content = content | ||
| 51 | |||
| 52 | class CopyrightBlock : | ||
| 53 | def __init__(self, yearBegin, yearEnd, author, license, seeMore, total): | ||
| 54 | self.yearBegin = yearBegin | ||
| 55 | self.yearEnd = yearEnd | ||
| 56 | self.author = author | ||
| 57 | self.license = license | ||
| 58 | self.seeMore = seeMore | ||
| 59 | self.total = total | ||
| 60 | |||
| 61 | def cref_from_group(match, group): | ||
| 62 | if match.group(group): | ||
| 63 | return ContentRef(match.start(group), match.end(group), match.group(group)) | ||
| 64 | else : | ||
| 65 | return None | ||
| 35 | 66 | ||
| 36 | def get_comment_chars_for_filename(filename) : | 67 | def parse_file_new(filename): |
| 37 | return ("/*", " *", " */") | 68 | copyrightBlocks = [] |
| 38 | 69 | with open(filename, 'r') as file: | |
| 39 | def get_comment_chars_re_for_filename(filename) : | 70 | content = file.read() |
| 40 | comment_chars = get_comment_chars_for_filename(filename) | 71 | for match in re.finditer(reParseLicenseCommentBlocks, content): |
| 41 | return "|".join(comment_chars).replace("*", "\\*") | 72 | copyrightBlocks.append(CopyrightBlock( |
| 42 | 73 | cref_from_group(match, "startYear"), | |
| 43 | def parse_file(filename) : | 74 | cref_from_group(match, "endYear"), |
| 44 | file = open(filename) | 75 | cref_from_group(match, "author"), |
| 45 | copyright_text = [] | 76 | cref_from_group(match, "license"), |
| 46 | prolog = "" | 77 | cref_from_group(match, "seeMore"), |
| 47 | epilog = "" | 78 | cref_from_group(match, 0))) |
| 48 | inProlog = True | 79 | return copyrightBlocks |
| 49 | inCopyright = False | ||
| 50 | inEpilog = False | ||
| 51 | for line in file.readlines() : | ||
| 52 | if inProlog : | ||
| 53 | if line.startswith("#!") or len(line.strip()) == 0 : | ||
| 54 | prolog += line | ||
| 55 | continue | ||
| 56 | else : | ||
| 57 | inProlog = False | ||
| 58 | inCopyright = True | ||
| 59 | |||
| 60 | if inCopyright : | ||
| 61 | if re.match(get_comment_chars_re_for_filename(filename), line) != None : | ||
| 62 | copyright_text.append(line.rstrip()) | ||
| 63 | continue | ||
| 64 | else : | ||
| 65 | inCopyright = False | ||
| 66 | inEpilog = True | ||
| 67 | if len(line.strip()) == 0 : | ||
| 68 | continue | ||
| 69 | |||
| 70 | if inEpilog : | ||
| 71 | epilog += line | ||
| 72 | continue | ||
| 73 | |||
| 74 | file.close() | ||
| 75 | |||
| 76 | # Parse the copyright | ||
| 77 | copyright = None | ||
| 78 | if len(copyright_text) == 5 : | ||
| 79 | comment_chars = get_comment_chars_for_filename(filename) | ||
| 80 | if copyright_text[0] == comment_chars[0] and copyright_text[4] == comment_chars[2] : | ||
| 81 | matchstring = "(" + get_comment_chars_re_for_filename(filename) + ") Copyright \(c\) (?P<startYear>\d\d\d\d)(-(?P<endYear>\d\d\d\d))? (?P<author>.*)" | ||
| 82 | m = re.match(matchstring, copyright_text[1]) | ||
| 83 | if m != None : | ||
| 84 | # FIXME: Do better copyright reconstruction here | ||
| 85 | copyright = True | ||
| 86 | if not copyright : | ||
| 87 | epilog = "\n".join(copyright_text) + epilog | ||
| 88 | return (prolog, copyright, epilog) | ||
| 89 | 80 | ||
| 90 | def get_userinfo() : | 81 | def get_userinfo() : |
| 91 | p = subprocess.Popen("git config user.name", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) | 82 | p = subprocess.Popen("git config user.name", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) |
| @@ -107,36 +98,91 @@ def get_copyright(username, email) : | |||
| 107 | license = CONTRIBUTOR_LICENSE | 98 | license = CONTRIBUTOR_LICENSE |
| 108 | return Copyright(username, datetime.date.today().strftime("%Y"), license) | 99 | return Copyright(username, datetime.date.today().strftime("%Y"), license) |
| 109 | 100 | ||
| 110 | def check_copyright(filename) : | 101 | def get_copyright_setting(username, email) : |
| 111 | (prolog, copyright, epilog) = parse_file(filename) | 102 | config = os.getenv("SWIFT_LICENSE_CONFIG") |
| 112 | if copyright == None : | 103 | if config : |
| 113 | print "No copyright found in: " + filename | 104 | copyrightHolder, license = config.split("|") |
| 114 | #print "Please run '" + sys.argv[0] + " set-copyright " + filename + "'" | 105 | else : |
| 106 | if email.endswith("isode.com") or email in ["git@el-tramo.be", "git@kismith.co.uk", "tm@ayena.de"] : | ||
| 107 | copyrightHolder, license = "Isode Limited", "default" | ||
| 108 | else : | ||
| 109 | copyrightHolder, license = username, "mit" | ||
| 110 | return Copyright(copyrightHolder, datetime.date.today().year, license) | ||
| 111 | |||
| 112 | def check_copyright(filename, hints) : | ||
| 113 | copyrightBlocks = parse_file_new(filename) | ||
| 114 | if copyrightBlocks : | ||
| 115 | # looking for copyright block for current author | ||
| 116 | username, email = get_userinfo() | ||
| 117 | copyrightSetting = get_copyright_setting(username, email) | ||
| 118 | for block in copyrightBlocks : | ||
| 119 | if block.author.content == copyrightSetting.author: | ||
| 120 | year = block.yearBegin.content if not block.yearEnd else block.yearEnd.content | ||
| 121 | if int(year) == copyrightSetting.year: | ||
| 122 | return True | ||
| 123 | else : | ||
| 124 | if hints : | ||
| 125 | print "Copyright block for " + copyrightSetting.author + " does not cover current year in: " + filename | ||
| 126 | return False | ||
| 127 | if hints : | ||
| 128 | print "Missing copyright block for " + copyrightSetting.author + " in: " + filename | ||
| 129 | return False | ||
| 130 | else : | ||
| 131 | if hints : | ||
| 132 | print "No copyright found in: " + filename | ||
| 115 | return False | 133 | return False |
| 134 | |||
| 135 | def replace_data_in_file(filename, begin, end, replaceWith) : | ||
| 136 | with open(filename, 'r') as file: | ||
| 137 | content = file.read() | ||
| 138 | with open(filename, 'w') as file: | ||
| 139 | file.write(content[:begin] + replaceWith + content[end:]) | ||
| 140 | |||
| 141 | def set_or_update_copyright(filename) : | ||
| 142 | if check_copyright(filename, False) : | ||
| 143 | print "No update required for file: " + filename | ||
| 116 | else : | 144 | else : |
| 117 | return True | 145 | copyrightBlocks = parse_file_new(filename) |
| 118 | 146 | username, email = get_userinfo() | |
| 119 | def set_copyright(filename, copyright) : | 147 | copyrightSetting = get_copyright_setting(username, email) |
| 120 | (prolog, c, epilog) = parse_file(filename) | 148 | lastBlock = 0 |
| 121 | comment_chars = get_comment_chars_for_filename(filename) | 149 | for block in copyrightBlocks : |
| 122 | copyright_text = copyright.to_string(comment_chars) | 150 | if block.author.content == copyrightSetting.author : |
| 123 | file = open(filename, "w") | 151 | if not block.yearEnd : |
| 124 | if prolog != "": | 152 | # replace year with range |
| 125 | file.write(prolog) | 153 | replace_data_in_file(filename, block.yearBegin.begin, block.yearBegin.end, "%s-%s" % (block.yearBegin.content, str(copyrightSetting.year))) |
| 126 | file.write(copyright_text) | 154 | else : |
| 127 | if epilog != "" : | 155 | # replace end of range with current year |
| 128 | file.write(epilog) | 156 | replace_data_in_file(filename, block.yearEnd.begin, block.yearEnd.end, "%s" % str(copyrightSetting.year)) |
| 129 | file.close() | 157 | return |
| 158 | lastBlock = block.total.end | ||
| 159 | |||
| 160 | # No copyright block found. Append a new one. | ||
| 161 | replace_data_in_file(filename, lastBlock+1, lastBlock+1, "\n" + str(copyrightSetting)) | ||
| 162 | |||
| 163 | def print_help() : | ||
| 164 | print """Usage: | ||
| 165 | Copyrighter.py check-copyright $filename | ||
| 166 | Cheks for the existence of a copyright comment block. | ||
| 167 | |||
| 168 | Copyrighter.py set-copyright $filename | ||
| 169 | Adds or updates the existing copyright comment block. | ||
| 170 | |||
| 171 | License setting: | ||
| 172 | A users license configuration can be set via the SWIFT_LICENSE_CONFIG environment variable | ||
| 173 | in the format "$copyright holder|$license", e.g. "Jane Doe|mit". Possible values for | ||
| 174 | $license are default, mit and gpl. | ||
| 175 | """ | ||
| 130 | 176 | ||
| 131 | if sys.argv[1] == "check-copyright" : | 177 | if sys.argv[1] == "check-copyright" : |
| 132 | file = sys.argv[2] | 178 | file = sys.argv[2] |
| 133 | if (file.endswith(".cpp") or file.endswith(".h")) and not "3rdParty" in file : | 179 | if (file.endswith(".cpp") or file.endswith(".h")) and not "3rdParty" in file : |
| 134 | if not check_copyright(file) : | 180 | if not check_copyright(file, True) : |
| 135 | sys.exit(-1) | 181 | sys.exit(-1) |
| 136 | elif sys.argv[1] == "set-copyright" : | 182 | elif sys.argv[1] == "set-copyright" : |
| 137 | (username, email) = get_userinfo() | 183 | file = sys.argv[2] |
| 138 | copyright = get_copyright(username, email) | 184 | set_or_update_copyright(file) |
| 139 | set_copyright(sys.argv[2], copyright) | ||
| 140 | else : | 185 | else : |
| 141 | print "Unknown command: " + sys.argv[1] | 186 | print "Unknown command: " + sys.argv[1] |
| 187 | print_help() | ||
| 142 | sys.exit(-1) | 188 | sys.exit(-1) |
Swift