diff options
Diffstat (limited to 'maintainers/scripts/kde/utils.py')
-rw-r--r-- | maintainers/scripts/kde/utils.py | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/maintainers/scripts/kde/utils.py b/maintainers/scripts/kde/utils.py new file mode 100644 index 0000000000000..7a82c4955c6b4 --- /dev/null +++ b/maintainers/scripts/kde/utils.py @@ -0,0 +1,185 @@ +import collections +import dataclasses +import functools +import json +import pathlib +import subprocess + +import yaml + +class DataclassEncoder(json.JSONEncoder): + def default(self, it): + if dataclasses.is_dataclass(it): + return dataclasses.asdict(it) + return super().default(it) + + +@dataclasses.dataclass +class Project: + name: str + description: str | None + project_path: str + repo_path: str | None + + def __hash__(self) -> int: + return hash(self.name) + + @classmethod + def from_yaml(cls, path: pathlib.Path): + data = yaml.safe_load(path.open()) + return cls( + name=data["identifier"], + description=data["description"], + project_path=data["projectpath"], + repo_path=data["repopath"] + ) + + +def get_git_commit(path: pathlib.Path): + return subprocess.check_output(["git", "-C", path, "rev-parse", "--short", "HEAD"]).decode().strip() + + +def validate_unique(projects: list[Project], attr: str): + seen = set() + for item in projects: + attr_value = getattr(item, attr) + if attr_value in seen: + raise Exception(f"Duplicate {attr}: {attr_value}") + seen.add(attr_value) + + +THIRD_PARTY = { + "third-party/appstream": "appstream-qt", + "third-party/cmark": "cmark", + "third-party/gpgme": "gpgme", + "third-party/kdsoap": "kdsoap", + "third-party/libaccounts-qt": "accounts-qt", + "third-party/libgpg-error": "libgpg-error", + "third-party/libquotient": "libquotient", + "third-party/packagekit-qt": "packagekit-qt", + "third-party/poppler": "poppler", + "third-party/qcoro": "qcoro", + "third-party/qmltermwidget": "qmltermwidget", + "third-party/qtkeychain": "qtkeychain", + "third-party/signond": "signond", + "third-party/taglib": "taglib", + "third-party/wayland-protocols": "wayland-protocols", + "third-party/wayland": "wayland", + "third-party/zxing-cpp": "zxing-cpp", +} + +IGNORE = { + "kdesupport/phonon-directshow", + "kdesupport/phonon-mmf", + "kdesupport/phonon-mplayer", + "kdesupport/phonon-quicktime", + "kdesupport/phonon-waveout", + "kdesupport/phonon-xine" +} + +WARNED = set() + + +@dataclasses.dataclass +class KDERepoMetadata: + version: str + projects: list[Project] + dep_graph: dict[Project, set[Project]] + + @functools.cached_property + def projects_by_name(self): + return {p.name: p for p in self.projects} + + @functools.cached_property + def projects_by_path(self): + return {p.project_path: p for p in self.projects} + + def try_lookup_package(self, path): + if path in IGNORE: + return None + project = self.projects_by_path.get(path) + if project is None and path not in WARNED: + WARNED.add(path) + print(f"Warning: unknown project {path}") + return project + + @classmethod + def from_repo_metadata_checkout(cls, repo_metadata: pathlib.Path): + projects = [ + Project.from_yaml(metadata_file) + for metadata_file in repo_metadata.glob("projects-invent/**/metadata.yaml") + ] + [ + Project(id, None, project_path, None) + for project_path, id in THIRD_PARTY.items() + ] + + validate_unique(projects, "name") + validate_unique(projects, "project_path") + + self = cls( + version=get_git_commit(repo_metadata), + projects=projects, + dep_graph={}, + ) + + dep_specs = [ + "dependency-data-common", + "dependency-data-kf6-qt6" + ] + dep_graph = collections.defaultdict(set) + + for spec in dep_specs: + spec_path = repo_metadata / "dependencies" / spec + for line in spec_path.open(): + line = line.strip() + if line.startswith("#"): + continue + if not line: + continue + + dependent, dependency = line.split(": ") + + dependent = self.try_lookup_package(dependent) + if dependent is None: + continue + + dependency = self.try_lookup_package(dependency) + if dependency is None: + continue + + dep_graph[dependent].add(dependency) + + self.dep_graph = dep_graph + + return self + + def write_json(self, root: pathlib.Path): + root.mkdir(parents=True, exist_ok=True) + + with (root / "projects.json").open("w") as fd: + json.dump(self.projects_by_name, fd, cls=DataclassEncoder, sort_keys=True, indent=2) + + with (root / "dependencies.json").open("w") as fd: + deps = {k.name: sorted(dep.name for dep in v) for k, v in self.dep_graph.items()} + json.dump({"version": self.version, "dependencies": deps}, fd, cls=DataclassEncoder, sort_keys=True, indent=2) + + @classmethod + def from_json(cls, root: pathlib.Path): + projects = [ + Project(**v) for v in json.load((root / "projects.json").open()).values() + ] + + deps = json.load((root / "dependencies.json").open()) + self = cls( + version=deps["version"], + projects=projects, + dep_graph={}, + ) + + dep_graph = collections.defaultdict(set) + for dependent, dependencies in deps["dependencies"].items(): + for dependency in dependencies: + dep_graph[self.projects_by_name[dependent]].add(self.projects_by_name[dependency]) + + self.dep_graph = dep_graph + return self |