Building a foreign-arch image (e.g. arm64 Raspberry Pi OS on an x86 host) needs a qemu binfmt handler, or the chroot fails with "Exec format error". crossarch detects this before the target is erased and, on confirmation, installs qemu-user-static via the host package manager (apt-get/dnf/zypper/pacman) and registers binfmt; otherwise it aborts with per-distro instructions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
2.7 KiB
Python
72 lines
2.7 KiB
Python
"""Ensure the host can chroot into a foreign-architecture image (qemu binfmt)."""
|
|
|
|
import platform
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from lim import runner, ui
|
|
from lim.errors import LimError
|
|
|
|
# Building natively needs no qemu; on these the chroot runs directly.
|
|
_NATIVE_ARM = ("aarch64", "armv7l", "armv6l", "arm64")
|
|
|
|
# Repo-based installs only; AUR helpers refuse to run from this root process.
|
|
_BINFMT_INSTALLERS = {
|
|
"apt-get": ["apt-get", "install", "-y", "qemu-user-static", "binfmt-support"],
|
|
"dnf": ["dnf", "install", "-y", "qemu-user-static"],
|
|
"zypper": ["zypper", "--non-interactive", "install", "qemu-linux-user"],
|
|
"pacman": ["pacman", "-S", "--needed", "--noconfirm", "qemu-user-static-binfmt"],
|
|
}
|
|
|
|
|
|
def _entry_enabled(entry: Path) -> bool:
|
|
try:
|
|
return "enabled" in entry.read_text()
|
|
except OSError:
|
|
return False
|
|
|
|
|
|
def qemu_binfmt_enabled() -> bool:
|
|
binfmt = Path("/proc/sys/fs/binfmt_misc")
|
|
return binfmt.is_dir() and any(_entry_enabled(e) for e in binfmt.glob("qemu-*"))
|
|
|
|
|
|
def auto_enable() -> bool:
|
|
"""Install qemu-user-static via the host package manager and register binfmt."""
|
|
command = next((cmd for tool, cmd in _BINFMT_INSTALLERS.items() if shutil.which(tool)), None)
|
|
if command is None:
|
|
ui.warning("No supported host package manager found (apt-get/dnf/zypper/pacman).")
|
|
return False
|
|
runner.run(command, sudo=True, check=False)
|
|
runner.run(["systemctl", "restart", "systemd-binfmt"], sudo=True, check=False)
|
|
return qemu_binfmt_enabled()
|
|
|
|
|
|
def ensure_ready() -> None:
|
|
"""Make foreign-arch chroot work, or fail with actionable guidance.
|
|
|
|
Called before the target is erased, so a host that cannot run the image's
|
|
binaries aborts early instead of mid-build with a cryptic "Exec format error".
|
|
"""
|
|
if platform.machine() in _NATIVE_ARM:
|
|
return
|
|
if qemu_binfmt_enabled():
|
|
ui.info("Cross-architecture build: qemu binfmt handlers are registered.")
|
|
return
|
|
ui.warning(
|
|
f"Cross-architecture build: this host ({platform.machine()}) cannot run the "
|
|
"image's ARM binaries in chroot yet."
|
|
)
|
|
if ui.confirm("Install qemu-user-static and register binfmt now?"):
|
|
if auto_enable():
|
|
ui.success("qemu binfmt handlers registered.")
|
|
return
|
|
ui.warning("Automatic setup did not register a handler.")
|
|
raise LimError(
|
|
"Cross-architecture chroot is not available. Install qemu-user-static + binfmt "
|
|
"manually, or build on the target itself (e.g. the Pi booted from USB).\n"
|
|
" Arch/Manjaro : qemu-user-static + qemu-user-static-binfmt (AUR), "
|
|
"then: sudo systemctl restart systemd-binfmt\n"
|
|
" Debian/Ubuntu: sudo apt install qemu-user-static binfmt-support"
|
|
)
|