Files
scripts/reuse_helper/reuse_helper.py
Michael W 50d5f8aa37 scripts: Make REUSE compliant
Test: pipx run reuse lint
Change-Id: I73a60c421ec2d4399d511043e92d387dc890eb3d
2024-02-25 18:01:11 +02:00

188 lines
5.1 KiB
Python
Executable File

#!/usr/bin/env python
# SPDX-FileCopyrightText: 2023 The LineageOS Project
# SPDX-License-Identifier: Apache-2.0
# REUSE-IgnoreStart
import argparse
import os
import re
import sys
from pathlib import Path
from utils import check_dependencies, run_subprocess
def fix_files(project_path, extension, args):
extension_map = {
"*.java": ["java"],
"*.bp": ["java", "c"],
"*.proto": ["java", "c"],
"*.xml": ["xml"],
"*.py": ["py"],
}
path_list = Path(project_path).rglob(extension)
for item in path_list:
path_in_str = str(item)
if extension in extension_map:
for comment_style in extension_map[extension]:
clean_file(path_in_str, comment_style, args)
return
def clean_file(file, comment_style, args):
if should_ignore_file(file):
return
try:
fh = open(file, "r+")
except OSError:
print(f"Something went wrong while opening file {file}")
return
content = fh.read()
pattern_map = {
"c": r"((//[^\n]*\n)*(//)?)",
"java": r"(/\*.*?\*/)",
"py": r"((#[^\n]*\n)*#?)",
"xml": r"(<!--.*?-->)",
}
if comment_style not in pattern_map:
print(f"Comment style '{comment_style}' unsupported!")
return
pattern = pattern_map[comment_style]
match = re.search(pattern, content, re.DOTALL)
if match is None:
fh.close()
return
comment = match.group(1)
license_type = get_license_type(comment)
parts = comment.split("\n")
if len(parts) == 1:
fh.close()
return
i = 0
match = None
while match is None:
if len(parts) <= i:
break
match = re.search(r".*Copyright (?:\([cC]\))?\s*(.*)", parts[i])
if not match:
i += 1
if match is None:
fh.close()
return
copyright_lines = [match.group(1)]
pattern = re.compile(r"\s*\*?\s+(?:(?:Copyright )?\([Cc]\))?\s*((\d+)(.*))")
match = pattern.match(parts[i + 1])
while match is not None:
copyright_lines.append(match.group(1))
i += 1
match = pattern.match(parts[i + 1])
if license_type is not None:
new_comment = build_spdx_comment(comment_style, copyright_lines, license_type)
new_content = content.replace(comment, new_comment)
if args.fix_newlines:
if new_content[-1] != "\n":
new_content += "\n"
fh.seek(0)
fh.write(new_content)
fh.truncate()
fh.close()
def should_ignore_file(file):
if not "/res/values-" in file:
return False
else:
# We want to ignore translations
can_modify_values = ["land", "large", "night", "television", "v2", "v3"]
for m in can_modify_values:
if re.search(rf"/values-{m}", file):
return False
return True
def build_spdx_comment(comment_style, copyright_lines, license_type):
if comment_style == "java" or comment_style == "c":
return build_comment(copyright_lines, license_type, "/*\n", " * ", " */")
elif comment_style == "xml":
return build_comment(copyright_lines, license_type, "<!--\n", " ", "-->")
elif comment_style == "py":
return build_comment(copyright_lines, license_type, "", "# ", "")
else:
return ""
def build_comment(copyright_lines, license_type, comment_start, comment_middle, comment_end):
comment = comment_start
for line in copyright_lines:
comment += f"{comment_middle}SPDX-FileCopyrightText: {line}\n"
comment += f"{comment_middle}SPDX-License-Identifier: {license_type}\n"
comment += comment_end
return comment
def get_license_type(comment):
lic = None
if "http://www.apache.org/licenses/LICENSE-2.0" in comment:
lic = "Apache-2.0"
elif "GNU General Public" in comment and "version 2" in comment:
lic = "GPL-2.0-or-later"
return lic
def parse_args():
parser = argparse.ArgumentParser(description="Make project REUSE compliant")
parser.add_argument(
"-r", "--root", default=None, help="Specify the root path of your sources"
)
parser.add_argument(
"-p",
"--project",
required=True,
help="Specify the relative path of the project you want to convert",
)
parser.add_argument(
"-f",
"--fix_newlines",
action="store_true",
help="Add newlines to files that miss them",
)
return parser.parse_args()
def main():
args = parse_args()
root = args.root
if args.root is None:
root = str(Path.cwd())
root = root.replace("/lineage/scripts/reuse_helper", "")
path = os.path.join(root, args.project)
# We need "pipx"
if not check_dependencies():
sys.exit(-1)
# Parse and change known file-/comment-types
extensions = ["java", "xml", "bp", "proto", "py"]
for ext in extensions:
fix_files(path, f"*.{ext}", args)
# Download all licenses automatically
os.chdir(path)
_, code = run_subprocess(["pipx", "run", "reuse", "download", "--all"], True)
if __name__ == "__main__":
main()
# REUSE-IgnoreEnd