mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-23 21:25:02 +00:00

## Summary Allows uv to recognize the ARMv5TE platform. This platform is currently supported on Debian distributions. It is an older 32 bit platform mostly used in embedded devices, currently in rust tier 2.5 so it requires cross compilation. Fixes #10157 . ## Test Plan Tested directly on device by applying a slightly different patch to tag 0.5.4 which is used by the current Home Assistant version (2024.12.5). After the patch Home Assistant is able to recognize the Python venv and setup its dependencies. Patched uv was built with ``` $ CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_GNUEABI_LINKER="/usr/bin/arm-linux-gnueabi-gcc" maturin build --release --target armv5te-unknown-linux-gnueabi --manylinux off ``` The target wheel was then moved on the device and installed via pip install.
172 lines
4.7 KiB
Python
Executable file
172 lines
4.7 KiB
Python
Executable file
# /// script
|
|
# requires-python = ">=3.12"
|
|
# dependencies = [
|
|
# "chevron-blue < 1",
|
|
# ]
|
|
# ///
|
|
"""
|
|
Generate static Rust code from Python version download metadata.
|
|
|
|
Generates the `downloads.inc` file from the `downloads.inc.mustache` template.
|
|
|
|
Usage:
|
|
|
|
uv run -- crates/uv-python/template-download-metadata.py
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import chevron_blue
|
|
|
|
CRATE_ROOT = Path(__file__).parent
|
|
WORKSPACE_ROOT = CRATE_ROOT.parent.parent
|
|
VERSION_METADATA = CRATE_ROOT / "download-metadata.json"
|
|
TEMPLATE = CRATE_ROOT / "src" / "downloads.inc.mustache"
|
|
TARGET = TEMPLATE.with_suffix("")
|
|
PRERELEASE_PATTERN = re.compile(r"(a|b|rc)(\d+)")
|
|
|
|
|
|
def prepare_name(name: str) -> str:
|
|
match name:
|
|
case "cpython":
|
|
return "CPython"
|
|
case "pypy":
|
|
return "PyPy"
|
|
case _:
|
|
raise ValueError(f"Unknown implementation name: {name}")
|
|
|
|
|
|
def prepare_libc(libc: str) -> str | None:
|
|
if libc == "none":
|
|
return None
|
|
else:
|
|
return libc.title()
|
|
|
|
|
|
def prepare_variant(variant: str | None) -> str | None:
|
|
match variant:
|
|
case None:
|
|
return "PythonVariant::Default"
|
|
case "freethreaded":
|
|
return "PythonVariant::Freethreaded"
|
|
case "debug":
|
|
return "PythonVariant::Debug"
|
|
case _:
|
|
raise ValueError(f"Unknown variant: {variant}")
|
|
|
|
|
|
def prepare_arch(arch: dict) -> tuple[str, str]:
|
|
match arch["family"]:
|
|
# Special constructors
|
|
case "i686":
|
|
family = "X86_32(target_lexicon::X86_32Architecture::I686)"
|
|
case "aarch64":
|
|
family = "Aarch64(target_lexicon::Aarch64Architecture::Aarch64)"
|
|
case "armv5tel":
|
|
family = "Arm(target_lexicon::ArmArchitecture::Armv5te)"
|
|
case "armv7":
|
|
family = "Arm(target_lexicon::ArmArchitecture::Armv7)"
|
|
case value:
|
|
family = value.capitalize()
|
|
variant = (
|
|
f"Some(ArchVariant::{arch['variant'].capitalize()})"
|
|
if arch["variant"]
|
|
else "None"
|
|
)
|
|
return family, variant
|
|
|
|
|
|
def prepare_os(os: str) -> str:
|
|
match os:
|
|
# Special constructors
|
|
case "darwin":
|
|
return "Darwin(None)"
|
|
|
|
return os.title()
|
|
|
|
|
|
def prepare_prerelease(prerelease: str) -> str:
|
|
if not prerelease:
|
|
return "None"
|
|
if not (match := PRERELEASE_PATTERN.match(prerelease)):
|
|
raise ValueError(f"Invalid prerelease: {prerelease!r}")
|
|
kind, number = match.groups()
|
|
return f"Some(Prerelease {{ kind: PrereleaseKind::{kind.capitalize()}, number: {number} }})"
|
|
|
|
|
|
def prepare_value(value: dict) -> dict:
|
|
value["os"] = prepare_os(value["os"])
|
|
value["arch_family"], value["arch_variant"] = prepare_arch(value["arch"])
|
|
value["name"] = prepare_name(value["name"])
|
|
value["libc"] = prepare_libc(value["libc"])
|
|
value["prerelease"] = prepare_prerelease(value["prerelease"])
|
|
value["variant"] = prepare_variant(value["variant"])
|
|
return value
|
|
|
|
|
|
def main() -> None:
|
|
debug = logging.getLogger().getEffectiveLevel() <= logging.DEBUG
|
|
|
|
data: dict[str, Any] = {}
|
|
data["generated_with"] = Path(__file__).relative_to(WORKSPACE_ROOT).as_posix()
|
|
data["generated_from"] = TEMPLATE.relative_to(WORKSPACE_ROOT).as_posix()
|
|
data["versions"] = [
|
|
{"key": key, "value": prepare_value(value)}
|
|
for key, value in json.loads(VERSION_METADATA.read_text()).items()
|
|
# Exclude debug variants for now, we don't support them in the Rust side
|
|
if value["variant"] != "debug"
|
|
]
|
|
|
|
# Render the template
|
|
logging.info(f"Rendering `{TEMPLATE.name}`...")
|
|
output = chevron_blue.render(
|
|
template=TEMPLATE.read_text(), data=data, no_escape=True, warn=debug
|
|
)
|
|
|
|
# Update the file
|
|
logging.info(f"Updating `{TARGET}`...")
|
|
TARGET.write_text("// DO NOT EDIT\n//\n" + output)
|
|
subprocess.check_call(
|
|
["rustfmt", str(TARGET)],
|
|
stderr=subprocess.STDOUT,
|
|
stdout=sys.stderr if debug else subprocess.DEVNULL,
|
|
)
|
|
|
|
logging.info("Done!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="Generates Rust code for Python version metadata.",
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
help="Enable debug logging",
|
|
)
|
|
parser.add_argument(
|
|
"-q",
|
|
"--quiet",
|
|
action="store_true",
|
|
help="Disable logging",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
if args.quiet:
|
|
log_level = logging.CRITICAL
|
|
elif args.verbose:
|
|
log_level = logging.DEBUG
|
|
else:
|
|
log_level = logging.INFO
|
|
|
|
logging.basicConfig(level=log_level, format="%(message)s")
|
|
|
|
main()
|