1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-17 13:25:10 +03:00
pmbootstrap/pmb/types.py
Casey Connolly b9d6a304dc try to add FDE support but it doesn't work
Signed-off-by: Casey Connolly <kcxt@postmarketos.org>
2025-06-13 21:47:42 +02:00

357 lines
9.4 KiB
Python

# Copyright 2024 Caleb Connolly
# SPDX-License-Identifier: GPL-3.0-or-later
import enum
import subprocess
from argparse import Namespace
from pathlib import Path
from typing import Any, Literal, TypedDict
from pmb.core.arch import Arch
from pmb.core.chroot import Chroot
import uuid
class CrossCompile(enum.Enum):
# Cross compilation isn't needed for this package:
# 1) Either because the arch we will build for is exactly the same as the
# native arch, or
# 2) because CPU emulation is not needed (e.g. x86 on x86_64)
UNNECESSARY = "unnecessary"
# Cross compilation disabled, only use QEMU
QEMU_ONLY = "qemu-only"
# Cross compilation will use crossdirect
CROSSDIRECT = "crossdirect"
# Cross compilation will use cross-native
CROSS_NATIVE = "cross-native"
# Cross compilation will use cross-native2
CROSS_NATIVE2 = "cross-native2"
def __str__(self) -> str:
return self.value
def enabled(self) -> bool:
"""Are we cross-compiling for this value of cross?"""
return self not in [CrossCompile.UNNECESSARY, CrossCompile.QEMU_ONLY]
def host_chroot(self, arch: Arch) -> Chroot:
"""Chroot for the package target architecture (the "host" machine).
Cross native (v1) is the exception, since we exclusively use the native
chroot for that."""
if arch == Arch.native():
return Chroot.native()
match self:
case CrossCompile.CROSS_NATIVE:
return Chroot.native()
case _:
return Chroot.buildroot(arch)
def build_chroot(self, arch: Arch) -> Chroot:
"""Chroot for the package build architecture (the "build" machine)."""
if arch == Arch.native():
return Chroot.native()
match self:
case CrossCompile.UNNECESSARY | CrossCompile.CROSSDIRECT | CrossCompile.QEMU_ONLY:
return Chroot.buildroot(arch)
case CrossCompile.CROSS_NATIVE | CrossCompile.CROSS_NATIVE2:
return Chroot.native()
class DiskPartition:
name: str
size: int # in bytes
filesystem: str | None
# offset into the disk image!
offset: int # bytes
path: str # e.g. /dev/install or /dev/installp1 for --split
_uuid: str
def __init__(self, name: str, size: int):
self.name = name
self.size = size
self.filesystem = None
self.offset = 0
self.path = ""
self._uuid = ""
@property
def uuid(self) -> str:
"""
We generate a UUID the first time we're called. The length
depends on which filesystem, since FAT only supported short
volume IDs.
"""
if self.filesystem is None:
raise ValueError("Can't get UUID when filesystem not set")
if self._uuid:
return self._uuid
if self.filesystem.startswith("fat"):
# FAT UUIDs are only 8 bytes and are always uppercase
self._uuid = ("-".join(str(uuid.uuid4()).split("-")[1:3])).upper()
else:
self._uuid = str(uuid.uuid4())
return self._uuid
@property
def size_mb(self) -> int:
return round(self.size / (1024**2))
@property
def partition_label(self) -> str:
return f"pmOS_{self.name}"
def offset_sectors(self, sector_size: int) -> int:
if self.offset % sector_size != 0:
raise ValueError(
f"Partition {self.name} offset not a multiple of sector size {sector_size}!"
)
return int(self.offset / sector_size)
def size_sectors(self, sector_size: int) -> int:
ss = int((self.size + sector_size) / sector_size)
# sgdisk requires aligning to 2048-sector boundaries.
# It conservatively rounds down but we want to round up...
ss = int((ss + 2047) / 2048) * 2048
return ss
def __str__(self) -> str:
return f"DiskPartition {{name: {self.name}, size: {self.size_mb}M, offset: {self.offset / 1024 / 1024}M{', path: ' + self.path if self.path else ''}{', fs: ' + self.filesystem if self.filesystem else ''}}}"
RunOutputTypeDefault = Literal["log", "stdout", "interactive", "tui", "null"]
RunOutputTypePopen = Literal["background", "pipe"]
RunOutputType = RunOutputTypeDefault | RunOutputTypePopen
RunReturnType = str | int | subprocess.Popen
PathString = Path | str
Env = dict[str, PathString]
Apkbuild = dict[str, Any]
WithExtraRepos = Literal["default", "enabled", "disabled"]
# These types are not definitive / API, they exist to describe the current
# state of things so that we can improve our type hinting coverage and make
# future refactoring efforts easier.
class PartitionLayout(list[DiskPartition]):
"""
Subclass list to provide easy accessors without relying on
fragile indexes while still allowing the partitions to be
iterated over for simplicity. This is not a good design tbh
"""
path: str # path to disk image
split: bool # image per partition
fde: bool
def __init__(self, path: str, split: bool, fde: bool):
super().__init__(self)
# Path to the disk image
self.path = path
self.split = split
self.fde = fde
@property
def kernel(self):
"""
Get the kernel partition (specific to Chromebooks).
"""
if self[0].name != "kernel":
raise ValueError("First partition not kernel partition!")
return self[0]
@property
def boot(self):
"""
Get the boot partition, must be the first or second if we have
a kernel partition
"""
if self[0].name == "boot":
return self[0]
if self[0].name == "kernel" and self[1].name == "boot":
return self[1]
raise ValueError("First partition not boot partition!")
@property
def root(self):
"""
Get the root partition, must be the second or third if we have
a kernel partition
"""
if self[1].name == "root":
return self[1]
if self[0].name == "kernel" and self[2].name == "root":
return self[2]
raise ValueError("First partition not root partition!")
class AportGenEntry(TypedDict):
prefixes: list[str]
confirm_overwrite: bool
class Bootimg(TypedDict):
cmdline: str
qcdt: str
qcdt_type: str | None
dtb_offset: str | None
dtb_second: str
base: str
kernel_offset: str
ramdisk_offset: str
second_offset: str
tags_offset: str
pagesize: str
header_version: str | None
mtk_label_kernel: str
mtk_label_ramdisk: str
# Property list generated with:
# $ rg --vimgrep "((^|\s)args\.\w+)" --only-matching | cut -d"." -f3 | sort | uniq
class PmbArgs(Namespace):
action_flasher: str
action_initfs: str
action_kconfig: str
action_netboot: str
action_test: str
add: str
all: bool
all_git: bool
all_stable: bool
android_recovery_zip: bool
apkindex_path: Path
aports: list[Path] | None
arch: Arch | None
as_root: bool
assume_yes: bool
auto: bool
autoinstall: bool
boot_size: str
buildroot: str
built: bool
ccache: bool
ccache_size: str
chroot_usb: bool
cipher: str
clear_log: bool
cmdline: str
command: str
config: Path
cross: bool
details: bool
details_to_stdout: bool
deviceinfo_parse_kernel: str
devices: str
disk: Path
dry: bool
efi: bool
envkernel: bool
export_folder: Path
extra_space: str
fast: bool
file: str
filesystem: str
flash_method: str
folder: str
force: bool
fork_alpine: bool
fork_alpine_retain_branch: bool
full_disk_encryption: bool
go_mod_cache: bool
hook: str
host: str
host_qemu: bool
http: bool
ignore_depends: bool
image_size: str
install_base: bool
install_blockdev: bool
install_cgpt: bool
install_key: bool
install_local_pkgs: bool
install_recommends: bool
is_default_channel: str
iter_time: str
jobs: str
kconfig_check_details: bool
kernel: str
keymap: str
keep_going: bool
lines: int
log: Path
mirror_alpine: str
mirror_postmarketos: str
name: str
nconfig: bool
netboot: bool
no_depends: bool
no_fde: bool
no_firewall: bool
no_image: bool
no_reboot: bool
no_sshd: bool
non_existing: str
odin_flashable_tar: bool
offline: bool
output: RunOutputType
overview: bool
# FIXME (#2324): figure out the args.package vs args.packages situation
package: str | list[str]
packages: list[str]
partition: str
password: str
path: Path
pkgname: str
pkgname_pkgver_srcurl: str
pkgs_local: bool
pkgs_local_mismatch: bool
pkgs_online_mismatch: bool
port: str
qemu_audio: str
qemu_cpu: str
qemu_display: str
qemu_gl: bool
qemu_kvm: bool
qemu_redir_stdio: str
qemu_tablet: bool
qemu_video: str
recovery_flash_kernel: bool
recovery_install_partition: str
ref: str
replace: bool
repository: str
reset: bool
resume: bool
rootfs: bool
rsync: bool
scripts: str
second_storage: str
sector_size: int | None
selected_providers: dict[str, str]
sparse: bool
split: bool
src: str
ssh_keys: str
strict: bool
suffix: str
systemd: str
timeout: float
user: str
value: str
verbose: bool
verify: bool
work: Path
xauth: bool
xconfig: bool
zap: bool
# type: ignore