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 # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core import get_context from pmb.core import get_context
from pmb.types import PmbArgs from pmb.parse.deviceinfo import Deviceinfo
import pmb.helpers.run import pmb.helpers.run
import pmb.aportgen.core import pmb.aportgen.core
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.parse.arch import pmb.parse.arch
def generate_apkbuild(pkgname, deviceinfo, patches): def generate_apkbuild(pkgname, deviceinfo: Deviceinfo, patches):
device = "-".join(pkgname.split("-")[1:]) 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", makedepends = ["bash", "bc", "bison", "devicepkg-dev", "findutils", "flex",
"openssl-dev", "perl"] "openssl-dev", "perl"]
@ -24,13 +24,13 @@ def generate_apkbuild(pkgname, deviceinfo, patches):
downstreamkernel_package "$builddir" "$pkgdir" "$_carch\" \\ downstreamkernel_package "$builddir" "$pkgdir" "$_carch\" \\
"$_flavor" "$_outdir\"""" "$_flavor" "$_outdir\""""
if deviceinfo.get("header_version") == "2": if deviceinfo.header_version == "2":
package += """ package += """
make dtbs_install O="$_outdir" ARCH="$_carch" \\ make dtbs_install O="$_outdir" ARCH="$_carch" \\
INSTALL_DTBS_PATH="$pkgdir\"/boot/dtbs""" INSTALL_DTBS_PATH="$pkgdir\"/boot/dtbs"""
if deviceinfo["bootimg_qcdt"] == "true": if deviceinfo.bootimg_qcdt == "true":
build += """\n build += """\n
# Master DTB (deviceinfo_bootimg_qcdt)""" # Master DTB (deviceinfo_bootimg_qcdt)"""
vendors = ["spreadtrum", "exynos", "other"] vendors = ["spreadtrum", "exynos", "other"]
@ -68,8 +68,8 @@ def generate_apkbuild(pkgname, deviceinfo, patches):
pkgname={pkgname} pkgname={pkgname}
pkgver=3.x.x pkgver=3.x.x
pkgrel=0 pkgrel=0
pkgdesc="{deviceinfo["name"]} kernel fork" pkgdesc="{deviceinfo.name} kernel fork"
arch="{deviceinfo["arch"]}" arch="{deviceinfo.arch}"
_carch="{carch}" _carch="{carch}"
_flavor="{device}" _flavor="{device}"
url="https://kernel.org" url="https://kernel.org"

View file

@ -13,7 +13,7 @@ from pmb.core import Chroot, ChrootType, get_context
# FIXME (#2324): type hint Arch # 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 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 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 # Return its arch
device = pkgname.split("-", 1)[1] device = pkgname.split("-", 1)[1]
arch = pmb.parse.deviceinfo(device)["arch"] arch = pmb.parse.deviceinfo(device).arch
logging.verbose(pkgname + ": arch from deviceinfo: " + arch) logging.verbose(pkgname + ": arch from deviceinfo: " + arch)
return 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 Find a good default in case the user did not specify for which architecture
a package should be built. a package should be built.
@ -50,7 +50,7 @@ def arch(args: PmbArgs, pkgname: str):
aport = pmb.helpers.pmaports.find(pkgname) aport = pmb.helpers.pmaports.find(pkgname)
if not aport: if not aport:
raise FileNotFoundError(f"APKBUILD not found for {pkgname}") raise FileNotFoundError(f"APKBUILD not found for {pkgname}")
ret = arch_from_deviceinfo(args, pkgname, aport) ret = arch_from_deviceinfo(pkgname, aport)
if ret: if ret:
return ret return ret
@ -59,11 +59,11 @@ def arch(args: PmbArgs, pkgname: str):
deviceinfo = pmb.parse.deviceinfo() deviceinfo = pmb.parse.deviceinfo()
if get_context().config.build_default_device_arch: if get_context().config.build_default_device_arch:
preferred_arch = deviceinfo["arch"] preferred_arch = deviceinfo.arch
preferred_arch_2nd = pmb.config.arch_native preferred_arch_2nd = pmb.config.arch_native
else: else:
preferred_arch = pmb.config.arch_native 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: if "noarch" in arches or "all" in arches or preferred_arch in arches:
return preferred_arch return preferred_arch

View file

@ -190,7 +190,7 @@ def package_kernel(args: PmbArgs):
modify_apkbuild(pkgname, aport) modify_apkbuild(pkgname, aport)
apkbuild_path = context.config.work / "aportgen/APKBUILD" 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) apkbuild = pmb.parse.apkbuild(apkbuild_path, check_pkgname=False)
if apkbuild["_outdir"]: if apkbuild["_outdir"]:
kbuild_out = 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!") raise RuntimeError("Failed to parse 'cryptsetup status' output!")
def shutdown(args: PmbArgs, only_install_related=False): def shutdown(only_install_related=False):
# Stop daemons # Stop daemons
kill_adb() kill_adb()
kill_sccache() kill_sccache()

View file

@ -4,17 +4,18 @@ import glob
from pmb.helpers import logging from pmb.helpers import logging
import os import os
import pmb.build.other
import pmb.config.workdir
import pmb.chroot import pmb.chroot
import pmb.config.pmaports import pmb.config.pmaports
import pmb.config.workdir import pmb.config.workdir
from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
from pmb.core import Chroot, get_context 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, pkgs_local_mismatch=False, pkgs_online_mismatch=False, distfiles=False,
rust=False, netboot=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, # Delete packages with a different version compared to aports,
# then re-index # then re-index
if pkgs_local_mismatch: if pkgs_local_mismatch:
zap_pkgs_local_mismatch(args, confirm, dry) zap_pkgs_local_mismatch(confirm, dry)
# Delete outdated binary packages # Delete outdated binary packages
if pkgs_online_mismatch: 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 # Deletion patterns for folders inside get_context().config.work
patterns = list(Chroot.iter_patterns()) 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]) pmb.helpers.run.root(["rm", "-rf", match])
# Remove config init dates for deleted chroots # 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 # Chroots were zapped, so no repo lists exist anymore
pmb.helpers.other.cache["apk_repository_list_updated"].clear() 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") 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"] channel = pmb.config.pmaports.read_config()["channel"]
if not os.path.exists(f"{get_context().config.work}/packages/{channel}"): if not os.path.exists(f"{get_context().config.work}/packages/{channel}"):
return return
@ -131,10 +132,10 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
reindex = True reindex = True
if reindex: 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 # Check whether we need to do anything
paths = glob.glob(f"{get_context().config.work}/cache_apk_*") paths = glob.glob(f"{get_context().config.work}/cache_apk_*")
if not len(paths): if not len(paths):

