Files
ElyOffice/packaging/crates.py
T
ZacharyZhang-NYandClaude Opus 5.5 a1b8c18ffd R7.7: crates.py lists what cargo links on this target
The walk over cargo metadata ignored an edge's target, so lopdf (Linux
only, a test dependency elsewhere) counted on Windows. Linux needs the
BSD-3-Clause text for alloc-stdlib, which ships no licence file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 17:08:30 -04:00

53 lines
2.7 KiB
Python

"""Notices of every crate built into packages. Usage: crates.py <package>..."""
import json
import pathlib
import subprocess
import sys
NAMES = ("licen", "copying", "notice", "copyright", "unlicense")
root = pathlib.Path(__file__).resolve().parent.parent
standard = root / "packaging" / "licenses"
host = next(line.split()[1] for line in subprocess.run(
["rustc", "-vV"], check=True, capture_output=True, text=True).stdout.splitlines() if line.startswith("host:"))
meta = json.loads(subprocess.run(
["cargo", "metadata", "--format-version", "1", "--locked",
"--manifest-path", str(root / "Cargo.toml")],
check=True, capture_output=True, text=True).stdout)
by_id = {package["id"]: package for package in meta["packages"]}
own = set(meta["workspace_members"])
if len([pid for pid in own if by_id[pid]["name"] in sys.argv[1:]]) != len(sys.argv) - 1:
sys.exit(f"crates.py: unknown package among {sys.argv[1:]}")
# Cargo decides which normal edges link on this target.
tree = subprocess.run(
["cargo", "tree", "--locked", "-e", "normal", "--target", host, "--prefix", "none", "-f", "{p}",
"--manifest-path", str(root / "Cargo.toml"), *[arg for name in sys.argv[1:] for arg in ("-p", name)]],
check=True, capture_output=True, text=True).stdout
linked = {tuple(line.split()[:2]) for line in tree.splitlines() if line.strip()}
wanted = {pid for pid, package in by_id.items() if (package["name"], "v" + package["version"]) in linked}
out, missing = [], []
for pid in sorted(wanted - own, key=lambda pid: (by_id[pid]["name"], by_id[pid]["version"])):
package = by_id[pid]
folder = pathlib.Path(package["manifest_path"]).parent
files = sorted(path for path in folder.iterdir()
if path.is_file() and path.name.lower().startswith(NAMES))
if package.get("license_file"):
files.append(folder / package["license_file"])
head = f"{package['name']} {package['version']} — {package['license'] or 'no licence field'}"
out.append("=" * 78 + f"\n{head}\n" + "=" * 78)
if files:
out += [f"--- {path.name}\n{path.read_text(errors='replace')}" for path in dict.fromkeys(files)]
else:
# No file shipped: the expression's standard text, with its authors.
ids = (package["license"] or "").replace("(", " ").replace(")", " ").replace("/", " ").split()
text = next((standard / f"{spdx}.txt" for spdx in ids if (standard / f"{spdx}.txt").is_file()), None)
if text is None:
missing.append(head)
continue
authors = ", ".join(package["authors"]) or package["name"] + " developers"
out.append(f"Copyright (c) {authors}\n--- {text.stem}\n{text.read_text()}")
if missing:
sys.exit("crates.py: no licence text for:\n " + "\n ".join(missing))
print("\n".join(out))