From 97bd8b96ec0df7d8d09a462535a6224f82b77a69 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Thu, 6 Jun 2024 15:05:59 +0200 Subject: [PATCH] parse: deviceinfo: make Deviceinfo a class (MR 2252) Introduce a Deviceinfo class and use it rather than the dictionary. This gives us sweet sweet autocomplete, and lays the foundation for having a proper deviceinfo validator in the future. Additionally, continue refactoring out args... Signed-off-by: Caleb Connolly --- pmb/aportgen/linux.py | 14 +- pmb/build/autodetect.py | 12 +- pmb/build/envkernel.py | 2 +- pmb/chroot/shutdown.py | 2 +- pmb/chroot/zap.py | 19 +-- pmb/config/init.py | 2 +- pmb/config/workdir.py | 2 +- pmb/export/frontend.py | 2 +- pmb/export/odin.py | 8 +- pmb/export/symlinks.py | 5 +- pmb/flasher/frontend.py | 62 ++++---- pmb/flasher/init.py | 13 +- pmb/flasher/run.py | 19 ++- pmb/flasher/variables.py | 47 +++--- pmb/helpers/args.py | 2 +- pmb/helpers/frontend.py | 22 +-- pmb/helpers/other.py | 8 +- pmb/helpers/repo_bootstrap.py | 48 +++--- pmb/helpers/status.py | 2 +- pmb/install/_install.py | 61 ++++---- pmb/install/format.py | 4 +- pmb/install/losetup.py | 2 +- pmb/install/partition.py | 10 +- pmb/install/recovery.py | 4 +- pmb/parse/deviceinfo.py | 281 ++++++++++++++++++++++------------ pmb/qemu/run.py | 6 +- pmb/types.py | 1 - 27 files changed, 372 insertions(+), 288 deletions(-) diff --git a/pmb/aportgen/linux.py b/pmb/aportgen/linux.py index 946f8040..decee098 100644 --- a/pmb/aportgen/linux.py +++ b/pmb/aportgen/linux.py @@ -1,16 +1,16 @@ # Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later from pmb.core import get_context -from pmb.types import PmbArgs +from pmb.parse.deviceinfo import Deviceinfo import pmb.helpers.run import pmb.aportgen.core import pmb.parse.apkindex import pmb.parse.arch -def generate_apkbuild(pkgname, deviceinfo, patches): +def generate_apkbuild(pkgname, deviceinfo: Deviceinfo, patches): device = "-".join(pkgname.split("-")[1:]) - carch = pmb.parse.arch.alpine_to_kernel(deviceinfo["arch"]) + carch = pmb.parse.arch.alpine_to_kernel(deviceinfo.arch) makedepends = ["bash", "bc", "bison", "devicepkg-dev", "findutils", "flex", "openssl-dev", "perl"] @@ -24,13 +24,13 @@ def generate_apkbuild(pkgname, deviceinfo, patches): downstreamkernel_package "$builddir" "$pkgdir" "$_carch\" \\ "$_flavor" "$_outdir\"""" - if deviceinfo.get("header_version") == "2": + if deviceinfo.header_version == "2": package += """ make dtbs_install O="$_outdir" ARCH="$_carch" \\ INSTALL_DTBS_PATH="$pkgdir\"/boot/dtbs""" - if deviceinfo["bootimg_qcdt"] == "true": + if deviceinfo.bootimg_qcdt == "true": build += """\n # Master DTB (deviceinfo_bootimg_qcdt)""" vendors = ["spreadtrum", "exynos", "other"] @@ -68,8 +68,8 @@ def generate_apkbuild(pkgname, deviceinfo, patches): pkgname={pkgname} pkgver=3.x.x pkgrel=0 - pkgdesc="{deviceinfo["name"]} kernel fork" - arch="{deviceinfo["arch"]}" + pkgdesc="{deviceinfo.name} kernel fork" + arch="{deviceinfo.arch}" _carch="{carch}" _flavor="{device}" url="https://kernel.org" diff --git a/pmb/build/autodetect.py b/pmb/build/autodetect.py index 11c8b0bc..e5494b34 100644 --- a/pmb/build/autodetect.py +++ b/pmb/build/autodetect.py @@ -13,7 +13,7 @@ from pmb.core import Chroot, ChrootType, get_context # FIXME (#2324): type hint Arch -def arch_from_deviceinfo(args: PmbArgs, pkgname, aport: Path) -> Optional[str]: +def arch_from_deviceinfo(pkgname, aport: Path) -> Optional[str]: """ The device- packages are noarch packages. But it only makes sense to build them for the device's architecture, which is specified in the deviceinfo @@ -31,12 +31,12 @@ def arch_from_deviceinfo(args: PmbArgs, pkgname, aport: Path) -> Optional[str]: # Return its arch device = pkgname.split("-", 1)[1] - arch = pmb.parse.deviceinfo(device)["arch"] + arch = pmb.parse.deviceinfo(device).arch logging.verbose(pkgname + ": arch from deviceinfo: " + arch) return arch -def arch(args: PmbArgs, pkgname: str): +def arch(pkgname: str): """ Find a good default in case the user did not specify for which architecture a package should be built. @@ -50,7 +50,7 @@ def arch(args: PmbArgs, pkgname: str): aport = pmb.helpers.pmaports.find(pkgname) if not aport: raise FileNotFoundError(f"APKBUILD not found for {pkgname}") - ret = arch_from_deviceinfo(args, pkgname, aport) + ret = arch_from_deviceinfo(pkgname, aport) if ret: return ret @@ -59,11 +59,11 @@ def arch(args: PmbArgs, pkgname: str): deviceinfo = pmb.parse.deviceinfo() if get_context().config.build_default_device_arch: - preferred_arch = deviceinfo["arch"] + preferred_arch = deviceinfo.arch preferred_arch_2nd = pmb.config.arch_native else: preferred_arch = pmb.config.arch_native - preferred_arch_2nd = deviceinfo["arch"] + preferred_arch_2nd = deviceinfo.arch if "noarch" in arches or "all" in arches or preferred_arch in arches: return preferred_arch diff --git a/pmb/build/envkernel.py b/pmb/build/envkernel.py index be78de79..adfdf791 100644 --- a/pmb/build/envkernel.py +++ b/pmb/build/envkernel.py @@ -190,7 +190,7 @@ def package_kernel(args: PmbArgs): modify_apkbuild(pkgname, aport) apkbuild_path = context.config.work / "aportgen/APKBUILD" - arch = pmb.parse.deviceinfo()["arch"] + arch = pmb.parse.deviceinfo().arch apkbuild = pmb.parse.apkbuild(apkbuild_path, check_pkgname=False) if apkbuild["_outdir"]: kbuild_out = apkbuild["_outdir"] diff --git a/pmb/chroot/shutdown.py b/pmb/chroot/shutdown.py index cb4065d5..80615904 100644 --- a/pmb/chroot/shutdown.py +++ b/pmb/chroot/shutdown.py @@ -58,7 +58,7 @@ def shutdown_cryptsetup_device(name: str): raise RuntimeError("Failed to parse 'cryptsetup status' output!") -def shutdown(args: PmbArgs, only_install_related=False): +def shutdown(only_install_related=False): # Stop daemons kill_adb() kill_sccache() diff --git a/pmb/chroot/zap.py b/pmb/chroot/zap.py index 96144d67..692e67f8 100644 --- a/pmb/chroot/zap.py +++ b/pmb/chroot/zap.py @@ -4,17 +4,18 @@ import glob from pmb.helpers import logging import os +import pmb.build.other +import pmb.config.workdir import pmb.chroot import pmb.config.pmaports import pmb.config.workdir -from pmb.types import PmbArgs import pmb.helpers.pmaports import pmb.helpers.run import pmb.parse.apkindex from pmb.core import Chroot, get_context -def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False, +def zap(confirm=True, dry=False, pkgs_local=False, http=False, pkgs_local_mismatch=False, pkgs_online_mismatch=False, distfiles=False, rust=False, netboot=False): """ @@ -38,13 +39,13 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False, # Delete packages with a different version compared to aports, # then re-index if pkgs_local_mismatch: - zap_pkgs_local_mismatch(args, confirm, dry) + zap_pkgs_local_mismatch(confirm, dry) # Delete outdated binary packages if pkgs_online_mismatch: - zap_pkgs_online_mismatch(args, confirm, dry) + zap_pkgs_online_mismatch(confirm, dry) - pmb.chroot.shutdown(args) + pmb.chroot.shutdown() # Deletion patterns for folders inside get_context().config.work patterns = list(Chroot.iter_patterns()) @@ -72,7 +73,7 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False, pmb.helpers.run.root(["rm", "-rf", match]) # Remove config init dates for deleted chroots - pmb.config.workdir.clean(args) + pmb.config.workdir.clean() # Chroots were zapped, so no repo lists exist anymore pmb.helpers.other.cache["apk_repository_list_updated"].clear() @@ -82,7 +83,7 @@ def zap(args: PmbArgs, confirm=True, dry=False, pkgs_local=False, http=False, logging.info("Dry run: nothing has been deleted") -def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False): +def zap_pkgs_local_mismatch(confirm=True, dry=False): channel = pmb.config.pmaports.read_config()["channel"] if not os.path.exists(f"{get_context().config.work}/packages/{channel}"): return @@ -131,10 +132,10 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False): reindex = True if reindex: - pmb.build.other.index_repo(args) + pmb.build.other.index_repo() -def zap_pkgs_online_mismatch(args: PmbArgs, confirm=True, dry=False): +def zap_pkgs_online_mismatch(confirm=True, dry=False): # Check whether we need to do anything paths = glob.glob(f"{get_context().config.work}/cache_apk_*") if not len(paths): diff --git a/pmb/config/init.py b/pmb/config/init.py index 453fb239..363fb44c 100644 --- a/pmb/config/init.py +++ b/pmb/config/init.py @@ -745,6 +745,6 @@ def frontend(args: PmbArgs): setattr(args, "deviceinfo", info) # Do not zap any existing packages or cache_http directories - pmb.chroot.zap(args, confirm=False) + pmb.chroot.zap(confirm=False) logging.info("DONE!") diff --git a/pmb/config/workdir.py b/pmb/config/workdir.py index 9020dab8..fe7717c2 100644 --- a/pmb/config/workdir.py +++ b/pmb/config/workdir.py @@ -90,7 +90,7 @@ def chroot_check_channel(chroot: Chroot): f" '{channel}' channel now. {msg_again}") -def clean(args: PmbArgs): +def clean(): """Remove obsolete data data from workdir.cfg. :returns: None if workdir does not exist, diff --git a/pmb/export/frontend.py b/pmb/export/frontend.py index ce5baeb5..8520eb46 100644 --- a/pmb/export/frontend.py +++ b/pmb/export/frontend.py @@ -33,4 +33,4 @@ def frontend(args: PmbArgs): # FIXME: ARGS_REFACTOR logging.info(f"Export symlinks to: {target}") if args.odin_flashable_tar: pmb.export.odin(context, flavor, target) - pmb.export.symlinks(args, flavor, target) + pmb.export.symlinks(flavor, target) diff --git a/pmb/export/odin.py b/pmb/export/odin.py index a5f54bfe..81ccacf3 100644 --- a/pmb/export/odin.py +++ b/pmb/export/odin.py @@ -18,7 +18,7 @@ def odin(context: Context, flavor, folder: Path): for devices configured with the flasher method 'heimdall-isorec' and with boot.img for devices with 'heimdall-bootimg' """ - pmb.flasher.init(context.device) + pmb.flasher.init(context.device, "heimdall-isorec") suffix = Chroot(ChrootType.ROOTFS, context.device) deviceinfo = pmb.parse.deviceinfo(context.device) @@ -29,7 +29,7 @@ def odin(context: Context, flavor, folder: Path): suffix_flavor = "" # Validate method - method = deviceinfo["flash_method"] + method = deviceinfo.flash_method or "" if not method.startswith("heimdall-"): raise RuntimeError("An odin flashable tar is not supported" f" for the flash method '{method}' specified" @@ -37,8 +37,8 @@ def odin(context: Context, flavor, folder: Path): " Only 'heimdall' methods are supported.") # Partitions - partition_kernel = deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL" - partition_initfs = deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY" + partition_kernel = deviceinfo.flash_heimdall_partition_kernel or "KERNEL" + partition_initfs = deviceinfo.flash_heimdall_partition_initfs or "RECOVERY" # Temporary folder temp_folder = "/tmp/odin-flashable-tar" diff --git a/pmb/export/symlinks.py b/pmb/export/symlinks.py index 665f98e6..5b94a116 100644 --- a/pmb/export/symlinks.py +++ b/pmb/export/symlinks.py @@ -9,19 +9,18 @@ import pmb.build import pmb.chroot.apk import pmb.config import pmb.config.pmaports -from pmb.types import PmbArgs import pmb.flasher import pmb.helpers.file from pmb.core import Chroot, ChrootType -def symlinks(args: PmbArgs, flavor, folder: Path): +def symlinks(flavor, folder: Path): """ Create convenience symlinks to the rootfs and boot files. """ context = get_context() - arch = pmb.parse.deviceinfo(context.device)["arch"] + arch = pmb.parse.deviceinfo(context.device).arch # Backwards compatibility with old mkinitfs (pma#660) suffix = f"-{flavor}" diff --git a/pmb/flasher/frontend.py b/pmb/flasher/frontend.py index e58bf537..ed58cfe2 100644 --- a/pmb/flasher/frontend.py +++ b/pmb/flasher/frontend.py @@ -1,10 +1,10 @@ # Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Dict from pmb.core.context import get_context from pmb.helpers import logging import pmb.config +from pmb.parse.deviceinfo import Deviceinfo from pmb.types import PmbArgs import pmb.flasher import pmb.install @@ -17,11 +17,11 @@ import pmb.parse.kconfig from pmb.core import Chroot, ChrootType -def kernel(device: str, deviceinfo: Dict[str, str], boot: bool = False, autoinstall: bool = False): +def kernel(deviceinfo: Deviceinfo, method: str, boot: bool = False, autoinstall: bool = False): # Rebuild the initramfs, just to make sure (see #69) - flavor = pmb.helpers.frontend._parse_flavor(device, autoinstall) + flavor = pmb.helpers.frontend._parse_flavor(deviceinfo.codename, autoinstall) if autoinstall: - pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, device)) + pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, deviceinfo.codename)) # Check kernel config pmb.parse.kconfig.check(flavor, must_exist=False) @@ -29,10 +29,10 @@ def kernel(device: str, deviceinfo: Dict[str, str], boot: bool = False, autoinst # Generate the paths and run the flasher if boot: logging.info("(native) boot " + flavor + " kernel") - pmb.flasher.run(device, deviceinfo, "boot", flavor) + pmb.flasher.run(deviceinfo, method, "boot", flavor) else: logging.info("(native) flash kernel " + flavor) - pmb.flasher.run(device, deviceinfo, "flash_kernel", flavor) + pmb.flasher.run(deviceinfo, method, "flash_kernel", flavor) logging.info("You will get an IP automatically assigned to your " "USB interface shortly.") logging.info("Then you can connect to your device using ssh after pmOS has" @@ -49,61 +49,61 @@ def list_flavors(device: str): logging.info("* " + pmb.chroot.other.kernel_flavor_installed(chroot)) -def rootfs(device: str, deviceinfo: Dict[str, str], method: str): +def rootfs(deviceinfo: Deviceinfo, method: str): # Generate rootfs, install flasher suffix = ".img" if pmb.config.flashers.get(method, {}).get("split", False): suffix = "-root.img" - img_path = Chroot.native() / "home/pmos/rootfs" / f"{device}{suffix}" + img_path = Chroot.native() / "home/pmos/rootfs" / f"{deviceinfo.codename}{suffix}" if not img_path.exists(): raise RuntimeError("The rootfs has not been generated yet, please run" " 'pmbootstrap install' first.") # Do not flash if using fastboot & image is too large if method.startswith("fastboot") \ - and deviceinfo["flash_fastboot_max_size"]: + and deviceinfo.flash_fastboot_max_size: img_size = img_path.stat().st_size / 1024**2 - max_size = int(deviceinfo["flash_fastboot_max_size"]) + max_size = int(deviceinfo.flash_fastboot_max_size) if img_size > max_size: raise RuntimeError("The rootfs is too large for fastboot to" " flash.") # Run the flasher logging.info("(native) flash rootfs image") - pmb.flasher.run(device, deviceinfo, "flash_rootfs") + pmb.flasher.run(deviceinfo, method, "flash_rootfs") -def list_devices(device, deviceinfo): - pmb.flasher.run(device, deviceinfo, "list_devices") +def list_devices(deviceinfo: Deviceinfo, method: str): + pmb.flasher.run(deviceinfo, method, "list_devices") -def sideload(device: str, deviceinfo: Dict[str, str]): +def sideload(deviceinfo: Deviceinfo, method: str): # Install depends - pmb.flasher.install_depends() + pmb.flasher.install_depends(method) # Mount the buildroot - chroot = Chroot.buildroot(deviceinfo["arch"]) + chroot = Chroot.buildroot(deviceinfo.arch) mountpoint = "/mnt/" / chroot pmb.helpers.mount.bind(chroot.path, Chroot.native().path / mountpoint) # Missing recovery zip error if not (Chroot.native() / mountpoint / "/var/lib/postmarketos-android-recovery-installer" - / f"pmos-{device}.zip").exists(): + / f"pmos-{deviceinfo.codename}.zip").exists(): raise RuntimeError("The recovery zip has not been generated yet," " please run 'pmbootstrap install' with the" " '--android-recovery-zip' parameter first!") - pmb.flasher.run(device, deviceinfo, "sideload") + pmb.flasher.run(deviceinfo, method, "sideload") -def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str): +def flash_lk2nd(deviceinfo: Deviceinfo, method: str): if method == "fastboot": # In the future this could be expanded to use "fastboot flash lk2nd $img" # which reflashes/updates lk2nd from itself. For now let the user handle this # manually since supporting the codepath with heimdall requires more effort. - pmb.flasher.init(device) + pmb.flasher.init(deviceinfo.codename, method) logging.info("(native) checking current fastboot product") output = pmb.chroot.root(["fastboot", "getvar", "product"], output="interactive", output_return=True) @@ -114,7 +114,7 @@ def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str): " bootloader mode to re-flash lk2nd.") # Get the lk2nd package (which is a dependency of the device package) - device_pkg = f"device-{device}" + device_pkg = f"device-{deviceinfo.codename}" apkbuild = pmb.helpers.pmaports.get(device_pkg) lk2nd_pkg = None for dep in apkbuild["depends"]: @@ -125,11 +125,11 @@ def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str): if not lk2nd_pkg: raise RuntimeError(f"{device_pkg} does not depend on any lk2nd package") - suffix = Chroot(ChrootType.ROOTFS, device) + suffix = Chroot(ChrootType.ROOTFS, deviceinfo.codename) pmb.chroot.apk.install([lk2nd_pkg], suffix) logging.info("(native) flash lk2nd image") - pmb.flasher.run(device, deviceinfo, "flash_lk2nd") + pmb.flasher.run(deviceinfo, method, "flash_lk2nd") def frontend(args: PmbArgs): @@ -137,7 +137,7 @@ def frontend(args: PmbArgs): action = args.action_flasher device = context.device deviceinfo = pmb.parse.deviceinfo() - method = args.flash_method or deviceinfo["flash_method"] + method = args.flash_method or deviceinfo.flash_method or "none" if method == "none" and action in ["boot", "flash_kernel", "flash_rootfs", "flash_lk2nd"]: @@ -145,20 +145,20 @@ def frontend(args: PmbArgs): return if action in ["boot", "flash_kernel"]: - kernel(device, deviceinfo) + kernel(deviceinfo, method) elif action == "flash_rootfs": - rootfs(device, deviceinfo, method) + rootfs(deviceinfo, method) elif action == "flash_vbmeta": logging.info("(native) flash vbmeta.img with verity disabled flag") - pmb.flasher.run(device, deviceinfo, "flash_vbmeta") + pmb.flasher.run(deviceinfo, method, "flash_vbmeta") elif action == "flash_dtbo": logging.info("(native) flash dtbo image") - pmb.flasher.run(device, deviceinfo, "flash_dtbo") + pmb.flasher.run(deviceinfo, method, "flash_dtbo") elif action == "flash_lk2nd": - flash_lk2nd(device, deviceinfo, method) + flash_lk2nd(deviceinfo, method) elif action == "list_flavors": list_flavors(device) elif action == "list_devices": - list_devices(device, deviceinfo) + pmb.flasher.run(deviceinfo, method, "list_devices") elif action == "sideload": - sideload(device, deviceinfo) + sideload(deviceinfo, method) diff --git a/pmb/flasher/init.py b/pmb/flasher/init.py index c9e7833e..12f37b9c 100644 --- a/pmb/flasher/init.py +++ b/pmb/flasher/init.py @@ -10,14 +10,7 @@ from pmb.helpers.mount import mount_device_rootfs from pmb.core import Chroot, ChrootType -def install_depends() -> None: - args: PmbArgs = pmb.helpers.args.please_i_really_need_args() - if hasattr(args, 'flash_method'): - method = args.flash_method - - if not method: - method = pmb.parse.deviceinfo()["flash_method"] - +def install_depends(method: str) -> None: if method not in pmb.config.flashers: raise RuntimeError(f"Flash method {method} is not supported by the" " current configuration. However, adding a new" @@ -46,8 +39,8 @@ def install_depends() -> None: pmb.chroot.apk.install(depends, Chroot.native()) -def init(device: str): - install_depends() +def init(device: str, method: str): + install_depends(method) # Mount folders from host system for folder in pmb.config.flash_mount_bind: diff --git a/pmb/flasher/run.py b/pmb/flasher/run.py index d1322479..d6cfd8ad 100644 --- a/pmb/flasher/run.py +++ b/pmb/flasher/run.py @@ -1,31 +1,28 @@ # Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later from typing import Dict +from pmb.parse.deviceinfo import Deviceinfo from pmb.types import PmbArgs import pmb.flasher import pmb.chroot.initfs import pmb.helpers.args -def check_partition_blacklist(args: PmbArgs, deviceinfo: Dict[str, str], key, value): +def check_partition_blacklist(deviceinfo: Deviceinfo, key, value): if not key.startswith("$PARTITION_"): return - name = deviceinfo["name"] - if value in deviceinfo["partition_blacklist"].split(","): + name = deviceinfo.name + if value in (deviceinfo.partition_blacklist or "").split(","): raise RuntimeError("'" + value + "'" + " partition is blacklisted " + "from being flashed! See the " + name + " device " + "wiki page for more information.") -def run(device: str, deviceinfo: Dict[str, str], action, flavor=None): - pmb.flasher.init(device) - - # FIXME: handle argparsing and pass in only the args we need. - args = pmb.helpers.args.please_i_really_need_args() +def run(deviceinfo: Deviceinfo, method: str, action: str, flavor=None): + pmb.flasher.init(deviceinfo.codename, method) # Verify action - method = args.flash_method or deviceinfo["flash_method"] cfg = pmb.config.flashers[method] if not isinstance(cfg["actions"], dict): raise TypeError(f"Flashers misconfigured! {method} key 'actions' should be a dictionary") @@ -38,6 +35,8 @@ def run(device: str, deviceinfo: Dict[str, str], action, flavor=None): "Deviceinfo_flash_methods>") # Variable setup + # FIXME: handle argparsing and pass in only the args we need. + args = pmb.helpers.args.please_i_really_need_args() fvars = pmb.flasher.variables(args, flavor, method) # vbmeta flasher requires vbmeta partition to be explicitly specified @@ -79,7 +78,7 @@ def run(device: str, deviceinfo: Dict[str, str], action, flavor=None): " but the value for this variable" " is None! Is that missing in your" " deviceinfo?") - check_partition_blacklist(args, deviceinfo, key, value) + check_partition_blacklist(deviceinfo, key, value) command[i] = command[i].replace(key, value) # Remove empty strings diff --git a/pmb/flasher/variables.py b/pmb/flasher/variables.py index 9709f1e0..35c29478 100644 --- a/pmb/flasher/variables.py +++ b/pmb/flasher/variables.py @@ -9,11 +9,11 @@ from pmb.types import PmbArgs def variables(args: PmbArgs, flavor, method): device = get_context().config.device deviceinfo = pmb.parse.deviceinfo() - _cmdline = deviceinfo["kernel_cmdline"] or "" + _cmdline = deviceinfo.kernel_cmdline or "" if "cmdline" in args and args.cmdline: _cmdline = args.cmdline - flash_pagesize = deviceinfo['flash_pagesize'] + flash_pagesize = deviceinfo.flash_pagesize # TODO Remove _partition_system deviceinfo support once pmaports has been # updated and minimum pmbootstrap version bumped. @@ -23,39 +23,39 @@ def variables(args: PmbArgs, flavor, method): _partition_rootfs: Optional[str] if method.startswith("fastboot"): - _partition_kernel = deviceinfo["flash_fastboot_partition_kernel"]\ + _partition_kernel = deviceinfo.flash_fastboot_partition_kernel\ or "boot" - _partition_rootfs = deviceinfo["flash_fastboot_partition_rootfs"]\ - or deviceinfo["flash_fastboot_partition_system"] or "userdata" - _partition_vbmeta = deviceinfo["flash_fastboot_partition_vbmeta"]\ + _partition_rootfs = deviceinfo.flash_fastboot_partition_rootfs\ + or deviceinfo.flash_fastboot_partition_system or "userdata" + _partition_vbmeta = deviceinfo.flash_fastboot_partition_vbmeta\ or None - _partition_dtbo = deviceinfo["flash_fastboot_partition_dtbo"]\ + _partition_dtbo = deviceinfo.flash_fastboot_partition_dtbo\ or None # Require that the partitions are specified in deviceinfo for now elif method.startswith("rkdeveloptool"): - _partition_kernel = deviceinfo["flash_rk_partition_kernel"]\ + _partition_kernel = deviceinfo.flash_rk_partition_kernel\ or None - _partition_rootfs = deviceinfo["flash_rk_partition_rootfs"]\ - or deviceinfo["flash_rk_partition_system"] or None + _partition_rootfs = deviceinfo.flash_rk_partition_rootfs\ + or deviceinfo.flash_rk_partition_system or None _partition_vbmeta = None _partition_dtbo = None elif method.startswith("mtkclient"): - _partition_kernel = deviceinfo["flash_mtkclient_partition_kernel"]\ + _partition_kernel = deviceinfo.flash_mtkclient_partition_kernel\ or "boot" - _partition_rootfs = deviceinfo["flash_mtkclient_partition_rootfs"]\ + _partition_rootfs = deviceinfo.flash_mtkclient_partition_rootfs\ or "userdata" - _partition_vbmeta = deviceinfo["flash_mtkclient_partition_vbmeta"]\ + _partition_vbmeta = deviceinfo.flash_mtkclient_partition_vbmeta\ or None - _partition_dtbo = deviceinfo["flash_mtkclient_partition_dtbo"]\ + _partition_dtbo = deviceinfo.flash_mtkclient_partition_dtbo\ or None else: - _partition_kernel = deviceinfo["flash_heimdall_partition_kernel"]\ + _partition_kernel = deviceinfo.flash_heimdall_partition_kernel\ or "KERNEL" - _partition_rootfs = deviceinfo["flash_heimdall_partition_rootfs"]\ - or deviceinfo["flash_heimdall_partition_system"] or "SYSTEM" - _partition_vbmeta = deviceinfo["flash_heimdall_partition_vbmeta"]\ + _partition_rootfs = deviceinfo.flash_heimdall_partition_rootfs\ + or deviceinfo.flash_heimdall_partition_system or "SYSTEM" + _partition_vbmeta = deviceinfo.flash_heimdall_partition_vbmeta\ or None - _partition_dtbo = deviceinfo["flash_heimdall_partition_dtbo"]\ + _partition_dtbo = deviceinfo.flash_heimdall_partition_dtbo\ or None if "partition" in args and args.partition: @@ -67,7 +67,7 @@ def variables(args: PmbArgs, flavor, method): _partition_dtbo = args.partition _dtb = "" - if deviceinfo["append_dtb"] == "true": + if deviceinfo.append_dtb == "true": _dtb = "-dtb" _no_reboot = "" @@ -86,16 +86,15 @@ def variables(args: PmbArgs, flavor, method): "$IMAGE": "/home/pmos/rootfs/" + device + ".img", "$KERNEL_CMDLINE": _cmdline, "$PARTITION_KERNEL": _partition_kernel, - "$PARTITION_INITFS": deviceinfo[ - "flash_heimdall_partition_initfs"] or "RECOVERY", + "$PARTITION_INITFS": deviceinfo.flash_heimdall_partition_initfs or "RECOVERY", "$PARTITION_ROOTFS": _partition_rootfs, "$PARTITION_VBMETA": _partition_vbmeta, "$PARTITION_DTBO": _partition_dtbo, "$FLASH_PAGESIZE": flash_pagesize, - "$RECOVERY_ZIP": "/mnt/buildroot_" + deviceinfo["arch"] + + "$RECOVERY_ZIP": "/mnt/buildroot_" + deviceinfo.arch + "/var/lib/postmarketos-android-recovery-installer" "/pmos-" + device + ".zip", - "$UUU_SCRIPT": "/mnt/rootfs_" + deviceinfo["codename"] + + "$UUU_SCRIPT": "/mnt/rootfs_" + deviceinfo.codename + "/usr/share/uuu/flash_script.lst", "$NO_REBOOT": _no_reboot, "$RESUME": _resume diff --git a/pmb/helpers/args.py b/pmb/helpers/args.py index 000ed9e5..7079ca36 100644 --- a/pmb/helpers/args.py +++ b/pmb/helpers/args.py @@ -99,7 +99,7 @@ def init(args: PmbArgs) -> PmbArgs: pmb.config.pmaports.read_config() pmb.helpers.git.parse_channels_cfg(config.aports) deviceinfo = pmb.parse.deviceinfo() - context.device_arch = deviceinfo["arch"] + context.device_arch = deviceinfo.arch # Remove attributes from args so they don't get used by mistake delattr(args, "timeout") diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py index 4d3f298b..880dd9bd 100644 --- a/pmb/helpers/frontend.py +++ b/pmb/helpers/frontend.py @@ -65,7 +65,7 @@ def _parse_suffix(args: PmbArgs) -> Chroot: return Chroot(ChrootType.ROOTFS, get_context().config.device) elif args.buildroot: if args.buildroot == "device": - return Chroot.buildroot(pmb.parse.deviceinfo()["arch"]) + return Chroot.buildroot(pmb.parse.deviceinfo().arch) else: return Chroot.buildroot(args.buildroot) elif args.suffix: @@ -118,14 +118,14 @@ def build(args: PmbArgs): # Ensure repo_bootstrap is done for all arches we intend to build for for package in args.packages: - arch_package = args.arch or pmb.build.autodetect.arch(args, package) + arch_package = args.arch or pmb.build.autodetect.arch(package) pmb.helpers.repo_bootstrap.require_bootstrap(arch_package, f"build {package} for {arch_package}") context = get_context() # Build all packages for package in args.packages: - arch_package = args.arch or pmb.build.autodetect.arch(args, package) + arch_package = args.arch or pmb.build.autodetect.arch(package) if not pmb.build.package(context, package, arch_package, force, args.strict, src=src): logging.info("NOTE: Package '" + package + "' is up to date. Use" @@ -215,7 +215,9 @@ def config(args: PmbArgs): logging.info("NOTE: Valid config keys: " + ", ".join(keys)) raise RuntimeError("Invalid config key: " + args.name) - config = pmb.config.load(args) + # Reload the config because get_context().config has been overwritten + # by any rogue cmdline arguments. + config = pmb.config.load(args.config) if args.reset: if args.name is None: raise RuntimeError("config --reset requires a name to be given.") @@ -242,7 +244,7 @@ def config(args: PmbArgs): def repo_bootstrap(args: PmbArgs): - pmb.helpers.repo_bootstrap.main(args) + pmb.helpers.repo_bootstrap.main(args.arch, args.repository) def repo_missing(args: PmbArgs): @@ -272,8 +274,8 @@ def install(args: PmbArgs): raise ValueError("Installation using rsync" " is not currently supported on btrfs filesystem.") - pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo["arch"], - f"do 'pmbootstrap install' for {deviceinfo['arch']}" + pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo.arch, + f"do 'pmbootstrap install' for {deviceinfo.arch}" " (deviceinfo_arch)") # On-device installer checks @@ -297,7 +299,7 @@ def install(args: PmbArgs): raise ValueError("--on-device-installer cannot be combined with" " --filesystem") - if deviceinfo["cgpt_kpart"]: + if deviceinfo.cgpt_kpart: raise ValueError("--on-device-installer cannot be used with" " ChromeOS devices") else: @@ -324,7 +326,7 @@ def install(args: PmbArgs): if not args.disk and args.split is None: # Default to split if the flash method requires it - flasher = pmb.config.flashers.get(deviceinfo["flash_method"], {}) + flasher = pmb.config.flashers.get(deviceinfo.flash_method, {}) if flasher.get("split", False): args.split = True @@ -567,7 +569,7 @@ def work_migrate(args: PmbArgs): def zap(args: PmbArgs): - pmb.chroot.zap(args, dry=args.dry, http=args.http, + pmb.chroot.zap(dry=args.dry, http=args.http, distfiles=args.distfiles, pkgs_local=args.pkgs_local, pkgs_local_mismatch=args.pkgs_local_mismatch, pkgs_online_mismatch=args.pkgs_online_mismatch, diff --git a/pmb/helpers/other.py b/pmb/helpers/other.py index 48fdcaf0..5cc61c16 100644 --- a/pmb/helpers/other.py +++ b/pmb/helpers/other.py @@ -107,7 +107,7 @@ def migrate_work_folder(args: PmbArgs): raise RuntimeError("Aborted.") # Zap and update abuild.conf - pmb.chroot.zap(args, False) + pmb.chroot.zap(False) conf = context.config.work / "config_abuild/abuild.conf" if os.path.exists(conf): pmb.helpers.run.root(["sed", "-i", @@ -145,7 +145,7 @@ def migrate_work_folder(args: PmbArgs): raise RuntimeError("Aborted.") # Zap chroots - pmb.chroot.zap(args, False) + pmb.chroot.zap(False) # Update version file migrate_success(context.config.work, 3) @@ -186,7 +186,7 @@ def migrate_work_folder(args: PmbArgs): raise RuntimeError("Aborted.") # Zap chroots - pmb.chroot.zap(args, False) + pmb.chroot.zap(False) # Move packages to edge subdir edge_path = context.config.work / "packages/edge" @@ -224,7 +224,7 @@ def migrate_work_folder(args: PmbArgs): # Zap chroots to avoid potential "ERROR: Chroot 'native' was created # for the 'stable' channel, but you are on the 'v20.05' channel now." - pmb.chroot.zap(args, False) + pmb.chroot.zap(False) # Migrate packages_dir = f"{context.config.work}/packages" diff --git a/pmb/helpers/repo_bootstrap.py b/pmb/helpers/repo_bootstrap.py index 7a0b0598..cf4c41c4 100644 --- a/pmb/helpers/repo_bootstrap.py +++ b/pmb/helpers/repo_bootstrap.py @@ -1,12 +1,13 @@ # Copyright 2024 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later +from typing import Optional from pmb.core.chroot import Chroot from pmb.helpers import logging import glob import pmb.config.pmaports import pmb.helpers.repo -from pmb.types import PmbArgs +from pmb.types import Config from pmb.core import get_context @@ -15,19 +16,15 @@ progress_total = 0 progress_step: str -def get_arch(args: PmbArgs): - if args.arch: - return args.arch - - if args.build_default_device_arch: - return pmb.parse.deviceinfo()["arch"] +def get_arch(config: Config): + if config.build_default_device_arch: + return pmb.parse.deviceinfo().arch return pmb.config.arch_native -def check_repo_arg(args: PmbArgs): +def check_repo_arg(repo: str): cfg = pmb.config.pmaports.read_config_repos() - repo = args.repository if repo in cfg: return @@ -41,9 +38,9 @@ def check_repo_arg(args: PmbArgs): " current branch") -def check_existing_pkgs(args: PmbArgs, arch): +def check_existing_pkgs(config: Config, arch): channel = pmb.config.pmaports.read_config()["channel"] - path = get_context().config.work / "packages" / channel / arch + path = config.work / "packages" / channel / arch if glob.glob(f"{path}/*"): logging.info(f"Packages path: {path}") @@ -57,12 +54,12 @@ def check_existing_pkgs(args: PmbArgs, arch): raise RuntimeError(f"{msg}!") -def get_steps(args: PmbArgs): +def get_steps(repo: str): cfg = pmb.config.pmaports.read_config_repos() prev_step = 0 ret = {} - for key, packages in cfg[args.repository].items(): + for key, packages in cfg[repo].items(): if not key.startswith("bootstrap_"): continue @@ -76,7 +73,7 @@ def get_steps(args: PmbArgs): return ret -def get_suffix(args: PmbArgs, arch): +def get_suffix(arch): if pmb.parse.arch.cpu_emulation_required(arch): return f"buildroot_{arch}" return "native" @@ -91,7 +88,7 @@ def get_packages(bootstrap_line): return ret -def set_progress_total(args: PmbArgs, steps, arch): +def set_progress_total(steps, arch): global progress_total progress_total = 0 @@ -117,14 +114,14 @@ def log_progress(msg): progress_done += 1 -def run_steps(args: PmbArgs, steps, arch, chroot: Chroot): +def run_steps(steps, arch, chroot: Chroot): global progress_step for step, bootstrap_line in steps.items(): progress_step = step.replace("bootstrap_", "BOOTSTRAP=") log_progress("zapping") - pmb.chroot.zap(args, confirm=False) + pmb.chroot.zap(confirm=False) usr_merge = pmb.chroot.UsrMerge.OFF if "[usr_merge]" in bootstrap_line: @@ -148,17 +145,18 @@ def run_steps(args: PmbArgs, steps, arch, chroot: Chroot): log_progress("bootstrap complete!") -def main(args: PmbArgs): - check_repo_arg(args) +def main(arch: Optional[str], repository: str): # noqa: F821 + config = get_context().config + check_repo_arg(repository) - arch = get_arch(args) - check_existing_pkgs(args, arch) + arch = arch or get_arch(config) + check_existing_pkgs(config, arch) - steps = get_steps(args) - suffix = get_suffix(args, arch) + steps = get_steps(repository) + suffix = get_suffix(arch) - set_progress_total(args, steps, arch) - run_steps(args, steps, arch, suffix) + set_progress_total(steps, arch) + run_steps(steps, arch, suffix) def require_bootstrap_error(repo, arch, trigger_str): diff --git a/pmb/helpers/status.py b/pmb/helpers/status.py index ae687b72..23bce842 100644 --- a/pmb/helpers/status.py +++ b/pmb/helpers/status.py @@ -38,7 +38,7 @@ def print_device(config: Config) -> None: if pmb.parse._apkbuild.kernels(config.device): kernel = f", kernel: {config.kernel}" - value = f"{config.device} ({pmb.parse.deviceinfo()['arch']}{kernel})" + value = f"{config.device} ({pmb.parse.deviceinfo().arch}{kernel})" print_status_line("Device", value) diff --git a/pmb/install/_install.py b/pmb/install/_install.py index 48532294..dd5f7bc8 100644 --- a/pmb/install/_install.py +++ b/pmb/install/_install.py @@ -16,6 +16,7 @@ import pmb.chroot.other import pmb.chroot.initfs import pmb.config import pmb.config.pmaports +from pmb.parse.deviceinfo import Deviceinfo from pmb.types import Config, PartitionLayout, PmbArgs import pmb.helpers.devices from pmb.helpers.mount import mount_device_rootfs @@ -118,7 +119,7 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot): mountpoint_outside = Chroot.native() / mountpoint # Remove empty qemu-user binary stub (where the binary was bind-mounted) - arch_qemu = pmb.parse.arch.alpine_to_qemu(pmb.parse.deviceinfo()["arch"]) + arch_qemu = pmb.parse.arch.alpine_to_qemu(pmb.parse.deviceinfo().arch) qemu_binary = mountpoint_outside / ("/usr/bin/qemu-" + arch_qemu + "-static") if os.path.exists(qemu_binary): pmb.helpers.run.root(["rm", qemu_binary]) @@ -185,7 +186,7 @@ def configure_apk(args: PmbArgs): pmb.helpers.run.root(["cp", key, rootfs / "etc/apk/keys/"]) # Copy over the corresponding APKINDEX files from cache - index_files = pmb.helpers.repo.apkindex_files(arch=pmb.parse.deviceinfo()["arch"], + index_files = pmb.helpers.repo.apkindex_files(arch=pmb.parse.deviceinfo().arch, user_repository=False) for f in index_files: pmb.helpers.run.root(["cp", f, rootfs / "var/cache/apk/"]) @@ -325,11 +326,11 @@ def setup_keymap(config: Config): Set the keymap with the setup-keymap utility if the device requires it """ chroot = Chroot(ChrootType.ROOTFS, config.device) - info = pmb.parse.deviceinfo(device=config.device) - if "keymaps" not in info or info["keymaps"].strip() == "": + deviceinfo = pmb.parse.deviceinfo(device=config.device) + if not deviceinfo.keymaps or deviceinfo.keymaps.strip() == "": logging.info("NOTE: No valid keymap specified for device") return - options = info["keymaps"].split(' ') + options = deviceinfo.keymaps.split(' ') if (config.keymap != "" and config.keymap is not None and config.keymap in options): @@ -525,7 +526,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step): """ binary_ranges: Dict[int, int] = {} binary_list = [] - binaries = pmb.parse.deviceinfo()["sd_embed_firmware"].split(",") + binaries = pmb.parse.deviceinfo().sd_embed_firmware.split(",") for binary_offset in binaries: binary, _offset = binary_offset.split(':') @@ -541,7 +542,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step): f"/usr/share/{binary}") # Insure that embedding the firmware will not overrun the # first partition - boot_part_start = pmb.parse.deviceinfo()["boot_part_start"] or "2048" + boot_part_start = pmb.parse.deviceinfo().boot_part_start or "2048" max_size = (int(boot_part_start) * 512) - (offset * step) binary_size = os.path.getsize(binary_path) if binary_size > max_size: @@ -574,13 +575,13 @@ def embed_firmware(args: PmbArgs, suffix: Chroot): :param suffix: of the chroot, which holds the firmware files (either the rootfs_{args.device} or installer_{args.device} """ - if not pmb.parse.deviceinfo()["sd_embed_firmware"]: + if not pmb.parse.deviceinfo().sd_embed_firmware: return step = 1024 - if pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]: + if pmb.parse.deviceinfo().sd_embed_firmware_step_size: try: - step = int(pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]) + step = int(pmb.parse.deviceinfo().sd_embed_firmware_step_size) except ValueError: raise RuntimeError("Value for " "deviceinfo_sd_embed_firmware_step_size " @@ -606,7 +607,7 @@ def write_cgpt_kpart(args: PmbArgs, layout, suffix: Chroot): :param layout: partition layout from get_partition_layout() :param suffix: of the chroot, which holds the image file to be flashed """ - if not pmb.parse.deviceinfo()["cgpt_kpart"] or not args.install_cgpt: + if not pmb.parse.deviceinfo().cgpt_kpart or not args.install_cgpt: return device_rootfs = mount_device_rootfs(suffix) @@ -667,7 +668,7 @@ def sanity_check_disk_size(args: PmbArgs): def get_ondev_pkgver(args: PmbArgs): - arch = pmb.parse.deviceinfo()["arch"] + arch = pmb.parse.deviceinfo().arch package = pmb.helpers.package.get(args, "postmarketos-ondev", arch) return package["version"].split("-r")[0] @@ -762,7 +763,7 @@ def create_fstab(args: PmbArgs, layout, chroot: Chroot): else f"UUID={get_uuid(args, root_dev)}" boot_options = "nodev,nosuid,noexec" - boot_filesystem = pmb.parse.deviceinfo()["boot_filesystem"] or "ext2" + boot_filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2" if boot_filesystem in ("fat16", "fat32"): boot_filesystem = "vfat" boot_options += ",umask=0077,nosymfollow,codepage=437,iocharset=ascii" @@ -812,15 +813,15 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step device = chroot.name() # Partition and fill image file/disk block device logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***") - pmb.chroot.shutdown(args, True) + pmb.chroot.shutdown(True) (size_boot, size_root) = get_subpartitions_size(chroot) - layout = get_partition_layout(size_reserve, pmb.parse.deviceinfo()["cgpt_kpart"] \ + layout = get_partition_layout(size_reserve, pmb.parse.deviceinfo().cgpt_kpart \ and args.install_cgpt) if not args.rsync: pmb.install.blockdevice.create(args, size_boot, size_root, size_reserve, split, disk) if not split: - if pmb.parse.deviceinfo()["cgpt_kpart"] and args.install_cgpt: + if pmb.parse.deviceinfo().cgpt_kpart and args.install_cgpt: pmb.install.partition_cgpt( args, layout, size_boot, size_reserve) else: @@ -862,12 +863,12 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step if disk: logging.info(f"Unmounting disk {disk} (this may take a while " "to sync, please wait)") - pmb.chroot.shutdown(args, True) + pmb.chroot.shutdown(True) # Convert rootfs to sparse using img2simg sparse = args.sparse if sparse is None: - sparse = pmb.parse.deviceinfo()["flash_sparse"] == "true" + sparse = pmb.parse.deviceinfo().flash_sparse == "true" if sparse and not split and not disk: workdir = Path("/home/pmos/rootfs") @@ -881,7 +882,7 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step working_dir=workdir) # patch sparse image for Samsung devices if specified - samsungify_strategy = pmb.parse.deviceinfo()["flash_sparse_samsung_format"] + samsungify_strategy = pmb.parse.deviceinfo().flash_sparse_samsung_format if samsungify_strategy: logging.info("(native) convert sparse image into Samsung's sparse image format") pmb.chroot.apk.install(["sm-sparse-image-tool"], Chroot.native()) @@ -894,14 +895,14 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step working_dir=workdir) -def print_flash_info(device: str, deviceinfo: Dict[str, str], split: bool, have_disk: bool): +def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk: bool): """ Print flashing information, based on the deviceinfo data and the pmbootstrap arguments. """ logging.info("") # make the note stand out logging.info("*** FLASHING INFORMATION ***") # System flash information - method = deviceinfo["flash_method"] + method = deviceinfo.flash_method flasher = pmb.config.flashers.get(method, {}) flasher_actions = flasher.get("actions", {}) if not isinstance(flasher_actions, dict): @@ -934,16 +935,16 @@ def print_flash_info(device: str, deviceinfo: Dict[str, str], split: bool, have_ # if current flasher supports vbmeta and partition is explicitly specified # in deviceinfo if "flash_vbmeta" in flasher_actions and \ - (deviceinfo["flash_fastboot_partition_vbmeta"] or - deviceinfo["flash_heimdall_partition_vbmeta"]): + (deviceinfo.flash_fastboot_partition_vbmeta or + deviceinfo.flash_heimdall_partition_vbmeta): logging.info("* pmbootstrap flasher flash_vbmeta") logging.info(" Flashes vbmeta image with verification disabled flag.") # if current flasher supports dtbo and partition is explicitly specified # in deviceinfo if "flash_dtbo" in flasher_actions and \ - (deviceinfo["flash_fastboot_partition_dtbo"] or - deviceinfo["flash_heimdall_partition_dtbo"]): + (deviceinfo.flash_fastboot_partition_dtbo or + deviceinfo.flash_heimdall_partition_dtbo): logging.info("* pmbootstrap flasher flash_dtbo") logging.info(" Flashes dtbo image.") @@ -1226,7 +1227,7 @@ def create_device_rootfs(args: PmbArgs, step, steps): else: install_packages += ["postmarketos-base-nofde"] - pmb.helpers.repo.update(pmb.parse.deviceinfo()["arch"]) + pmb.helpers.repo.update(pmb.parse.deviceinfo().arch) # Install uninstallable "dependencies" by default install_packages += get_recommends(args, install_packages) @@ -1235,7 +1236,7 @@ def create_device_rootfs(args: PmbArgs, step, steps): # dependency, in case the version increased if config.build_pkgs_on_install: for pkgname in install_packages: - pmb.build.package(context, pkgname, pmb.parse.deviceinfo()["arch"]) + pmb.build.package(context, pkgname, pmb.parse.deviceinfo().arch) # Install all packages to device rootfs chroot (and rebuild the initramfs, # because that doesn't always happen automatically yet, e.g. when the user @@ -1296,7 +1297,7 @@ def install(args: PmbArgs): steps = 4 if args.zap: - pmb.chroot.zap(args, False) + pmb.chroot.zap(False) # Install required programs in native chroot step = 1 @@ -1313,7 +1314,7 @@ def install(args: PmbArgs): if args.no_image: return elif args.android_recovery_zip: - return install_recovery_zip(args, device, deviceinfo["arch"], steps) + return install_recovery_zip(args, device, deviceinfo.arch, steps) if args.on_device_installer: # Runs install_system_image twice @@ -1324,7 +1325,7 @@ def install(args: PmbArgs): print_flash_info(device, deviceinfo, args.split, True if args.disk and args.disk.is_absolute() else False) print_sshd_info(args) - print_firewall_info(args.no_firewall, deviceinfo["arch"]) + print_firewall_info(args.no_firewall, deviceinfo.arch) # Leave space before 'chroot still active' note logging.info("") diff --git a/pmb/install/format.py b/pmb/install/format.py index 318c883d..b88fdbc9 100644 --- a/pmb/install/format.py +++ b/pmb/install/format.py @@ -23,7 +23,7 @@ def format_and_mount_boot(args: PmbArgs, device, boot_label): ondev-prepare-internal-storage.sh in postmarketos-ondev.git! """ mountpoint = "/mnt/install/boot" - filesystem = pmb.parse.deviceinfo()["boot_filesystem"] or "ext2" + filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2" install_fsprogs(filesystem) logging.info(f"(native) format {device} (boot, {filesystem}), mount to" f" {mountpoint}") @@ -72,7 +72,7 @@ def format_luks_root(args: PmbArgs, device): def get_root_filesystem(args: PmbArgs): - ret = args.filesystem or pmb.parse.deviceinfo()["root_filesystem"] or "ext4" + ret = args.filesystem or pmb.parse.deviceinfo().root_filesystem or "ext4" pmaports_cfg = pmb.config.pmaports.read_config() supported = pmaports_cfg.get("supported_root_filesystems", "ext4") diff --git a/pmb/install/losetup.py b/pmb/install/losetup.py index 031365d7..d1408c97 100644 --- a/pmb/install/losetup.py +++ b/pmb/install/losetup.py @@ -42,7 +42,7 @@ def mount(args: PmbArgs, img_path: Path): init() losetup_cmd: List[PathString] = ["losetup", "-f", img_path] - sector_size = pmb.parse.deviceinfo()["rootfs_image_sector_size"] + sector_size = pmb.parse.deviceinfo().rootfs_image_sector_size if sector_size: losetup_cmd += ["-b", str(int(sector_size))] diff --git a/pmb/install/partition.py b/pmb/install/partition.py index e46fb46b..ff431367 100644 --- a/pmb/install/partition.py +++ b/pmb/install/partition.py @@ -80,15 +80,15 @@ def partition(args: PmbArgs, layout, size_boot, size_reserve): logging.info(f"(native) partition /dev/install (boot: {mb_boot}," f" reserved: {mb_reserved}, root: the rest)") - filesystem = pmb.parse.deviceinfo()["boot_filesystem"] or "ext2" + filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2" # Actual partitioning with 'parted'. Using check=False, because parted # sometimes "fails to inform the kernel". In case it really failed with # partitioning, the follow-up mounting/formatting will not work, so it # will stop there (see #463). - boot_part_start = pmb.parse.deviceinfo()["boot_part_start"] or "2048" + boot_part_start = pmb.parse.deviceinfo().boot_part_start or "2048" - partition_type = pmb.parse.deviceinfo()["partition_type"] or "msdos" + partition_type = pmb.parse.deviceinfo().partition_type or "msdos" commands = [ ["mktable", partition_type], @@ -131,8 +131,8 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve): pmb.chroot.apk.install(["cgpt"], Chroot.native(), build=False) cgpt = { - 'kpart_start': pmb.parse.deviceinfo()["cgpt_kpart_start"], - 'kpart_size': pmb.parse.deviceinfo()["cgpt_kpart_size"], + 'kpart_start': pmb.parse.deviceinfo().cgpt_kpart_start, + 'kpart_size': pmb.parse.deviceinfo().cgpt_kpart_size, } # Convert to MB and print info diff --git a/pmb/install/recovery.py b/pmb/install/recovery.py index 6e5dc8e6..459d1502 100644 --- a/pmb/install/recovery.py +++ b/pmb/install/recovery.py @@ -20,7 +20,7 @@ def create_zip(args: PmbArgs, chroot: Chroot, device: str): rootfs = "/mnt/rootfs_" + device flavor = pmb.helpers.frontend._parse_flavor(device) deviceinfo = pmb.parse.deviceinfo() - method = deviceinfo["flash_method"] + method = deviceinfo.flash_method fvars = pmb.flasher.variables(args, flavor, method) # Install recovery installer package in buildroot @@ -30,7 +30,7 @@ def create_zip(args: PmbArgs, chroot: Chroot, device: str): logging.info(f"({chroot}) create recovery zip") for key in fvars: - pmb.flasher.check_partition_blacklist(args, deviceinfo, key, fvars[key]) + pmb.flasher.check_partition_blacklist(deviceinfo, key, fvars[key]) # Create config file for the recovery installer options = { diff --git a/pmb/parse/deviceinfo.py b/pmb/parse/deviceinfo.py index 0cf823a0..651d734d 100644 --- a/pmb/parse/deviceinfo.py +++ b/pmb/parse/deviceinfo.py @@ -1,7 +1,8 @@ # Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later import copy -from typing import Dict +from pathlib import Path +from typing import Dict, Optional from pmb.core import get_context from pmb.helpers import logging import os @@ -9,77 +10,12 @@ import pmb.config import pmb.helpers.other import pmb.helpers.devices - -def sanity_check(info, path): - # Resolve path for more readable error messages - path = os.path.realpath(path) - - # Legacy errors - if "flash_methods" in info: - raise RuntimeError("deviceinfo_flash_methods has been renamed to" - " deviceinfo_flash_method. Please adjust your" - " deviceinfo file: " + path) - if "external_disk" in info or "external_disk_install" in info: - raise RuntimeError("Instead of deviceinfo_external_disk and" - " deviceinfo_external_disk_install, please use the" - " new variable deviceinfo_external_storage in your" - " deviceinfo file: " + path) - if "msm_refresher" in info: - raise RuntimeError("It is enough to specify 'msm-fb-refresher' in the" - " depends of your device's package now. Please" - " delete the deviceinfo_msm_refresher line in: " + - path) - if "flash_fastboot_vendor_id" in info: - raise RuntimeError("Fastboot doesn't allow specifying the vendor ID" - " anymore (#1830). Try removing the" - " 'deviceinfo_flash_fastboot_vendor_id' line in: " + - path + " (if you are sure that you need this, then" - " we can probably bring it back to fastboot, just" - " let us know in the postmarketOS issues!)") - if "nonfree" in info: - raise RuntimeError("deviceinfo_nonfree is unused. " - "Please delete it in: " + path) - if "dev_keyboard" in info: - raise RuntimeError("deviceinfo_dev_keyboard is unused. " - "Please delete it in: " + path) - if "date" in info: - raise RuntimeError("deviceinfo_date was replaced by deviceinfo_year. " - "Set it to the release year in: " + path) - - # "codename" is required - codename = os.path.basename(os.path.dirname(path)) - if codename.startswith("device-"): - codename = codename[7:] - if "codename" not in info or info["codename"] != codename: - raise RuntimeError(f"Please add 'deviceinfo_codename=\"{codename}\"' " - f"to: {path}") - - # "chassis" is required - chassis_types = pmb.config.deviceinfo_chassis_types - if "chassis" not in info or not info["chassis"]: - logging.info("NOTE: the most commonly used chassis types in" - " postmarketOS are 'handset' (for phones) and 'tablet'.") - raise RuntimeError(f"Please add 'deviceinfo_chassis' to: {path}") - - # "arch" is required - if "arch" not in info or not info["arch"]: - raise RuntimeError(f"Please add 'deviceinfo_arch' to: {path}") - - arch = info["arch"] - if (arch != pmb.config.arch_native and - arch not in pmb.config.build_device_architectures): - raise ValueError("Arch '" + arch + "' is not available in" - " postmarketOS. If you would like to add it, see:" - " ") - - # "chassis" validation - chassis_type = info["chassis"] - if chassis_type not in chassis_types: - raise RuntimeError(f"Unknown chassis type '{chassis_type}', should" - f" be one of {', '.join(chassis_types)}. Fix this" - f" and try again: {path}") - - +# FIXME: It feels weird to handle this at parse time. +# we should instead have the Deviceinfo object store +# the attributes for all kernels and require the user +# to specify which one they're using. +# Basically: treat Deviceinfo as a standalone type that +# doesn't need to traverse pmaports. def _parse_kernel_suffix(info, device, kernel): """ Remove the kernel suffix (as selected in 'pmbootstrap init') from @@ -108,7 +44,7 @@ def _parse_kernel_suffix(info, device, kernel): ret = copy.copy(info) suffix_kernel = kernel.replace("-", "_") - for key in pmb.config.deviceinfo_attributes: + for key in Deviceinfo.__annotations__.keys(): key_kernel = f"{key}_{suffix_kernel}" if key_kernel not in ret: continue @@ -121,8 +57,7 @@ def _parse_kernel_suffix(info, device, kernel): return ret -# FIXME (#2324): Make deviceinfo a type! (class!!!) -def deviceinfo(device=None, kernel=None) -> Dict[str, str]: +def deviceinfo(device=None, kernel=None) -> "Deviceinfo": """ :param device: defaults to args.device :param kernel: defaults to args.kernel @@ -150,25 +85,183 @@ def deviceinfo(device=None, kernel=None) -> Dict[str, str]: " start a new device port or to choose another device. It may have" " been renamed, see ") - ret = {} - with open(path) as handle: - for line in handle: - if not line.startswith("deviceinfo_"): - continue - if "=" not in line: - raise SyntaxError(f"{path}: No '=' found:\n\t{line}") - split = line.split("=", 1) - key = split[0][len("deviceinfo_"):] - value = split[1].replace("\"", "").replace("\n", "") - ret[key] = value + di = Deviceinfo(path, kernel) - # Assign empty string as default - for key in pmb.config.deviceinfo_attributes: - if key not in ret: - ret[key] = "" + pmb.helpers.other.cache["deviceinfo"][device] = di + return di - ret = _parse_kernel_suffix(ret, device, kernel) - sanity_check(ret, path) +class Deviceinfo: + """Variables from deviceinfo. Reference: + Many of these are unused in pmbootstrap, and still more that are described + on the wiki are missing. Eventually this class and associated code should + be moved to a separate library and become the authoritative source of truth + for the deviceinfo format.""" + path: Path + # general + format_version: str + name: str + manufacturer: str + codename: str + year: str + dtb: str + arch: str - pmb.helpers.other.cache["deviceinfo"][device] = ret - return ret + # device + chassis: str + keyboard: Optional[str] = "" + external_storage: Optional[str] = "" + dev_touchscreen: Optional[str] = "" + dev_touchscreen_calibration: Optional[str] = "" + append_dtb: Optional[str] = "" + + # bootloader + flash_method: Optional[str] = "" + boot_filesystem: Optional[str] = "" + + # flash + flash_heimdall_partition_kernel: Optional[str] = "" + flash_heimdall_partition_initfs: Optional[str] = "" + flash_heimdall_partition_rootfs: Optional[str] = "" + flash_heimdall_partition_system: Optional[str] = "" # deprecated + flash_heimdall_partition_vbmeta: Optional[str] = "" + flash_heimdall_partition_dtbo: Optional[str] = "" + flash_fastboot_partition_kernel: Optional[str] = "" + flash_fastboot_partition_rootfs: Optional[str] = "" + flash_fastboot_partition_system: Optional[str] = "" # deprecated + flash_fastboot_partition_vbmeta: Optional[str] = "" + flash_fastboot_partition_dtbo: Optional[str] = "" + flash_rk_partition_kernel: Optional[str] = "" + flash_rk_partition_rootfs: Optional[str] = "" + flash_rk_partition_system: Optional[str] = "" # deprecated + flash_mtkclient_partition_kernel: Optional[str] = "" + flash_mtkclient_partition_rootfs: Optional[str] = "" + flash_mtkclient_partition_vbmeta: Optional[str] = "" + flash_mtkclient_partition_dtbo: Optional[str] = "" + generate_legacy_uboot_initfs: Optional[str] = "" + kernel_cmdline: Optional[str] = "" + generate_bootimg: Optional[str] = "" + header_version: Optional[str] = "" + bootimg_qcdt: Optional[str] = "" + bootimg_mtk_mkimage: Optional[str] = "" # deprecated + bootimg_mtk_label_kernel: Optional[str] = "" + bootimg_mtk_label_ramdisk: Optional[str] = "" + bootimg_dtb_second: Optional[str] = "" + bootimg_custom_args: Optional[str] = "" + flash_offset_base: Optional[str] = "" + flash_offset_dtb: Optional[str] = "" + flash_offset_kernel: Optional[str] = "" + flash_offset_ramdisk: Optional[str] = "" + flash_offset_second: Optional[str] = "" + flash_offset_tags: Optional[str] = "" + flash_pagesize: Optional[str] = "" + flash_fastboot_max_size: Optional[str] = "" + flash_sparse: Optional[str] = "" + flash_sparse_samsung_format: Optional[str] = "" + rootfs_image_sector_size: Optional[str] = "" + sd_embed_firmware: Optional[str] = "" + sd_embed_firmware_step_size: Optional[str] = "" + partition_blacklist: Optional[str] = "" + boot_part_start: Optional[str] = "" + partition_type: Optional[str] = "" + root_filesystem: Optional[str] = "" + flash_kernel_on_update: Optional[str] = "" + cgpt_kpart: Optional[str] = "" + cgpt_kpart_start: Optional[str] = "" + cgpt_kpart_size: Optional[str] = "" + + # weston + weston_pixman_type: Optional[str] = "" + + # keymaps + keymaps: Optional[str] = "" + + @staticmethod + def __validate(info: Dict[str, str], path: Path): + # Resolve path for more readable error messages + path = path.resolve() + + # Legacy errors + if "flash_methods" in info: + raise RuntimeError("deviceinfo_flash_methods has been renamed to" + " deviceinfo_flash_method. Please adjust your" + f" deviceinfo file: {path}") + if "external_disk" in info or "external_disk_install" in info: + raise RuntimeError("Instead of deviceinfo_external_disk and" + " deviceinfo_external_disk_install, please use the" + " new variable deviceinfo_external_storage in your" + f" deviceinfo file: {path}") + if "msm_refresher" in info: + raise RuntimeError("It is enough to specify 'msm-fb-refresher' in the" + " depends of your device's package now. Please" + " delete the deviceinfo_msm_refresher line in: " + f"{path}") + if "flash_fastboot_vendor_id" in info: + raise RuntimeError("Fastboot doesn't allow specifying the vendor ID" + " anymore (#1830). Try removing the" + " 'deviceinfo_flash_fastboot_vendor_id' line in: " + f"{path} (if you are sure that you need this, then" + " we can probably bring it back to fastboot, just" + " let us know in the postmarketOS issues!)") + if "nonfree" in info: + raise RuntimeError("deviceinfo_nonfree is unused. " + f"Please delete it in: {path}") + if "dev_keyboard" in info: + raise RuntimeError("deviceinfo_dev_keyboard is unused. " + f"Please delete it in: {path}") + if "date" in info: + raise RuntimeError("deviceinfo_date was replaced by deviceinfo_year. " + f"Set it to the release year in: {path}") + + # "codename" is required + codename = os.path.basename(os.path.dirname(path))[7:] + if "codename" not in info or info["codename"] != codename: + raise RuntimeError(f"Please add 'deviceinfo_codename=\"{codename}\"' " + f"to: {path}") + + # "chassis" is required + chassis_types = pmb.config.deviceinfo_chassis_types + if "chassis" not in info or not info["chassis"]: + logging.info("NOTE: the most commonly used chassis types in" + " postmarketOS are 'handset' (for phones) and 'tablet'.") + raise RuntimeError(f"Please add 'deviceinfo_chassis' to: {path}") + + # "arch" is required + if "arch" not in info or not info["arch"]: + raise RuntimeError(f"Please add 'deviceinfo_arch' to: {path}") + + arch = info["arch"] + if (arch != pmb.config.arch_native and + arch not in pmb.config.build_device_architectures): + raise ValueError("Arch '" + arch + "' is not available in" + " postmarketOS. If you would like to add it, see:" + " ") + + # "chassis" validation + chassis_type = info["chassis"] + if chassis_type not in chassis_types: + raise RuntimeError(f"Unknown chassis type '{chassis_type}', should" + f" be one of {', '.join(chassis_types)}. Fix this" + f" and try again: {path}") + + + def __init__(self, path: Path, kernel: Optional[str] = None): + ret = {} + with open(path) as handle: + for line in handle: + if not line.startswith("deviceinfo_"): + continue + if "=" not in line: + raise SyntaxError(f"{path}: No '=' found:\n\t{line}") + split = line.split("=", 1) + key = split[0][len("deviceinfo_"):] + value = split[1].replace("\"", "").replace("\n", "") + ret[key] = value + + ret = _parse_kernel_suffix(ret, ret["codename"], kernel) + Deviceinfo.__validate(ret, path) + + for key, value in ret.items(): + # FIXME: something to turn on and fix in the future + # if key not in Deviceinfo.__annotations__.keys(): + # logging.warning(f"deviceinfo: {key} is not a known attribute") + setattr(self, key, value) diff --git a/pmb/qemu/run.py b/pmb/qemu/run.py index 2369d5ab..5ad91876 100644 --- a/pmb/qemu/run.py +++ b/pmb/qemu/run.py @@ -92,7 +92,7 @@ def command_qemu(args: PmbArgs, device: str, arch, img_path, img_path_2nd=None): """ Generate the full qemu command with arguments to run postmarketOS """ - cmdline = pmb.parse.deviceinfo()["kernel_cmdline"] + cmdline = pmb.parse.deviceinfo().kernel_cmdline if args.cmdline: cmdline = args.cmdline @@ -221,7 +221,7 @@ def command_qemu(args: PmbArgs, device: str, arch, img_path, img_path_2nd=None): "if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF.fd"] # Kernel Virtual Machine (KVM) support - native = pmb.config.arch_native == pmb.parse.deviceinfo()["arch"] + native = pmb.config.arch_native == pmb.parse.deviceinfo().arch if args.qemu_kvm and native and os.path.exists("/dev/kvm"): command += ["-enable-kvm"] command += ["-cpu", "host"] @@ -338,7 +338,7 @@ def run(args: PmbArgs): raise RuntimeError("'pmbootstrap qemu' can be only used with one of " "the QEMU device packages. Run 'pmbootstrap init' " "and select the 'qemu' vendor.") - arch = pmb.parse.arch.alpine_to_qemu(pmb.parse.deviceinfo()["arch"]) + arch = pmb.parse.arch.alpine_to_qemu(pmb.parse.deviceinfo().arch) img_path = system_image(device) img_path_2nd = None diff --git a/pmb/types.py b/pmb/types.py index dc2985db..dc1b14a4 100644 --- a/pmb/types.py +++ b/pmb/types.py @@ -57,7 +57,6 @@ class PmbArgs(Namespace): cross: bool details: bool details_to_stdout: bool - deviceinfo: Dict[str, str] deviceinfo_parse_kernel: str devices: str disk: Path