View file

@ -745,6 +745,6 @@ def frontend(args: PmbArgs):
setattr(args, "deviceinfo", info) setattr(args, "deviceinfo", info)
# Do not zap any existing packages or cache_http directories # Do not zap any existing packages or cache_http directories
pmb.chroot.zap(args, confirm=False) pmb.chroot.zap(confirm=False)
logging.info("DONE!") logging.info("DONE!")

View file

@ -90,7 +90,7 @@ def chroot_check_channel(chroot: Chroot):
f" '{channel}' channel now. {msg_again}") f" '{channel}' channel now. {msg_again}")
def clean(args: PmbArgs): def clean():
"""Remove obsolete data data from workdir.cfg. """Remove obsolete data data from workdir.cfg.
:returns: None if workdir does not exist, :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}") logging.info(f"Export symlinks to: {target}")
if args.odin_flashable_tar: if args.odin_flashable_tar:
pmb.export.odin(context, flavor, target) 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' for devices configured with the flasher method 'heimdall-isorec'
and with boot.img for devices with 'heimdall-bootimg' 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) suffix = Chroot(ChrootType.ROOTFS, context.device)
deviceinfo = pmb.parse.deviceinfo(context.device) deviceinfo = pmb.parse.deviceinfo(context.device)
@ -29,7 +29,7 @@ def odin(context: Context, flavor, folder: Path):
suffix_flavor = "" suffix_flavor = ""
# Validate method # Validate method
method = deviceinfo["flash_method"] method = deviceinfo.flash_method or ""
if not method.startswith("heimdall-"): if not method.startswith("heimdall-"):
raise RuntimeError("An odin flashable tar is not supported" raise RuntimeError("An odin flashable tar is not supported"
f" for the flash method '{method}' specified" f" for the flash method '{method}' specified"
@ -37,8 +37,8 @@ def odin(context: Context, flavor, folder: Path):
" Only 'heimdall' methods are supported.") " Only 'heimdall' methods are supported.")
# Partitions # Partitions
partition_kernel = deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL" partition_kernel = deviceinfo.flash_heimdall_partition_kernel or "KERNEL"
partition_initfs = deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY" partition_initfs = deviceinfo.flash_heimdall_partition_initfs or "RECOVERY"
# Temporary folder # Temporary folder
temp_folder = "/tmp/odin-flashable-tar" temp_folder = "/tmp/odin-flashable-tar"

View file

@ -9,19 +9,18 @@ import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.helpers.file import pmb.helpers.file
from pmb.core import Chroot, ChrootType 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. Create convenience symlinks to the rootfs and boot files.
""" """
context = get_context() context = get_context()
arch = pmb.parse.deviceinfo(context.device)["arch"] arch = pmb.parse.deviceinfo(context.device).arch
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
suffix = f"-{flavor}" suffix = f"-{flavor}"

View file

