166 lines
4.6 KiB
Python
166 lines
4.6 KiB
Python
"""A tiny local build backend for paperlib.
|
|
|
|
This keeps `uv run paperlib ...` working without requiring network access
|
|
to download a third-party build backend.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import hashlib
|
|
from pathlib import Path
|
|
from zipfile import ZIP_DEFLATED, ZipFile
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent
|
|
SRC_ROOT = PROJECT_ROOT / "src"
|
|
PACKAGE_ROOT = SRC_ROOT / "paperlib"
|
|
NAME = "paperlib"
|
|
VERSION = "0.1.0"
|
|
|
|
|
|
def _dist_info_dir() -> str:
|
|
return f"{NAME}-{VERSION}.dist-info"
|
|
|
|
|
|
def _wheel_name() -> str:
|
|
return f"{NAME}-{VERSION}-py3-none-any.whl"
|
|
|
|
|
|
def _metadata_contents() -> bytes:
|
|
return "\n".join(
|
|
[
|
|
"Metadata-Version: 2.1",
|
|
f"Name: {NAME}",
|
|
f"Version: {VERSION}",
|
|
"Summary: Local-first CLI toolkit for managing a paper library",
|
|
"",
|
|
]
|
|
).encode("utf-8")
|
|
|
|
|
|
def _wheel_contents() -> bytes:
|
|
return "\n".join(
|
|
[
|
|
"Wheel-Version: 1.0",
|
|
"Generator: paperlib_build_backend",
|
|
"Root-Is-Purelib: true",
|
|
"Tag: py3-none-any",
|
|
"",
|
|
]
|
|
).encode("utf-8")
|
|
|
|
|
|
def _entry_points_contents() -> bytes:
|
|
return b"[console_scripts]\npaperlib = paperlib.cli:main\n"
|
|
|
|
|
|
def _hash_bytes(data: bytes) -> tuple[str, str]:
|
|
digest = hashlib.sha256(data).digest()
|
|
encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
|
|
return f"sha256={encoded}", str(len(data))
|
|
|
|
|
|
def _package_files() -> list[tuple[str, bytes]]:
|
|
files: list[tuple[str, bytes]] = []
|
|
for path in sorted(PACKAGE_ROOT.rglob("*")):
|
|
if path.is_file():
|
|
relative = path.relative_to(SRC_ROOT).as_posix()
|
|
files.append((relative, path.read_bytes()))
|
|
return files
|
|
|
|
|
|
def _build_records(files: list[tuple[str, bytes]]) -> bytes:
|
|
record_path = f"{_dist_info_dir()}/RECORD"
|
|
rows: list[list[str]] = []
|
|
for path, data in files:
|
|
digest, size = _hash_bytes(data)
|
|
rows.append([path, digest, size])
|
|
rows.append([record_path, "", ""])
|
|
|
|
output: list[str] = []
|
|
for row in rows:
|
|
output.append(",".join(row))
|
|
return ("\n".join(output) + "\n").encode("utf-8")
|
|
|
|
|
|
def _build_wheel_archive(target: Path) -> str:
|
|
files = _package_files()
|
|
files.extend(
|
|
[
|
|
(f"{_dist_info_dir()}/METADATA", _metadata_contents()),
|
|
(f"{_dist_info_dir()}/WHEEL", _wheel_contents()),
|
|
(f"{_dist_info_dir()}/entry_points.txt", _entry_points_contents()),
|
|
]
|
|
)
|
|
files.append((f"{_dist_info_dir()}/RECORD", _build_records(files)))
|
|
|
|
wheel_path = target / _wheel_name()
|
|
with ZipFile(wheel_path, "w", compression=ZIP_DEFLATED) as zf:
|
|
for archive_path, data in files:
|
|
zf.writestr(archive_path, data)
|
|
return wheel_path.name
|
|
|
|
|
|
def build_wheel(
|
|
wheel_directory: str,
|
|
config_settings: dict[str, object] | None = None,
|
|
metadata_directory: str | None = None,
|
|
) -> str:
|
|
"""Build a wheel for paperlib."""
|
|
del config_settings, metadata_directory
|
|
target = Path(wheel_directory)
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
return _build_wheel_archive(target)
|
|
|
|
|
|
def build_editable(
|
|
wheel_directory: str,
|
|
config_settings: dict[str, object] | None = None,
|
|
metadata_directory: str | None = None,
|
|
) -> str:
|
|
"""Build an installable wheel for editable requests.
|
|
|
|
For now, we return a normal pure-Python wheel; that is sufficient for the
|
|
project's current bootstrap needs.
|
|
"""
|
|
return build_wheel(wheel_directory, config_settings, metadata_directory)
|
|
|
|
|
|
def get_requires_for_build_wheel(
|
|
config_settings: dict[str, object] | None = None,
|
|
) -> list[str]:
|
|
"""This backend has no external build requirements."""
|
|
del config_settings
|
|
return []
|
|
|
|
|
|
def get_requires_for_build_editable(
|
|
config_settings: dict[str, object] | None = None,
|
|
) -> list[str]:
|
|
"""This backend has no external build requirements."""
|
|
del config_settings
|
|
return []
|
|
|
|
|
|
def prepare_metadata_for_build_wheel(
|
|
metadata_directory: str,
|
|
config_settings: dict[str, object] | None = None,
|
|
) -> str:
|
|
"""Write minimal dist-info metadata for frontends that request it."""
|
|
del config_settings
|
|
dist_info = Path(metadata_directory) / _dist_info_dir()
|
|
dist_info.mkdir(parents=True, exist_ok=True)
|
|
(dist_info / "METADATA").write_bytes(_metadata_contents())
|
|
(dist_info / "WHEEL").write_bytes(_wheel_contents())
|
|
(dist_info / "entry_points.txt").write_bytes(_entry_points_contents())
|
|
(dist_info / "RECORD").write_text("", encoding="utf-8")
|
|
return dist_info.name
|
|
|
|
|
|
def prepare_metadata_for_build_editable(
|
|
metadata_directory: str,
|
|
config_settings: dict[str, object] | None = None,
|
|
) -> str:
|
|
"""Alias editable metadata generation to the wheel metadata implementation."""
|
|
return prepare_metadata_for_build_wheel(metadata_directory, config_settings)
|