mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
The script iterates through (almost) all files within the Mumble repository and updates the copyright notice in them. If source files don't contain such a notice yet, it can optionally add one in. The script asks git when the file was first added to the git index and uses that year as the starting year for the copyright notice. The end year is always set to be the current year at the time the script is run.
164 lines
5.7 KiB
Python
Executable File
164 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import os
|
|
import platform
|
|
import subprocess
|
|
import datetime
|
|
from string import Template
|
|
import re
|
|
|
|
def cmd(args):
|
|
shell = platform.system() == 'Windows'
|
|
p = subprocess.Popen(args, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
stdout, stderr = p.communicate()
|
|
if p.returncode != 0:
|
|
raise Exception('cmd(): {0} failed with status {1}: {2}'.format(args, p.returncode, stderr))
|
|
return stdout.decode('utf-8')
|
|
|
|
|
|
licenseHeaderTemplate = Template(
|
|
"""${commentChar} Copyright ${copyright} The Mumble Developers. All rights reserved.
|
|
${commentChar} Use of this source code is governed by a BSD-style license
|
|
${commentChar} that can be found in the LICENSE file at the root of the
|
|
${commentChar} Mumble source tree or at <https://www.mumble.info/LICENSE>.""")
|
|
|
|
blackListFiles = []
|
|
|
|
class InvalidFileError(Exception):
|
|
pass
|
|
|
|
class LicenseNotFoundError(Exception):
|
|
pass
|
|
|
|
def generateHeaderForFile(filePath):
|
|
if filePath in blackListFiles:
|
|
raise InvalidFileError("File is blacklisted: \"" + filePath + "\"")
|
|
|
|
fileName = os.path.basename(filePath)
|
|
extension = os.path.splitext(fileName)[-1]
|
|
|
|
commentChar = None
|
|
|
|
if extension in [".cpp", ".c", ".h", ".hpp", ".cs", ".ice", ".proto", ".mm", ".vs", ".m", ".ps", ".hlsl", ".fx"]:
|
|
commentChar = "//"
|
|
elif extension in [".bash", ".sh", ".py", ".yaml", ".yml", ".ps1", ".pl", ".cmake", ".rb", ".init"] or fileName == "CMakeLists.txt":
|
|
commentChar = "#"
|
|
elif extension in [".bat", ".cmd"]:
|
|
commentChar = "::"
|
|
|
|
if commentChar is None:
|
|
raise InvalidFileError("Can't handle extension: \"" + extension + "\"")
|
|
|
|
# Get file's creation date
|
|
creationDateStr = cmd(["git", "log", "--diff-filter=A", "--follow", "--format=%ci", "-1", "--", filePath]).strip()
|
|
|
|
if not creationDateStr:
|
|
raise InvalidFileError("File not in git index: \"" + filePath + "\"")
|
|
|
|
# We only need the day, month and year
|
|
creationDate = datetime.datetime.fromisoformat(creationDateStr.split()[0])
|
|
|
|
copyright = None
|
|
if creationDate.year == datetime.datetime.today().year:
|
|
copyright = str(creationDate.year)
|
|
else:
|
|
copyright = str(creationDate.year) + "-" + str(datetime.datetime.today().year)
|
|
|
|
return licenseHeaderTemplate.substitute(commentChar=commentChar, copyright=copyright)
|
|
|
|
|
|
def findCopyright(content, pattern):
|
|
match = re.search(pattern, content)
|
|
|
|
if match:
|
|
return (match.start(), match.end())
|
|
else:
|
|
raise LicenseNotFoundError()
|
|
|
|
|
|
def exclude(path, excludes):
|
|
for current in excludes:
|
|
if path.startswith(current):
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Updates (and adds) license headers of the Mumble source files")
|
|
parser.add_argument("-a", "--add-new", action="store_true", default=False, help="Also add license headers to files that did not contain one before")
|
|
|
|
args = parser.parse_args()
|
|
|
|
scriptDir = os.path.dirname(os.path.realpath(__file__))
|
|
rootDir = os.path.realpath(os.path.join(scriptDir, ".."))
|
|
|
|
excludePaths = ["build", "docs", "3rdparty", "3rdPartylLicenses", "themes", ".git", "man"]
|
|
for i in range(len(excludePaths)):
|
|
excludePaths[i] = os.path.join(rootDir, excludePaths[i])
|
|
|
|
for i in range(len(blackListFiles)):
|
|
blackListFiles[i] = os.path.join(rootDir, blackListFiles[i])
|
|
|
|
# Convert the license template into a pattern that matches the license declaration inside the source files
|
|
licensePattern = re.compile(licenseHeaderTemplate.substitute(copyright="\d+(-\d+)?", commentChar=".+?").strip().replace("\n", "\\r?\\n").replace(" ",
|
|
"\\s*"), re.IGNORECASE)
|
|
|
|
modifiedFiles = 0
|
|
for root, dirs, files in os.walk(rootDir, topdown = True):
|
|
for currentFile in files:
|
|
fullPath = os.path.join(root, currentFile)
|
|
|
|
if exclude(fullPath, excludePaths):
|
|
continue
|
|
|
|
try:
|
|
generatedCopyright = generateHeaderForFile(fullPath)
|
|
|
|
fileHandle = open(fullPath, "r")
|
|
fileContent = fileHandle.read()
|
|
|
|
try:
|
|
start,end = findCopyright(fileContent, licensePattern)
|
|
except LicenseNotFoundError:
|
|
# no license yet
|
|
if not args.add_new:
|
|
print("No license header found for", fullPath)
|
|
continue
|
|
else:
|
|
print("Adding license header to", fullPath)
|
|
if not fileContent.startswith("#!"):
|
|
# File does not start with shebang -> write license to the top
|
|
start = 0
|
|
end = 0
|
|
|
|
generatedCopyright += "\n\n"
|
|
else:
|
|
# File starts with shebang -> append license after the first line (and add
|
|
# an extra blank line)
|
|
start = fileContent.find("\n") + 1
|
|
assert start > 0
|
|
end = start
|
|
|
|
generatedCopyright = "\n" + generatedCopyright + "\n\n"
|
|
|
|
modifiedContent = fileContent[ : start] + generatedCopyright + fileContent[end : ]
|
|
|
|
if modifiedContent != fileContent:
|
|
fileHandle.close()
|
|
# Open for writing
|
|
fileHandle = open(fullPath, "w")
|
|
|
|
modifiedFiles += 1
|
|
|
|
fileHandle.write(modifiedContent)
|
|
except InvalidFileError:
|
|
continue
|
|
|
|
print("Modified {} files".format(modifiedFiles))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|