@ -1,10 +1,10 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Dict
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
import pmb.config import pmb.config
from pmb.parse.deviceinfo import Deviceinfo
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.install import pmb.install
@ -17,11 +17,11 @@ import pmb.parse.kconfig
from pmb.core import Chroot, ChrootType 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) # 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: if autoinstall:
pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, device)) pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, deviceinfo.codename))
# Check kernel config # Check kernel config
pmb.parse.kconfig.check(flavor, must_exist=False) 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 # Generate the paths and run the flasher
if boot: if boot:
logging.info("(native) boot " + flavor + " kernel") logging.info("(native) boot " + flavor + " kernel")
pmb.flasher.run(device, deviceinfo, "boot", flavor) pmb.flasher.run(deviceinfo, method, "boot", flavor)
else: else:
logging.info("(native) flash kernel " + flavor) 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 " logging.info("You will get an IP automatically assigned to your "
"USB interface shortly.") "USB interface shortly.")
logging.info("Then you can connect to your device using ssh after pmOS has" 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)) 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 # Generate rootfs, install flasher
suffix = ".img" suffix = ".img"
if pmb.config.flashers.get(method, {}).get("split", False): if pmb.config.flashers.get(method, {}).get("split", False):
suffix = "-root.img" 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(): if not img_path.exists():
raise RuntimeError("The rootfs has not been generated yet, please run" raise RuntimeError("The rootfs has not been generated yet, please run"
" 'pmbootstrap install' first.") " 'pmbootstrap install' first.")
# Do not flash if using fastboot & image is too large # Do not flash if using fastboot & image is too large
if method.startswith("fastboot") \ 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 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: if img_size > max_size:
raise RuntimeError("The rootfs is too large for fastboot to" raise RuntimeError("The rootfs is too large for fastboot to"
" flash.") " flash.")
# Run the flasher # Run the flasher
logging.info("(native) flash rootfs image") 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): def list_devices(deviceinfo: Deviceinfo, method: str):
pmb.flasher.run(device, deviceinfo, "list_devices") pmb.flasher.run(deviceinfo, method, "list_devices")
def sideload(device: str, deviceinfo: Dict[str, str]): def sideload(deviceinfo: Deviceinfo, method: str):
# Install depends # Install depends
pmb.flasher.install_depends() pmb.flasher.install_depends(method)
# Mount the buildroot # Mount the buildroot
chroot = Chroot.buildroot(deviceinfo["arch"]) chroot = Chroot.buildroot(deviceinfo.arch)
mountpoint = "/mnt/" / chroot mountpoint = "/mnt/" / chroot
pmb.helpers.mount.bind(chroot.path, pmb.helpers.mount.bind(chroot.path,
Chroot.native().path / mountpoint) Chroot.native().path / mountpoint)
# Missing recovery zip error # Missing recovery zip error
if not (Chroot.native() / mountpoint / "/var/lib/postmarketos-android-recovery-installer" 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," raise RuntimeError("The recovery zip has not been generated yet,"
" please run 'pmbootstrap install' with the" " please run 'pmbootstrap install' with the"
" '--android-recovery-zip' parameter first!") " '--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": if method == "fastboot":
# In the future this could be expanded to use "fastboot flash lk2nd $img" # 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 # which reflashes/updates lk2nd from itself. For now let the user handle this
# manually since supporting the codepath with heimdall requires more effort. # 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") logging.info("(native) checking current fastboot product")
output = pmb.chroot.root(["fastboot", "getvar", "product"], output = pmb.chroot.root(["fastboot", "getvar", "product"],
output="interactive", output_return=True) 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.") " bootloader mode to re-flash lk2nd.")
# Get the lk2nd package (which is a dependency of the device package) # 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) apkbuild = pmb.helpers.pmaports.get(device_pkg)
lk2nd_pkg = None lk2nd_pkg = None
for dep in apkbuild["depends"]: for dep in apkbuild["depends"]:
@ -125,11 +125,11 @@ def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str):
if not lk2nd_pkg: if not lk2nd_pkg:
raise RuntimeError(f"{device_pkg} does not depend on any lk2nd package") 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) pmb.chroot.apk.install([lk2nd_pkg], suffix)
logging.info("(native) flash lk2nd image") logging.info("(native) flash lk2nd image")
pmb.flasher.run(device, deviceinfo, "flash_lk2nd") pmb.flasher.run(deviceinfo, method, "flash_lk2nd")
def frontend(args: PmbArgs): def frontend(args: PmbArgs):
@ -137,7 +137,7 @@ def frontend(args: PmbArgs):
action = args.action_flasher action = args.action_flasher
device = context.device device = context.device
deviceinfo = pmb.parse.deviceinfo() 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", if method == "none" and action in ["boot", "flash_kernel", "flash_rootfs",
"flash_lk2nd"]: "flash_lk2nd"]:
@ -145,20 +145,20 @@ def frontend(args: PmbArgs):
return return
if action in ["boot", "flash_kernel"]: if action in ["boot", "flash_kernel"]:
kernel(device, deviceinfo) kernel(deviceinfo, method)
elif action == "flash_rootfs": elif action == "flash_rootfs":
rootfs(device, deviceinfo, method) rootfs(deviceinfo, method)
elif action == "flash_vbmeta": elif action == "flash_vbmeta":
logging.info("(native) flash vbmeta.img with verity disabled flag") 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": elif action == "flash_dtbo":
logging.info("(native) flash dtbo image") logging.info("(native) flash dtbo image")
pmb.flasher.run(device, deviceinfo, "flash_dtbo") pmb.flasher.run(deviceinfo, method, "flash_dtbo")
elif action == "flash_lk2nd": elif action == "flash_lk2nd":
flash_lk2nd(device, deviceinfo, method) flash_lk2nd(deviceinfo, method)
elif action == "list_flavors": elif action == "list_flavors":
list_flavors(device) list_flavors(device)
elif action == "list_devices": elif action == "list_devices":
list_devices(device, deviceinfo) pmb.flasher.run(deviceinfo, method, "list_devices")
elif action == "sideload": 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 from pmb.core import Chroot, ChrootType
def install_depends() -> None: def install_depends(method: str) -> 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"]
if method not in pmb.config.flashers: if method not in pmb.config.flashers:
raise RuntimeError(f"Flash method {method} is not supported by the" raise RuntimeError(f"Flash method {method} is not supported by the"
" current configuration. However, adding a new" " current configuration. However, adding a new"
@ -46,8 +39,8 @@ def install_depends() -> None:
pmb.chroot.apk.install(depends, Chroot.native()) pmb.chroot.apk.install(depends, Chroot.native())
def init(device: str): def init(device: str, method: str):
install_depends() install_depends(method)
# Mount folders from host system # Mount folders from host system
for folder in pmb.config.flash_mount_bind: for folder in pmb.config.flash_mount_bind:

View file

@ -1,31 +1,28 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Dict from typing import Dict
from pmb.parse.deviceinfo import Deviceinfo
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.chroot.initfs import pmb.chroot.initfs
import pmb.helpers.args 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_"): if not key.startswith("$PARTITION_"):
return return
name = deviceinfo["name"] name = deviceinfo.name
if value in deviceinfo["partition_blacklist"].split(","): if value in (deviceinfo.partition_blacklist or "").split(","):
raise RuntimeError("'" + value + "'" + " partition is blacklisted " + raise RuntimeError("'" + value + "'" + " partition is blacklisted " +
"from being flashed! See the " + name + " device " + "from being flashed! See the " + name + " device " +
"wiki page for more information.") "wiki page for more information.")
def run(device: str, deviceinfo: Dict[str, str], action, flavor=None): def run(deviceinfo: Deviceinfo, method: str, action: str, flavor=None):
pmb.flasher.init(device) pmb.flasher.init(deviceinfo.codename, method)
# FIXME: handle argparsing and pass in only the args we need.
args = pmb.helpers.args.please_i_really_need_args()
# Verify action # Verify action
method = args.flash_method or deviceinfo["flash_method"]
cfg = pmb.config.flashers[method] cfg = pmb.config.flashers[method]
if not isinstance(cfg["actions"], dict): if not isinstance(cfg["actions"], dict):
raise TypeError(f"Flashers misconfigured! {method} key 'actions' should be a dictionary") 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>") "Deviceinfo_flash_methods>")
# Variable setup # 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) fvars = pmb.flasher.variables(args, flavor, method)
# vbmeta flasher requires vbmeta partition to be explicitly specified # 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" " but the value for this variable"
" is None! Is that missing in your" " is None! Is that missing in your"
" deviceinfo?") " deviceinfo?")
check_partition_blacklist(args, deviceinfo, key, value) check_partition_blacklist(deviceinfo, key, value)
command[i] = command[i].replace(key, value) command[i] = command[i].replace(key, value)
# Remove empty strings # Remove empty strings

View file

