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 <caleb@postmarketos.org>
This commit is contained in:
Caleb Connolly 2024-06-06 15:05:59 +02:00 committed by Oliver Smith
parent b51d31acab
commit 97bd8b96ec
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
27 changed files with 372 additions and 288 deletions

View file

@ -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"

View file

@ -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

View file

@ -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"]

View file

@ -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()

View file

@ -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):

View file

@ -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!")

View file

@ -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,

View file

@ -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)

View file

@ -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"

View file

@ -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}"

View file

@ -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)

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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,

View file

@ -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"

View file

@ -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):

View file

@ -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)

View file

@ -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("")

View file

@ -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")

View file

@ -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))]

View file

@ -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

View file

@ -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 = {

View file

@ -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,47 +10,210 @@ import pmb.config
import pmb.helpers.other
import pmb.helpers.devices
# 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
deviceinfo variables. Related:
https://wiki.postmarketos.org/wiki/Device_specific_package#Multiple_kernels
def sanity_check(info, path):
:param info: deviceinfo dict, e.g.:
{"a": "first",
"b_mainline": "second",
"b_downstream": "third"}
:param device: which device info belongs to
:param kernel: which kernel suffix to remove (e.g. "mainline")
:returns: info, but with the configured kernel suffix removed, e.g:
{"a": "first",
"b": "second",
"b_downstream": "third"}
"""
# Do nothing if the configured kernel isn't available in the kernel (e.g.
# after switching from device with multiple kernels to device with only one
# kernel)
kernels = pmb.parse._apkbuild.kernels(device)
if not kernels or kernel not in kernels:
logging.verbose(f"parse_kernel_suffix: {kernel} not in {kernels}")
return info
ret = copy.copy(info)
suffix_kernel = kernel.replace("-", "_")
for key in Deviceinfo.__annotations__.keys():
key_kernel = f"{key}_{suffix_kernel}"
if key_kernel not in ret:
continue
# Move ret[key_kernel] to ret[key]
logging.verbose(f"parse_kernel_suffix: {key_kernel} => {key}")
ret[key] = ret[key_kernel]
del ret[key_kernel]
return ret
def deviceinfo(device=None, kernel=None) -> "Deviceinfo":
"""
:param device: defaults to args.device
:param kernel: defaults to args.kernel
"""
context = get_context()
if not device:
device = context.config.device
if not kernel:
kernel = context.config.kernel
if device in pmb.helpers.other.cache["deviceinfo"]:
return pmb.helpers.other.cache["deviceinfo"][device]
aports = context.config.aports
if not aports.exists():
logging.fatal(f"Aports directory is missing, expected: {aports}")
logging.fatal("Please provide a path to the aports directory using the"
" -p flag")
raise RuntimeError("Aports directory missing")
path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if not path:
raise RuntimeError(
"Device '" + device + "' not found. Run 'pmbootstrap init' to"
" start a new device port or to choose another device. It may have"
" been renamed, see <https://postmarketos.org/renamed>")
di = Deviceinfo(path, kernel)
pmb.helpers.other.cache["deviceinfo"][device] = di
return di
class Deviceinfo:
"""Variables from deviceinfo. Reference: <https://postmarketos.org/deviceinfo>
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
# 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 = os.path.realpath(path)
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"
" deviceinfo file: " + path)
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"
" deviceinfo file: " + path)
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: " +
path)
" 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: " +
path + " (if you are sure that you need this, then"
" '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. "
"Please delete it in: " + path)
f"Please delete it in: {path}")
if "dev_keyboard" in info:
raise RuntimeError("deviceinfo_dev_keyboard is unused. "
"Please delete it in: " + path)
f"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)
f"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:]
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}")
@ -80,76 +244,7 @@ def sanity_check(info, path):
f" and try again: {path}")
def _parse_kernel_suffix(info, device, kernel):
"""
Remove the kernel suffix (as selected in 'pmbootstrap init') from
deviceinfo variables. Related:
https://wiki.postmarketos.org/wiki/Device_specific_package#Multiple_kernels
:param info: deviceinfo dict, e.g.:
{"a": "first",
"b_mainline": "second",
"b_downstream": "third"}
:param device: which device info belongs to
:param kernel: which kernel suffix to remove (e.g. "mainline")
:returns: info, but with the configured kernel suffix removed, e.g:
{"a": "first",
"b": "second",
"b_downstream": "third"}
"""
# Do nothing if the configured kernel isn't available in the kernel (e.g.
# after switching from device with multiple kernels to device with only one
# kernel)
kernels = pmb.parse._apkbuild.kernels(device)
if not kernels or kernel not in kernels:
logging.verbose(f"parse_kernel_suffix: {kernel} not in {kernels}")
return info
ret = copy.copy(info)
suffix_kernel = kernel.replace("-", "_")
for key in pmb.config.deviceinfo_attributes:
key_kernel = f"{key}_{suffix_kernel}"
if key_kernel not in ret:
continue
# Move ret[key_kernel] to ret[key]
logging.verbose(f"parse_kernel_suffix: {key_kernel} => {key}")
ret[key] = ret[key_kernel]
del ret[key_kernel]
return ret
# FIXME (#2324): Make deviceinfo a type! (class!!!)
def deviceinfo(device=None, kernel=None) -> Dict[str, str]:
"""
:param device: defaults to args.device
:param kernel: defaults to args.kernel
"""
context = get_context()
if not device:
device = context.config.device
if not kernel:
kernel = context.config.kernel
if device in pmb.helpers.other.cache["deviceinfo"]:
return pmb.helpers.other.cache["deviceinfo"][device]
aports = context.config.aports
if not aports.exists():
logging.fatal(f"Aports directory is missing, expected: {aports}")
logging.fatal("Please provide a path to the aports directory using the"
" -p flag")
raise RuntimeError("Aports directory missing")
path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if not path:
raise RuntimeError(
"Device '" + device + "' not found. Run 'pmbootstrap init' to"
" start a new device port or to choose another device. It may have"
" been renamed, see <https://postmarketos.org/renamed>")
def __init__(self, path: Path, kernel: Optional[str] = None):
ret = {}
with open(path) as handle:
for line in handle:
@ -162,13 +257,11 @@ def deviceinfo(device=None, kernel=None) -> Dict[str, str]:
value = split[1].replace("\"", "").replace("\n", "")
ret[key] = value
# Assign empty string as default
for key in pmb.config.deviceinfo_attributes:
if key not in ret:
ret[key] = ""
ret = _parse_kernel_suffix(ret, ret["codename"], kernel)
Deviceinfo.__validate(ret, path)
ret = _parse_kernel_suffix(ret, device, kernel)
sanity_check(ret, path)
pmb.helpers.other.cache["deviceinfo"][device] = ret
return ret
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)

View file

@ -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

View file

@ -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