@ -9,11 +9,11 @@ from pmb.types import PmbArgs
def variables(args: PmbArgs, flavor, method): def variables(args: PmbArgs, flavor, method):
device = get_context().config.device device = get_context().config.device
deviceinfo = pmb.parse.deviceinfo() deviceinfo = pmb.parse.deviceinfo()
_cmdline = deviceinfo["kernel_cmdline"] or "" _cmdline = deviceinfo.kernel_cmdline or ""
if "cmdline" in args and args.cmdline: if "cmdline" in args and args.cmdline:
_cmdline = 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 # TODO Remove _partition_system deviceinfo support once pmaports has been
# updated and minimum pmbootstrap version bumped. # updated and minimum pmbootstrap version bumped.
@ -23,39 +23,39 @@ def variables(args: PmbArgs, flavor, method):
_partition_rootfs: Optional[str] _partition_rootfs: Optional[str]
if method.startswith("fastboot"): if method.startswith("fastboot"):
_partition_kernel = deviceinfo["flash_fastboot_partition_kernel"]\ _partition_kernel = deviceinfo.flash_fastboot_partition_kernel\
or "boot" or "boot"
_partition_rootfs = deviceinfo["flash_fastboot_partition_rootfs"]\ _partition_rootfs = deviceinfo.flash_fastboot_partition_rootfs\
or deviceinfo["flash_fastboot_partition_system"] or "userdata" or deviceinfo.flash_fastboot_partition_system or "userdata"
_partition_vbmeta = deviceinfo["flash_fastboot_partition_vbmeta"]\ _partition_vbmeta = deviceinfo.flash_fastboot_partition_vbmeta\
or None or None
_partition_dtbo = deviceinfo["flash_fastboot_partition_dtbo"]\ _partition_dtbo = deviceinfo.flash_fastboot_partition_dtbo\
or None or None
# Require that the partitions are specified in deviceinfo for now # Require that the partitions are specified in deviceinfo for now
elif method.startswith("rkdeveloptool"): elif method.startswith("rkdeveloptool"):
_partition_kernel = deviceinfo["flash_rk_partition_kernel"]\ _partition_kernel = deviceinfo.flash_rk_partition_kernel\
or None or None
_partition_rootfs = deviceinfo["flash_rk_partition_rootfs"]\ _partition_rootfs = deviceinfo.flash_rk_partition_rootfs\
or deviceinfo["flash_rk_partition_system"] or None or deviceinfo.flash_rk_partition_system or None
_partition_vbmeta = None _partition_vbmeta = None
_partition_dtbo = None _partition_dtbo = None
elif method.startswith("mtkclient"): elif method.startswith("mtkclient"):
_partition_kernel = deviceinfo["flash_mtkclient_partition_kernel"]\ _partition_kernel = deviceinfo.flash_mtkclient_partition_kernel\
or "boot" or "boot"
_partition_rootfs = deviceinfo["flash_mtkclient_partition_rootfs"]\ _partition_rootfs = deviceinfo.flash_mtkclient_partition_rootfs\
or "userdata" or "userdata"
_partition_vbmeta = deviceinfo["flash_mtkclient_partition_vbmeta"]\ _partition_vbmeta = deviceinfo.flash_mtkclient_partition_vbmeta\
or None or None
_partition_dtbo = deviceinfo["flash_mtkclient_partition_dtbo"]\ _partition_dtbo = deviceinfo.flash_mtkclient_partition_dtbo\
or None or None
else: else:
_partition_kernel = deviceinfo["flash_heimdall_partition_kernel"]\ _partition_kernel = deviceinfo.flash_heimdall_partition_kernel\
or "KERNEL" or "KERNEL"
_partition_rootfs = deviceinfo["flash_heimdall_partition_rootfs"]\ _partition_rootfs = deviceinfo.flash_heimdall_partition_rootfs\
or deviceinfo["flash_heimdall_partition_system"] or "SYSTEM" or deviceinfo.flash_heimdall_partition_system or "SYSTEM"
_partition_vbmeta = deviceinfo["flash_heimdall_partition_vbmeta"]\ _partition_vbmeta = deviceinfo.flash_heimdall_partition_vbmeta\
or None or None
_partition_dtbo = deviceinfo["flash_heimdall_partition_dtbo"]\ _partition_dtbo = deviceinfo.flash_heimdall_partition_dtbo\
or None or None
if "partition" in args and args.partition: if "partition" in args and args.partition:
@ -67,7 +67,7 @@ def variables(args: PmbArgs, flavor, method):
_partition_dtbo = args.partition _partition_dtbo = args.partition
_dtb = "" _dtb = ""
if deviceinfo["append_dtb"] == "true": if deviceinfo.append_dtb == "true":
_dtb = "-dtb" _dtb = "-dtb"
_no_reboot = "" _no_reboot = ""
@ -86,16 +86,15 @@ def variables(args: PmbArgs, flavor, method):
"$IMAGE": "/home/pmos/rootfs/" + device + ".img", "$IMAGE": "/home/pmos/rootfs/" + device + ".img",
"$KERNEL_CMDLINE": _cmdline, "$KERNEL_CMDLINE": _cmdline,
"$PARTITION_KERNEL": _partition_kernel, "$PARTITION_KERNEL": _partition_kernel,
"$PARTITION_INITFS": deviceinfo[ "$PARTITION_INITFS": deviceinfo.flash_heimdall_partition_initfs or "RECOVERY",
"flash_heimdall_partition_initfs"] or "RECOVERY",
"$PARTITION_ROOTFS": _partition_rootfs, "$PARTITION_ROOTFS": _partition_rootfs,
"$PARTITION_VBMETA": _partition_vbmeta, "$PARTITION_VBMETA": _partition_vbmeta,
"$PARTITION_DTBO": _partition_dtbo, "$PARTITION_DTBO": _partition_dtbo,
"$FLASH_PAGESIZE": flash_pagesize, "$FLASH_PAGESIZE": flash_pagesize,
"$RECOVERY_ZIP": "/mnt/buildroot_" + deviceinfo["arch"] + "$RECOVERY_ZIP": "/mnt/buildroot_" + deviceinfo.arch +
"/var/lib/postmarketos-android-recovery-installer" "/var/lib/postmarketos-android-recovery-installer"
"/pmos-" + device + ".zip", "/pmos-" + device + ".zip",
"$UUU_SCRIPT": "/mnt/rootfs_" + deviceinfo["codename"] + "$UUU_SCRIPT": "/mnt/rootfs_" + deviceinfo.codename +
"/usr/share/uuu/flash_script.lst", "/usr/share/uuu/flash_script.lst",
"$NO_REBOOT": _no_reboot, "$NO_REBOOT": _no_reboot,
"$RESUME": _resume "$RESUME": _resume

View file

@ -99,7 +99,7 @@ def init(args: PmbArgs) -> PmbArgs:
pmb.config.pmaports.read_config() pmb.config.pmaports.read_config()
pmb.helpers.git.parse_channels_cfg(config.aports) pmb.helpers.git.parse_channels_cfg(config.aports)
deviceinfo = pmb.parse.deviceinfo() 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 # Remove attributes from args so they don't get used by mistake
delattr(args, "timeout") delattr(args, "timeout")

View file

@ -65,7 +65,7 @@ def _parse_suffix(args: PmbArgs) -> Chroot:
return Chroot(ChrootType.ROOTFS, get_context().config.device) return Chroot(ChrootType.ROOTFS, get_context().config.device)
elif args.buildroot: elif args.buildroot:
if args.buildroot == "device": if args.buildroot == "device":
return Chroot.buildroot(pmb.parse.deviceinfo()["arch"]) return Chroot.buildroot(pmb.parse.deviceinfo().arch)
else: else:
return Chroot.buildroot(args.buildroot) return Chroot.buildroot(args.buildroot)
elif args.suffix: elif args.suffix:
@ -118,14 +118,14 @@ def build(args: PmbArgs):
# Ensure repo_bootstrap is done for all arches we intend to build for # Ensure repo_bootstrap is done for all arches we intend to build for
for package in args.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)
pmb.helpers.repo_bootstrap.require_bootstrap(arch_package, pmb.helpers.repo_bootstrap.require_bootstrap(arch_package,
f"build {package} for {arch_package}") f"build {package} for {arch_package}")
context = get_context() context = get_context()
# Build all packages # Build all packages
for package in args.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, if not pmb.build.package(context, package, arch_package, force,
args.strict, src=src): args.strict, src=src):
logging.info("NOTE: Package '" + package + "' is up to date. Use" 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)) logging.info("NOTE: Valid config keys: " + ", ".join(keys))
raise RuntimeError("Invalid config key: " + args.name) 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.reset:
if args.name is None: if args.name is None:
raise RuntimeError("config --reset requires a name to be given.") raise RuntimeError("config --reset requires a name to be given.")
@ -242,7 +244,7 @@ def config(args: PmbArgs):
def repo_bootstrap(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): def repo_missing(args: PmbArgs):
@ -272,8 +274,8 @@ def install(args: PmbArgs):
raise ValueError("Installation using rsync" raise ValueError("Installation using rsync"
" is not currently supported on btrfs filesystem.") " is not currently supported on btrfs filesystem.")
pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo["arch"], pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo.arch,
f"do 'pmbootstrap install' for {deviceinfo['arch']}" f"do 'pmbootstrap install' for {deviceinfo.arch}"
" (deviceinfo_arch)") " (deviceinfo_arch)")
# On-device installer checks # On-device installer checks
@ -297,7 +299,7 @@ def install(args: PmbArgs):
raise ValueError("--on-device-installer cannot be combined with" raise ValueError("--on-device-installer cannot be combined with"
" --filesystem") " --filesystem")
if deviceinfo["cgpt_kpart"]: if deviceinfo.cgpt_kpart:
raise ValueError("--on-device-installer cannot be used with" raise ValueError("--on-device-installer cannot be used with"
" ChromeOS devices") " ChromeOS devices")
else: else:
@ -324,7 +326,7 @@ def install(args: PmbArgs):
if not args.disk and args.split is None: if not args.disk and args.split is None:
# Default to split if the flash method requires it # 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): if flasher.get("split", False):
args.split = True args.split = True
@ -567,7 +569,7 @@ def work_migrate(args: PmbArgs):
def zap(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, distfiles=args.distfiles, pkgs_local=args.pkgs_local,
pkgs_local_mismatch=args.pkgs_local_mismatch, pkgs_local_mismatch=args.pkgs_local_mismatch,
pkgs_online_mismatch=args.pkgs_online_mismatch, pkgs_online_mismatch=args.pkgs_online_mismatch,

View file

@ -107,7 +107,7 @@ def migrate_work_folder(args: PmbArgs):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
# Zap and update abuild.conf # Zap and update abuild.conf
pmb.chroot.zap(args, False) pmb.chroot.zap(False)
conf = context.config.work / "config_abuild/abuild.conf" conf = context.config.work / "config_abuild/abuild.conf"
if os.path.exists(conf): if os.path.exists(conf):
pmb.helpers.run.root(["sed", "-i", pmb.helpers.run.root(["sed", "-i",
@ -145,7 +145,7 @@ def migrate_work_folder(args: PmbArgs):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
# Zap chroots # Zap chroots
pmb.chroot.zap(args, False) pmb.chroot.zap(False)
# Update version file # Update version file
migrate_success(context.config.work, 3) migrate_success(context.config.work, 3)
@ -186,7 +186,7 @@ def migrate_work_folder(args: PmbArgs):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
# Zap chroots # Zap chroots
pmb.chroot.zap(args, False) pmb.chroot.zap(False)
# Move packages to edge subdir # Move packages to edge subdir
edge_path = context.config.work / "packages/edge" 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 # Zap chroots to avoid potential "ERROR: Chroot 'native' was created
# for the 'stable' channel, but you are on the 'v20.05' channel now." # for the 'stable' channel, but you are on the 'v20.05' channel now."
pmb.chroot.zap(args, False) pmb.chroot.zap(False)
# Migrate # Migrate
packages_dir = f"{context.config.work}/packages" packages_dir = f"{context.config.work}/packages"

View file

@ -1,12 +1,13 @@
# Copyright 2024 Oliver Smith # Copyright 2024 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Optional
from pmb.core.chroot import Chroot from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
import glob import glob
import pmb.config.pmaports import pmb.config.pmaports
import pmb.helpers.repo import pmb.helpers.repo
from pmb.types import PmbArgs from pmb.types import Config
from pmb.core import get_context from pmb.core import get_context
@ -15,19 +16,15 @@ progress_total = 0
progress_step: str progress_step: str
def get_arch(args: PmbArgs): def get_arch(config: Config):
if args.arch: if config.build_default_device_arch:
return args.arch return pmb.parse.deviceinfo().arch
if args.build_default_device_arch:
return pmb.parse.deviceinfo()["arch"]
return pmb.config.arch_native return pmb.config.arch_native
def check_repo_arg(args: PmbArgs): def check_repo_arg(repo: str):
cfg = pmb.config.pmaports.read_config_repos() cfg = pmb.config.pmaports.read_config_repos()
repo = args.repository
if repo in cfg: if repo in cfg:
return return
@ -41,9 +38,9 @@ def check_repo_arg(args: PmbArgs):
" current branch") " current branch")
def check_existing_pkgs(args: PmbArgs, arch): def check_existing_pkgs(config: Config, arch):
channel = pmb.config.pmaports.read_config()["channel"] 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}/*"): if glob.glob(f"{path}/*"):
logging.info(f"Packages path: {path}") logging.info(f"Packages path: {path}")
@ -57,12 +54,12 @@ def check_existing_pkgs(args: PmbArgs, arch):
raise RuntimeError(f"{msg}!") raise RuntimeError(f"{msg}!")
def get_steps(args: PmbArgs): def get_steps(repo: str):
cfg = pmb.config.pmaports.read_config_repos() cfg = pmb.config.pmaports.read_config_repos()
prev_step = 0 prev_step = 0
ret = {} ret = {}
for key, packages in cfg[args.repository].items(): for key, packages in cfg[repo].items():
if not key.startswith("bootstrap_"): if not key.startswith("bootstrap_"):
continue continue
@ -76,7 +73,7 @@ def get_steps(args: PmbArgs):
return ret return ret
def get_suffix(args: PmbArgs, arch): def get_suffix(arch):
if pmb.parse.arch.cpu_emulation_required(arch): if pmb.parse.arch.cpu_emulation_required(arch):
return f"buildroot_{arch}" return f"buildroot_{arch}"
return "native" return "native"
@ -91,7 +88,7 @@ def get_packages(bootstrap_line):
return ret return ret
def set_progress_total(args: PmbArgs, steps, arch): def set_progress_total(steps, arch):
global progress_total global progress_total
progress_total = 0 progress_total = 0
@ -117,14 +114,14 @@ def log_progress(msg):
progress_done += 1 progress_done += 1
def run_steps(args: PmbArgs, steps, arch, chroot: Chroot): def run_steps(steps, arch, chroot: Chroot):
global progress_step global progress_step
for step, bootstrap_line in steps.items(): for step, bootstrap_line in steps.items():
progress_step = step.replace("bootstrap_", "BOOTSTRAP=") progress_step = step.replace("bootstrap_", "BOOTSTRAP=")
log_progress("zapping") log_progress("zapping")
pmb.chroot.zap(args, confirm=False) pmb.chroot.zap(confirm=False)
usr_merge = pmb.chroot.UsrMerge.OFF usr_merge = pmb.chroot.UsrMerge.OFF
if "[usr_merge]" in bootstrap_line: if "[usr_merge]" in bootstrap_line:
@ -148,17 +145,18 @@ def run_steps(args: PmbArgs, steps, arch, chroot: Chroot):
log_progress("bootstrap complete!") log_progress("bootstrap complete!")
def main(args: PmbArgs): def main(arch: Optional[str], repository: str): # noqa: F821
check_repo_arg(args) config = get_context().config
check_repo_arg(repository)
arch = get_arch(args) arch = arch or get_arch(config)
check_existing_pkgs(args, arch) check_existing_pkgs(config, arch)
steps = get_steps(args) steps = get_steps(repository)
suffix = get_suffix(args, arch) suffix = get_suffix(arch)
set_progress_total(args, steps, arch) set_progress_total(steps, arch)
run_steps(args, steps, arch, suffix) run_steps(steps, arch, suffix)
def require_bootstrap_error(repo, arch, trigger_str): 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): if pmb.parse._apkbuild.kernels(config.device):
kernel = f", kernel: {config.kernel}" 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) print_status_line("Device", value)

View file

@ -16,6 +16,7 @@ import pmb.chroot.other
import pmb.chroot.initfs import pmb.chroot.initfs
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.parse.deviceinfo import Deviceinfo
from pmb.types import Config, PartitionLayout, PmbArgs from pmb.types import Config, PartitionLayout, PmbArgs
import pmb.helpers.devices import pmb.helpers.devices
from pmb.helpers.mount import mount_device_rootfs 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 mountpoint_outside = Chroot.native() / mountpoint
# Remove empty qemu-user binary stub (where the binary was bind-mounted) # 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") qemu_binary = mountpoint_outside / ("/usr/bin/qemu-" + arch_qemu + "-static")
if os.path.exists(qemu_binary): if os.path.exists(qemu_binary):
pmb.helpers.run.root(["rm", 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/"]) pmb.helpers.run.root(["cp", key, rootfs / "etc/apk/keys/"])
# Copy over the corresponding APKINDEX files from cache # 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) user_repository=False)
for f in index_files: for f in index_files:
pmb.helpers.run.root(["cp", f, rootfs / "var/cache/apk/"]) 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 Set the keymap with the setup-keymap utility if the device requires it
""" """
chroot = Chroot(ChrootType.ROOTFS, config.device) chroot = Chroot(ChrootType.ROOTFS, config.device)
info = pmb.parse.deviceinfo(device=config.device) deviceinfo = pmb.parse.deviceinfo(device=config.device)
if "keymaps" not in info or info["keymaps"].strip() == "": if not deviceinfo.keymaps or deviceinfo.keymaps.strip() == "":
logging.info("NOTE: No valid keymap specified for device") logging.info("NOTE: No valid keymap specified for device")
return return
options = info["keymaps"].split(' ') options = deviceinfo.keymaps.split(' ')
if (config.keymap != "" and if (config.keymap != "" and
config.keymap is not None and config.keymap is not None and
config.keymap in options): config.keymap in options):
@ -525,7 +526,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step):
""" """
binary_ranges: Dict[int, int] = {} binary_ranges: Dict[int, int] = {}
binary_list = [] binary_list = []
binaries = pmb.parse.deviceinfo()["sd_embed_firmware"].split(",") binaries = pmb.parse.deviceinfo().sd_embed_firmware.split(",")
for binary_offset in binaries: for binary_offset in binaries:
binary, _offset = binary_offset.split(':') binary, _offset = binary_offset.split(':')
@ -541,7 +542,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step):
f"/usr/share/{binary}") f"/usr/share/{binary}")
# Insure that embedding the firmware will not overrun the # Insure that embedding the firmware will not overrun the
# first partition # 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) max_size = (int(boot_part_start) * 512) - (offset * step)
binary_size = os.path.getsize(binary_path) binary_size = os.path.getsize(binary_path)
if binary_size > max_size: 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 :param suffix: of the chroot, which holds the firmware files (either the
rootfs_{args.device} or installer_{args.device} rootfs_{args.device} or installer_{args.device}
""" """
if not pmb.parse.deviceinfo()["sd_embed_firmware"]: if not pmb.parse.deviceinfo().sd_embed_firmware:
return return
step = 1024 step = 1024
if pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]: if pmb.parse.deviceinfo().sd_embed_firmware_step_size:
try: try:
step = int(pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]) step = int(pmb.parse.deviceinfo().sd_embed_firmware_step_size)
except ValueError: except ValueError:
raise RuntimeError("Value for " raise RuntimeError("Value for "
"deviceinfo_sd_embed_firmware_step_size " "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 layout: partition layout from get_partition_layout()
:param suffix: of the chroot, which holds the image file to be flashed :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 return
device_rootfs = mount_device_rootfs(suffix) device_rootfs = mount_device_rootfs(suffix)
@ -667,7 +668,7 @@ def sanity_check_disk_size(args: PmbArgs):
def get_ondev_pkgver(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) package = pmb.helpers.package.get(args, "postmarketos-ondev", arch)
return package["version"].split("-r")[0] 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)}" else f"UUID={get_uuid(args, root_dev)}"
boot_options = "nodev,nosuid,noexec" 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"): if boot_filesystem in ("fat16", "fat32"):
boot_filesystem = "vfat" boot_filesystem = "vfat"
boot_options += ",umask=0077,nosymfollow,codepage=437,iocharset=ascii" 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() device = chroot.name()
# Partition and fill image file/disk block device # Partition and fill image file/disk block device
logging.info(f"*** ({step}/{steps}) PREPARE INSTALL BLOCKDEVICE ***") 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) (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) and args.install_cgpt)
if not args.rsync: if not args.rsync:
pmb.install.blockdevice.create(args, size_boot, size_root, pmb.install.blockdevice.create(args, size_boot, size_root,
size_reserve, split, disk) size_reserve, split, disk)
if not split: 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( pmb.install.partition_cgpt(
args, layout, size_boot, size_reserve) args, layout, size_boot, size_reserve)
else: else:
@ -862,12 +863,12 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
if disk: if disk:
logging.info(f"Unmounting disk {disk} (this may take a while " logging.info(f"Unmounting disk {disk} (this may take a while "
"to sync, please wait)") "to sync, please wait)")
pmb.chroot.shutdown(args, True) pmb.chroot.shutdown(True)
# Convert rootfs to sparse using img2simg # Convert rootfs to sparse using img2simg
sparse = args.sparse sparse = args.sparse
if sparse is None: 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: if sparse and not split and not disk:
workdir = Path("/home/pmos/rootfs") workdir = Path("/home/pmos/rootfs")
@ -881,7 +882,7 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
working_dir=workdir) working_dir=workdir)
# patch sparse image for Samsung devices if specified # 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: if samsungify_strategy:
logging.info("(native) convert sparse image into Samsung's sparse image format") logging.info("(native) convert sparse image into Samsung's sparse image format")
pmb.chroot.apk.install(["sm-sparse-image-tool"], Chroot.native()) 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) 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 """ Print flashing information, based on the deviceinfo data and the
pmbootstrap arguments. """ pmbootstrap arguments. """
logging.info("") # make the note stand out logging.info("") # make the note stand out
logging.info("*** FLASHING INFORMATION ***") logging.info("*** FLASHING INFORMATION ***")
# System flash information # System flash information
method = deviceinfo["flash_method"] method = deviceinfo.flash_method
flasher = pmb.config.flashers.get(method, {}) flasher = pmb.config.flashers.get(method, {})
flasher_actions = flasher.get("actions", {}) flasher_actions = flasher.get("actions", {})
if not isinstance(flasher_actions, dict): 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 # if current flasher supports vbmeta and partition is explicitly specified
# in deviceinfo # in deviceinfo
if "flash_vbmeta" in flasher_actions and \ if "flash_vbmeta" in flasher_actions and \
(deviceinfo["flash_fastboot_partition_vbmeta"] or (deviceinfo.flash_fastboot_partition_vbmeta or
deviceinfo["flash_heimdall_partition_vbmeta"]): deviceinfo.flash_heimdall_partition_vbmeta):
logging.info("* pmbootstrap flasher flash_vbmeta") logging.info("* pmbootstrap flasher flash_vbmeta")
logging.info(" Flashes vbmeta image with verification disabled flag.") logging.info(" Flashes vbmeta image with verification disabled flag.")
# if current flasher supports dtbo and partition is explicitly specified # if current flasher supports dtbo and partition is explicitly specified
# in deviceinfo # in deviceinfo
if "flash_dtbo" in flasher_actions and \ if "flash_dtbo" in flasher_actions and \
(deviceinfo["flash_fastboot_partition_dtbo"] or (deviceinfo.flash_fastboot_partition_dtbo or
deviceinfo["flash_heimdall_partition_dtbo"]): deviceinfo.flash_heimdall_partition_dtbo):
logging.info("* pmbootstrap flasher flash_dtbo") logging.info("* pmbootstrap flasher flash_dtbo")
logging.info(" Flashes dtbo image.") logging.info(" Flashes dtbo image.")
@ -1226,7 +1227,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
else: else:
install_packages += ["postmarketos-base-nofde"] 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 uninstallable "dependencies" by default
install_packages += get_recommends(args, install_packages) 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 # dependency, in case the version increased
if config.build_pkgs_on_install: if config.build_pkgs_on_install:
for pkgname in install_packages: 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, # Install all packages to device rootfs chroot (and rebuild the initramfs,
# because that doesn't always happen automatically yet, e.g. when the user # because that doesn't always happen automatically yet, e.g. when the user
@ -1296,7 +1297,7 @@ def install(args: PmbArgs):
steps = 4 steps = 4
if args.zap: if args.zap:
pmb.chroot.zap(args, False) pmb.chroot.zap(False)
# Install required programs in native chroot # Install required programs in native chroot
step = 1 step = 1
@ -1313,7 +1314,7 @@ def install(args: PmbArgs):
if args.no_image: if args.no_image:
return return
elif args.android_recovery_zip: 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: if args.on_device_installer:
# Runs install_system_image twice # 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_flash_info(device, deviceinfo, args.split, True if args.disk and args.disk.is_absolute() else False)
print_sshd_info(args) 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 # Leave space before 'chroot still active' note
logging.info("") 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! ondev-prepare-internal-storage.sh in postmarketos-ondev.git!
""" """
mountpoint = "/mnt/install/boot" mountpoint = "/mnt/install/boot"
filesystem = pmb.parse.deviceinfo()["boot_filesystem"] or "ext2" filesystem = pmb.parse.deviceinfo().boot_filesystem or "ext2"
install_fsprogs(filesystem) install_fsprogs(filesystem)
logging.info(f"(native) format {device} (boot, {filesystem}), mount to" logging.info(f"(native) format {device} (boot, {filesystem}), mount to"
f" {mountpoint}") f" {mountpoint}")
@ -72,7 +72,7 @@ def format_luks_root(args: PmbArgs, device):
def get_root_filesystem(args: PmbArgs): 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() pmaports_cfg = pmb.config.pmaports.read_config()
supported = pmaports_cfg.get("supported_root_filesystems", "ext4") supported = pmaports_cfg.get("supported_root_filesystems", "ext4")

View file

@ -42,7 +42,7 @@ def mount(args: PmbArgs, img_path: Path):
init() init()
losetup_cmd: List[PathString] = ["losetup", "-f", img_path] 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: if sector_size:
losetup_cmd += ["-b", str(int(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}," logging.info(f"(native) partition /dev/install (boot: {mb_boot},"
f" reserved: {mb_reserved}, root: the rest)") 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 # Actual partitioning with 'parted'. Using check=False, because parted
# sometimes "fails to inform the kernel". In case it really failed with # sometimes "fails to inform the kernel". In case it really failed with
# partitioning, the follow-up mounting/formatting will not work, so it # partitioning, the follow-up mounting/formatting will not work, so it
# will stop there (see #463). # 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 = [ commands = [
["mktable", partition_type], ["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) pmb.chroot.apk.install(["cgpt"], Chroot.native(), build=False)
cgpt = { cgpt = {
'kpart_start': pmb.parse.deviceinfo()["cgpt_kpart_start"], 'kpart_start': pmb.parse.deviceinfo().cgpt_kpart_start,
'kpart_size': pmb.parse.deviceinfo()["cgpt_kpart_size"], 'kpart_size': pmb.parse.deviceinfo().cgpt_kpart_size,
} }
# Convert to MB and print info # 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 rootfs = "/mnt/rootfs_" + device
flavor = pmb.helpers.frontend._parse_flavor(device) flavor = pmb.helpers.frontend._parse_flavor(device)
deviceinfo = pmb.parse.deviceinfo() deviceinfo = pmb.parse.deviceinfo()
method = deviceinfo["flash_method"] method = deviceinfo.flash_method
fvars = pmb.flasher.variables(args, flavor, method) fvars = pmb.flasher.variables(args, flavor, method)
# Install recovery installer package in buildroot # 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") logging.info(f"({chroot}) create recovery zip")
for key in fvars: 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 # Create config file for the recovery installer
options = { options = {

View file

@ -1,7 +1,8 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import copy import copy
from typing import Dict from pathlib import Path
from typing import Dict, Optional
from pmb.core import get_context from pmb.core import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
@ -9,77 +10,12 @@ import pmb.config
import pmb.helpers.other import pmb.helpers.other
import pmb.helpers.devices import pmb.helpers.devices
# FIXME: It feels weird to handle this at parse time.
def sanity_check(info, path): # we should instead have the Deviceinfo object store
# Resolve path for more readable error messages # the attributes for all kernels and require the user
path = os.path.realpath(path) # to specify which one they're using.
# Basically: treat Deviceinfo as a standalone type that
# Legacy errors # doesn't need to traverse pmaports.
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:"
" <https://postmarketos.org/newarch>")
# "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 _parse_kernel_suffix(info, device, kernel): def _parse_kernel_suffix(info, device, kernel):
""" """
Remove the kernel suffix (as selected in 'pmbootstrap init') from 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) ret = copy.copy(info)
suffix_kernel = kernel.replace("-", "_") suffix_kernel = kernel.replace("-", "_")
for key in pmb.config.deviceinfo_attributes: for key in Deviceinfo.__annotations__.keys():
key_kernel = f"{key}_{suffix_kernel}" key_kernel = f"{key}_{suffix_kernel}"
if key_kernel not in ret: if key_kernel not in ret:
continue continue
@ -121,8 +57,7 @@ def _parse_kernel_suffix(info, device, kernel):
return ret return ret
# FIXME (#2324): Make deviceinfo a type! (class!!!) def deviceinfo(device=None, kernel=None) -> "Deviceinfo":
def deviceinfo(device=None, kernel=None) -> Dict[str, str]:
""" """
:param device: defaults to args.device :param device: defaults to args.device
:param kernel: defaults to args.kernel :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" " start a new device port or to choose another device. It may have"
" been renamed, see <https://postmarketos.org/renamed>") " been renamed, see <https://postmarketos.org/renamed>")
ret = {} di = Deviceinfo(path, kernel)
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
# Assign empty string as default pmb.helpers.other.cache["deviceinfo"][device] = di
for key in pmb.config.deviceinfo_attributes: return di
if key not in ret:
ret[key] = ""
ret = _parse_kernel_suffix(ret, device, kernel) class Deviceinfo:
sanity_check(ret, path) """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
pmb.helpers.other.cache["deviceinfo"][device] = ret # device
return ret 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:"
" <https://postmarketos.org/newarch>")
# "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)

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 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: if args.cmdline:
cmdline = 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"] "if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF.fd"]
# Kernel Virtual Machine (KVM) support # 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"): if args.qemu_kvm and native and os.path.exists("/dev/kvm"):
command += ["-enable-kvm"] command += ["-enable-kvm"]
command += ["-cpu", "host"] command += ["-cpu", "host"]
@ -338,7 +338,7 @@ def run(args: PmbArgs):
raise RuntimeError("'pmbootstrap qemu' can be only used with one of " raise RuntimeError("'pmbootstrap qemu' can be only used with one of "
"the QEMU device packages. Run 'pmbootstrap init' " "the QEMU device packages. Run 'pmbootstrap init' "
"and select the 'qemu' vendor.") "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 = system_image(device)
img_path_2nd = None img_path_2nd = None

View file

@ -57,7 +57,6 @@ class PmbArgs(Namespace):
cross: bool cross: bool
details: bool details: bool
details_to_stdout: bool details_to_stdout: bool
deviceinfo: Dict[str, str]
deviceinfo_parse_kernel: str deviceinfo_parse_kernel: str
devices: str devices: str
disk: Path disk: Path