WIP: 2024-06-05: args hacking and more (MR 2252)

Continue removing args and do some other optimisations.

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This commit is contained in:
Caleb Connolly 2024-06-05 05:31:02 +02:00 committed by Oliver Smith
parent 5bb2390d98
commit de4c912692
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
52 changed files with 498 additions and 464 deletions

View file

@ -54,7 +54,7 @@ def properties(pkgname):
raise ValueError("No generator available for " + pkgname + "!") raise ValueError("No generator available for " + pkgname + "!")
def generate(args: PmbArgs, pkgname, fork_alpine=False): def generate(pkgname, fork_alpine=False):
if fork_alpine: if fork_alpine:
prefix, folder, options = (pkgname, "temp", prefix, folder, options = (pkgname, "temp",
{"confirm_overwrite": True}) {"confirm_overwrite": True})
@ -83,7 +83,7 @@ def generate(args: PmbArgs, pkgname, fork_alpine=False):
else: else:
# Run pmb.aportgen.PREFIX.generate() # Run pmb.aportgen.PREFIX.generate()
# FIXME: this is really bad and hacky let's not do this please # FIXME: this is really bad and hacky let's not do this please
getattr(pmb.aportgen, prefix.replace("-", "_")).generate(args, pkgname) getattr(pmb.aportgen, prefix.replace("-", "_")).generate(pkgname)
# Move to the aports folder # Move to the aports folder
if os.path.exists(path_target): if os.path.exists(path_target):

View file

@ -11,7 +11,7 @@ import pmb.parse.apkindex
from pmb.core import Chroot, get_context from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname): def generate(pkgname):
arch = pkgname.split("-")[2] arch = pkgname.split("-")[2]
context = get_context() context = get_context()

View file

@ -101,7 +101,7 @@ def ask_for_flash_method():
" pmb/config/__init__.py.") " pmb/config/__init__.py.")
def ask_for_bootimg(args: PmbArgs): def ask_for_bootimg():
logging.info("You can analyze a known working boot.img file to" logging.info("You can analyze a known working boot.img file to"
" automatically fill out the flasher information for your" " automatically fill out the flasher information for your"
" deviceinfo file. Either specify the path to an image or" " deviceinfo file. Either specify the path to an image or"
@ -114,7 +114,7 @@ def ask_for_bootimg(args: PmbArgs):
if not path: if not path:
return None return None
try: try:
return pmb.parse.bootimg(args, path) return pmb.parse.bootimg(path)
except Exception as e: except Exception as e:
logging.fatal("ERROR: " + str(e) + ". Please try again.") logging.fatal("ERROR: " + str(e) + ". Please try again.")
@ -319,7 +319,7 @@ def generate_apkbuild(pkgname, name, arch, flash_method):
handle.write(line[8:].replace(" " * 4, "\t") + "\n") handle.write(line[8:].replace(" " * 4, "\t") + "\n")
def generate(args: PmbArgs, pkgname): def generate(pkgname):
arch = ask_for_architecture() arch = ask_for_architecture()
manufacturer = ask_for_manufacturer() manufacturer = ask_for_manufacturer()
name = ask_for_name(manufacturer) name = ask_for_name(manufacturer)
@ -330,7 +330,7 @@ def generate(args: PmbArgs, pkgname):
flash_method = ask_for_flash_method() flash_method = ask_for_flash_method()
bootimg = None bootimg = None
if flash_method in ["fastboot", "heimdall-bootimg"]: if flash_method in ["fastboot", "heimdall-bootimg"]:
bootimg = ask_for_bootimg(args) bootimg = ask_for_bootimg()
generate_deviceinfo(pkgname, name, manufacturer, year, arch, generate_deviceinfo(pkgname, name, manufacturer, year, arch,
chassis, has_keyboard, has_external_storage, chassis, has_keyboard, has_external_storage,

View file

@ -2,18 +2,17 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import pmb.aportgen.core import pmb.aportgen.core
from pmb.core import get_context from pmb.core import get_context
from pmb.types import PmbArgs
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.run import pmb.helpers.run
def generate(args: PmbArgs, pkgname): def generate(pkgname):
# Copy original aport # Copy original aport
prefix = pkgname.split("-")[0] prefix = pkgname.split("-")[0]
arch = pkgname.split("-")[1] arch = pkgname.split("-")[1]
context = get_context() context = get_context()
if prefix == "gcc": if prefix == "gcc":
upstream = pmb.aportgen.core.get_upstream_aport(args, "gcc", arch) upstream = pmb.aportgen.core.get_upstream_aport("gcc", arch)
based_on = "main/gcc (from Alpine)" based_on = "main/gcc (from Alpine)"
elif prefix == "gcc4": elif prefix == "gcc4":
upstream = f"{context.config.aports}/main/gcc4" upstream = f"{context.config.aports}/main/gcc4"

View file

@ -11,7 +11,7 @@ import pmb.parse.apkindex
from pmb.core import Chroot, get_context from pmb.core import Chroot, get_context
def generate(args: PmbArgs, pkgname): def generate(pkgname):
arch = "x86" arch = "x86"
if pkgname != "grub-efi-x86": if pkgname != "grub-efi-x86":
raise RuntimeError("only grub-efi-x86 is available") raise RuntimeError("only grub-efi-x86 is available")

View file

@ -8,7 +8,7 @@ import pmb.parse.apkindex
import pmb.parse.arch import pmb.parse.arch
def generate_apkbuild(args: PmbArgs, pkgname, deviceinfo, patches): def generate_apkbuild(pkgname, 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"])
@ -111,7 +111,7 @@ def generate_apkbuild(args: PmbArgs, pkgname, deviceinfo, patches):
hndl.write(line[8:].replace(" " * 4, "\t") + "\n") hndl.write(line[8:].replace(" " * 4, "\t") + "\n")
def generate(args: PmbArgs, pkgname): def generate(pkgname):
device = "-".join(pkgname.split("-")[1:]) device = "-".join(pkgname.split("-")[1:])
deviceinfo = pmb.parse.deviceinfo(device) deviceinfo = pmb.parse.deviceinfo(device)
work = get_context().config.work work = get_context().config.work
@ -129,4 +129,4 @@ def generate(args: PmbArgs, pkgname):
"../../.shared-patches/linux/" + patch, "../../.shared-patches/linux/" + patch,
(work / "aportgen" / patch)]) (work / "aportgen" / patch)])
generate_apkbuild(args, pkgname, deviceinfo, patches) generate_apkbuild(pkgname, deviceinfo, patches)

View file

@ -5,13 +5,12 @@ import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.chroot.apk_static import pmb.chroot.apk_static
from pmb.types import PmbArgs
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 generate(args: PmbArgs, pkgname): def generate(pkgname):
arch = pkgname.split("-")[1] arch = pkgname.split("-")[1]
# Parse musl version from APKINDEX # Parse musl version from APKINDEX

View file

@ -56,13 +56,14 @@ def arch(args: PmbArgs, pkgname: str):
apkbuild = pmb.parse.apkbuild(aport) apkbuild = pmb.parse.apkbuild(aport)
arches = apkbuild["arch"] arches = apkbuild["arch"]
deviceinfo = pmb.parse.deviceinfo()
if get_context().config.build_default_device_arch: if get_context().config.build_default_device_arch:
preferred_arch = args.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 = args.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 = args.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

@ -1,6 +1,7 @@
# 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 os import os
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import Any, Dict from typing import Any, Dict
@ -84,8 +85,8 @@ def get_outputdir(args: PmbArgs, pkgname: str, apkbuild: Dict[str, Any]) -> Path
" template with: pmbootstrap aportgen " + pkgname) " template with: pmbootstrap aportgen " + pkgname)
def extract_and_patch_sources(args: PmbArgs, pkgname: str, arch): def extract_and_patch_sources(pkgname: str, arch):
pmb.build.copy_to_buildpath(args, pkgname) pmb.build.copy_to_buildpath(pkgname)
logging.info("(native) extract kernel source") logging.info("(native) extract kernel source")
pmb.chroot.user(["abuild", "unpack"], working_dir=Path("/home/pmos/build")) pmb.chroot.user(["abuild", "unpack"], working_dir=Path("/home/pmos/build"))
logging.info("(native) apply patches") logging.info("(native) apply patches")
@ -102,14 +103,14 @@ def menuconfig(args: PmbArgs, pkgname: str, use_oldconfig):
aport = pmb.helpers.pmaports.find(pkgname) aport = pmb.helpers.pmaports.find(pkgname)
apkbuild = pmb.parse.apkbuild(aport / "APKBUILD") apkbuild = pmb.parse.apkbuild(aport / "APKBUILD")
arch = args.arch or get_arch(apkbuild) arch = args.arch or get_arch(apkbuild)
suffix = pmb.build.autodetect.chroot(apkbuild, arch) chroot = pmb.build.autodetect.chroot(apkbuild, arch)
cross = pmb.build.autodetect.crosscompile(args, apkbuild, arch, suffix) cross = pmb.build.autodetect.crosscompile(apkbuild, arch, chroot)
hostspec = pmb.parse.arch.alpine_to_hostspec(arch) hostspec = pmb.parse.arch.alpine_to_hostspec(arch)
# Set up build tools and makedepends # Set up build tools and makedepends
pmb.build.init(args, suffix) pmb.build.init(chroot)
if cross: if cross:
pmb.build.init_compiler(args, [], cross, arch) pmb.build.init_compiler(get_context(), [], cross, arch)
depends = apkbuild["makedepends"] depends = apkbuild["makedepends"]
copy_xauth = False copy_xauth = False
@ -128,13 +129,13 @@ def menuconfig(args: PmbArgs, pkgname: str, use_oldconfig):
else: else:
depends += ["ncurses-dev"] depends += ["ncurses-dev"]
pmb.chroot.apk.install(depends) pmb.chroot.apk.install(depends, Chroot.native())
# Copy host's .xauthority into native # Copy host's .xauthority into native
if copy_xauth: if copy_xauth:
pmb.chroot.other.copy_xauthority(args) pmb.chroot.other.copy_xauthority(args)
extract_and_patch_sources(args, pkgname, arch) extract_and_patch_sources(pkgname, arch)
# Check for background color variable # Check for background color variable
color = os.environ.get("MENUCONFIG_COLOR") color = os.environ.get("MENUCONFIG_COLOR")

View file

@ -3,7 +3,7 @@
import os import os
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import pmb.chroot.run import pmb.chroot
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
import pmb.parse import pmb.parse

View file

@ -9,6 +9,7 @@ from typing import List
import pmb.chroot import pmb.chroot
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.build
import pmb.helpers.file import pmb.helpers.file
import pmb.helpers.git import pmb.helpers.git
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -99,7 +100,7 @@ def index_repo(args: PmbArgs, arch=None):
:param arch: when not defined, re-index all repos :param arch: when not defined, re-index all repos
""" """
pmb.build.init(args) pmb.build.init()
channel = pmb.config.pmaports.read_config()["channel"] channel = pmb.config.pmaports.read_config()["channel"]
pkgdir = (get_context().config.work / "packages" / channel) pkgdir = (get_context().config.work / "packages" / channel)

View file

@ -11,10 +11,10 @@ import pmb.helpers.cli
from pmb.core import Chroot, get_context from pmb.core import Chroot, get_context
def build(args: PmbArgs, flavor, chroot: Chroot): def build(flavor, chroot: Chroot):
# Update mkinitfs and hooks # Update mkinitfs and hooks
pmb.chroot.apk.install(["postmarketos-mkinitfs"], chroot) pmb.chroot.apk.install(["postmarketos-mkinitfs"], chroot)
pmb.chroot.initfs_hooks.update(args, chroot) pmb.chroot.initfs_hooks.update(chroot)
pmaports_cfg = pmb.config.pmaports.read_config() pmaports_cfg = pmb.config.pmaports.read_config()
# Call mkinitfs # Call mkinitfs
@ -30,7 +30,7 @@ def build(args: PmbArgs, flavor, chroot: Chroot):
chroot) chroot)
def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False): def extract(flavor, chroot: Chroot, extra=False):
""" """
Extract the initramfs to /tmp/initfs-extracted or the initramfs-extra to Extract the initramfs to /tmp/initfs-extracted or the initramfs-extra to
/tmp/initfs-extra-extracted and return the outside extraction path. /tmp/initfs-extra-extracted and return the outside extraction path.
@ -77,11 +77,11 @@ def extract(args: PmbArgs, flavor, chroot: Chroot, extra=False):
return outside return outside
def ls(args: PmbArgs, flavor, suffix, extra=False): def ls( flavor, suffix, extra=False):
tmp = "/tmp/initfs-extracted" tmp = "/tmp/initfs-extracted"
if extra: if extra:
tmp = "/tmp/initfs-extra-extracted" tmp = "/tmp/initfs-extra-extracted"
extract(args, flavor, suffix, extra) extract(flavor, suffix, extra)
pmb.chroot.root(["ls", "-lahR", "."], suffix, Path(tmp), "stdout") pmb.chroot.root(["ls", "-lahR", "."], suffix, Path(tmp), "stdout")
pmb.chroot.root(["rm", "-r", tmp], suffix) pmb.chroot.root(["rm", "-r", tmp], suffix)
@ -95,17 +95,17 @@ def frontend(args: PmbArgs):
# Handle initfs actions # Handle initfs actions
action = args.action_initfs action = args.action_initfs
if action == "build": if action == "build":
build(args, flavor, chroot) build(flavor, chroot)
elif action == "extract": elif action == "extract":
dir = extract(args, flavor, chroot) dir = extract(flavor, chroot)
logging.info(f"Successfully extracted initramfs to: {dir}") logging.info(f"Successfully extracted initramfs to: {dir}")
dir_extra = extract(args, flavor, chroot, True) dir_extra = extract(flavor, chroot, True)
logging.info(f"Successfully extracted initramfs-extra to: {dir_extra}") logging.info(f"Successfully extracted initramfs-extra to: {dir_extra}")
elif action == "ls": elif action == "ls":
logging.info("*** initramfs ***") logging.info("*** initramfs ***")
ls(args, flavor, chroot) ls(flavor, chroot)
logging.info("*** initramfs-extra ***") logging.info("*** initramfs-extra ***")
ls(args, flavor, chroot, True) ls(flavor, chroot, True)
# Handle hook actions # Handle hook actions
elif action == "hook_ls": elif action == "hook_ls":
@ -117,7 +117,7 @@ def frontend(args: PmbArgs):
pmb.chroot.initfs_hooks.delete(args.hook, chroot) pmb.chroot.initfs_hooks.delete(args.hook, chroot)
# Rebuild the initfs after adding/removing a hook # Rebuild the initfs after adding/removing a hook
build(args, flavor, chroot) build(flavor, chroot)
if action in ["ls", "extract"]: if action in ["ls", "extract"]:
link = "https://wiki.postmarketos.org/wiki/Initramfs_development" link = "https://wiki.postmarketos.org/wiki/Initramfs_development"

View file

@ -55,7 +55,7 @@ def delete(hook, suffix: Chroot):
pmb.chroot.root(["apk", "del", f"{prefix}{hook}"], suffix) pmb.chroot.root(["apk", "del", f"{prefix}{hook}"], suffix)
def update(args: PmbArgs, suffix: Chroot): def update(suffix: Chroot):
""" """
Rebuild and update all hooks that are out of date Rebuild and update all hooks that are out of date
""" """

View file

@ -5,6 +5,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
import pmb.config import pmb.config
import pmb.chroot.apk
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse import pmb.parse
@ -98,7 +99,7 @@ def mount(chroot: Chroot):
pmb.helpers.mount.bind(source, target_outer) pmb.helpers.mount.bind(source, target_outer)
def mount_native_into_foreign(args: PmbArgs, chroot: Chroot): def mount_native_into_foreign(chroot: Chroot):
source = Chroot.native().path source = Chroot.native().path
target = chroot / "native" target = chroot / "native"
pmb.helpers.mount.bind(source, target) pmb.helpers.mount.bind(source, target)

View file

@ -115,7 +115,7 @@ def zap_pkgs_local_mismatch(args: PmbArgs, confirm=True, dry=False):
continue continue
# Aport path # Aport path
aport_path = pmb.helpers.pmaports.find_optional(args, origin) aport_path = pmb.helpers.pmaports.find_optional(origin)
if not aport_path: if not aport_path:
logging.info(f"% rm {apk_path_short}" logging.info(f"% rm {apk_path_short}"
f" ({origin} aport not found)") f" ({origin} aport not found)")

View file

@ -114,7 +114,7 @@ def ask_which_scripts_to_run(scripts_available):
return ret return ret
def copy_git_repo_to_chroot(args: PmbArgs, topdir): def copy_git_repo_to_chroot(topdir):
""" Create a tarball of the git repo (including unstaged changes and new """ Create a tarball of the git repo (including unstaged changes and new
files) and extract it in chroot_native. files) and extract it in chroot_native.
@ -122,7 +122,7 @@ def copy_git_repo_to_chroot(args: PmbArgs, topdir):
pmb.helpers.git.get_topdir() pmb.helpers.git.get_topdir()
""" """
pmb.chroot.init(args) pmb.chroot.init()
tarball_path = Chroot.native() / "tmp/git.tar.gz" tarball_path = Chroot.native() / "tmp/git.tar.gz"
files = pmb.helpers.git.get_files(topdir) files = pmb.helpers.git.get_files(topdir)
@ -141,7 +141,7 @@ def copy_git_repo_to_chroot(args: PmbArgs, topdir):
working_dir=ci_dir) working_dir=ci_dir)
def run_scripts(args: PmbArgs, topdir, scripts): def run_scripts(topdir, scripts):
""" Run one of the given scripts after another, either natively or in a """ Run one of the given scripts after another, either natively or in a
chroot. Display a progress message and stop on error (without printing chroot. Display a progress message and stop on error (without printing
a python stack trace). a python stack trace).
@ -174,7 +174,7 @@ def run_scripts(args: PmbArgs, topdir, scripts):
else: else:
# Run inside pmbootstrap chroot # Run inside pmbootstrap chroot
if not repo_copied: if not repo_copied:
copy_git_repo_to_chroot(args, topdir) copy_git_repo_to_chroot(topdir)
repo_copied = True repo_copied = True
env = {"TESTUSER": "pmos"} env = {"TESTUSER": "pmos"}

View file

@ -8,7 +8,6 @@ from pmb import commands
from pmb.types import PathString from pmb.types import PathString
from pmb.helpers import run from pmb.helpers import run
from pmb.core import get_context from pmb.core import get_context
from pmb import config
class Log(commands.Command): class Log(commands.Command):
clear_log: bool clear_log: bool
@ -20,7 +19,7 @@ class Log(commands.Command):
def run(self): def run(self):
context = get_context() context = get_context()
log_testsuite = config.work / "log_testsuite.txt" log_testsuite = context.config.work / "log_testsuite.txt"
if self.clear_log: if self.clear_log:
run.user(["truncate", "-s", "0", context.log]) run.user(["truncate", "-s", "0", context.log])

View file

@ -2,6 +2,7 @@
# 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.core.chroot import Chroot from pmb.core.chroot import Chroot
from pmb.core.context import Context
from pmb.helpers import logging from pmb.helpers import logging
import glob import glob
import json import json
@ -38,13 +39,13 @@ def require_programs():
f" {', '.join(missing)}") f" {', '.join(missing)}")
def ask_for_username(args: PmbArgs): def ask_for_username(args: PmbArgs, default_user: str):
"""Ask for a reasonable username for the non-root user. """Ask for a reasonable username for the non-root user.
:returns: the username :returns: the username
""" """
while True: while True:
ret = pmb.helpers.cli.ask("Username", None, args.user, False, ret = pmb.helpers.cli.ask("Username", None, default_user, False,
"[a-z_][a-z0-9_-]*") "[a-z_][a-z0-9_-]*")
if ret == "root": if ret == "root":
logging.fatal("ERROR: don't put \"root\" here. This is about" logging.fatal("ERROR: don't put \"root\" here. This is about"
@ -139,8 +140,8 @@ def ask_for_channel(args: PmbArgs):
" from the list above.") " from the list above.")
def ask_for_ui(args: PmbArgs, info): def ask_for_ui(info):
ui_list = pmb.helpers.ui.list_ui(args, info["arch"]) ui_list = pmb.helpers.ui.list_ui(info["arch"])
hidden_ui_count = 0 hidden_ui_count = 0
device_is_accelerated = info.get("gpu_accelerated") == "true" device_is_accelerated = info.get("gpu_accelerated") == "true"
if not device_is_accelerated: if not device_is_accelerated:
@ -154,7 +155,7 @@ def ask_for_ui(args: PmbArgs, info):
hidden_ui_count += 1 hidden_ui_count += 1
# Get default # Get default
default: Any = args.ui default: Any = get_context().config.ui
if default not in dict(ui_list).keys(): if default not in dict(ui_list).keys():
default = pmb.config.defaults["ui"] default = pmb.config.defaults["ui"]
@ -176,7 +177,7 @@ def ask_for_ui(args: PmbArgs, info):
" one from the list above.") " one from the list above.")
def ask_for_ui_extras(args: PmbArgs, ui): def ask_for_ui_extras(config: Config, ui):
apkbuild = pmb.helpers.pmaports.get(f"postmarketos-ui-{ui}", apkbuild = pmb.helpers.pmaports.get(f"postmarketos-ui-{ui}",
subpackages=False, must_exist=False) subpackages=False, must_exist=False)
if not apkbuild: if not apkbuild:
@ -190,17 +191,17 @@ def ask_for_ui_extras(args: PmbArgs, ui):
f" {extra['pkgdesc']}") f" {extra['pkgdesc']}")
return pmb.helpers.cli.confirm("Enable this package?", return pmb.helpers.cli.confirm("Enable this package?",
default=args.ui_extras) default=config.ui_extras)
def ask_for_systemd(args: PmbArgs, ui): def ask_for_systemd(config: Config, ui):
if "systemd" not in pmb.config.pmaports.read_config_repos(): if "systemd" not in pmb.config.pmaports.read_config_repos():
return args.systemd return config.systemd
if pmb.helpers.ui.check_option(ui, "pmb:systemd-never"): if pmb.helpers.ui.check_option(ui, "pmb:systemd-never"):
logging.info("Based on your UI selection, OpenRC will be used as init" logging.info("Based on your UI selection, OpenRC will be used as init"
" system. This UI does not support systemd.") " system. This UI does not support systemd.")
return args.systemd return config.systemd
default_is_systemd = pmb.helpers.ui.check_option(ui, "pmb:systemd") default_is_systemd = pmb.helpers.ui.check_option(ui, "pmb:systemd")
not_str = " " if default_is_systemd else " not " not_str = " " if default_is_systemd else " not "
@ -210,7 +211,7 @@ def ask_for_systemd(args: PmbArgs, ui):
choices = pmb.config.allowed_values["systemd"] choices = pmb.config.allowed_values["systemd"]
answer = pmb.helpers.cli.ask("Install systemd?", answer = pmb.helpers.cli.ask("Install systemd?",
choices, choices,
args.systemd, config.systemd,
validation_regex=f"^({'|'.join(choices)})$", validation_regex=f"^({'|'.join(choices)})$",
complete=choices) complete=choices)
return answer return answer
@ -314,7 +315,7 @@ def ask_for_provider_select(apkbuild, providers_cfg):
" one from the list above.") " one from the list above.")
def ask_for_provider_select_pkg(args: PmbArgs, pkgname, providers_cfg): def ask_for_provider_select_pkg(pkgname, providers_cfg):
"""Look up the APKBUILD for the specified pkgname and ask for selectable """Look up the APKBUILD for the specified pkgname and ask for selectable
providers that are specified using "_pmb_select". providers that are specified using "_pmb_select".
@ -327,10 +328,10 @@ def ask_for_provider_select_pkg(args: PmbArgs, pkgname, providers_cfg):
if not apkbuild: if not apkbuild:
return return
ask_for_provider_select(args, apkbuild, providers_cfg) ask_for_provider_select(apkbuild, providers_cfg)
def ask_for_device_kernel(args: PmbArgs, device: str): def ask_for_device_kernel(config: Config, device: str):
"""Ask for the kernel that should be used with the device. """Ask for the kernel that should be used with the device.
:param device: code name, e.g. "lg-mako" :param device: code name, e.g. "lg-mako"
@ -343,10 +344,10 @@ def ask_for_device_kernel(args: PmbArgs, device: str):
# Get kernels # Get kernels
kernels = pmb.parse._apkbuild.kernels(device) kernels = pmb.parse._apkbuild.kernels(device)
if not kernels: if not kernels:
return args.kernel return config.kernel
# Get default # Get default
default = args.kernel default = config.kernel
if default not in kernels: if default not in kernels:
default = list(kernels.keys())[0] default = list(kernels.keys())[0]
@ -374,7 +375,7 @@ def ask_for_device_kernel(args: PmbArgs, device: str):
return ret return ret
def ask_for_device(config: Config): def ask_for_device(context: Context):
""" """
Prompt for the device vendor, model, and kernel. Prompt for the device vendor, model, and kernel.
@ -383,16 +384,16 @@ def ask_for_device(config: Config):
* device_exists: bool indicating if device port exists in repo * device_exists: bool indicating if device port exists in repo
* kernel: type of kernel (downstream, etc) * kernel: type of kernel (downstream, etc)
""" """
vendors = sorted(pmb.helpers.devices.list_vendors(get_context().config.aports)) vendors = sorted(pmb.helpers.devices.list_vendors(context.config.aports))
logging.info("Choose your target device vendor (either an " logging.info("Choose your target device vendor (either an "
"existing one, or a new one for porting).") "existing one, or a new one for porting).")
logging.info(f"Available vendors ({len(vendors)}): {', '.join(vendors)}") logging.info(f"Available vendors ({len(vendors)}): {', '.join(vendors)}")
current_vendor = None current_vendor = None
current_codename = None current_codename = None
if config.device: if context.config.device:
current_vendor = config.device.split("-", 1)[0] current_vendor = context.config.device.split("-", 1)[0]
current_codename = config.device.split("-", 1)[1] current_codename = context.config.device.split("-", 1)[1]
while True: while True:
vendor = pmb.helpers.cli.ask("Vendor", None, current_vendor, vendor = pmb.helpers.cli.ask("Vendor", None, current_vendor,
@ -409,7 +410,7 @@ def ask_for_device(config: Config):
else: else:
# Archived devices can be selected, but are not displayed # Archived devices can be selected, but are not displayed
devices = sorted(pmb.helpers.devices.list_codenames( devices = sorted(pmb.helpers.devices.list_codenames(
get_context().config.aports, vendor, archived=False)) context.config.aports, vendor, archived=False))
# Remove "vendor-" prefixes from device list # Remove "vendor-" prefixes from device list
codenames = [x.split('-', 1)[1] for x in devices] codenames = [x.split('-', 1)[1] for x in devices]
logging.info(f"Available codenames ({len(codenames)}): " + logging.info(f"Available codenames ({len(codenames)}): " +
@ -424,7 +425,7 @@ def ask_for_device(config: Config):
device = f"{vendor}-{codename}" device = f"{vendor}-{codename}"
device_path = pmb.helpers.devices.find_path(device, 'deviceinfo') device_path = pmb.helpers.devices.find_path(device, 'deviceinfo')
if device_path is None: if device_path is None:
if device == args.devicesdhbfvhubsud: if device == context.device:
raise RuntimeError( raise RuntimeError(
"This device does not exist anymore, check" "This device does not exist anymore, check"
" <https://postmarketos.org/renamed>" " <https://postmarketos.org/renamed>"
@ -440,14 +441,14 @@ def ask_for_device(config: Config):
pmb.aportgen.generate(f"device-{device}") pmb.aportgen.generate(f"device-{device}")
pmb.aportgen.generate(f"linux-{device}") pmb.aportgen.generate(f"linux-{device}")
elif any("archived" == x for x in device_path.parts): elif any("archived" == x for x in device_path.parts):
apkbuild = f"{device_path[:-len('deviceinfo')]}APKBUILD" apkbuild = device_path.parent / "APKBUILD"
archived = pmb.parse._apkbuild.archived(apkbuild) archived = pmb.parse._apkbuild.archived(apkbuild)
logging.info(f"WARNING: {device} is archived: {archived}") logging.info(f"WARNING: {device} is archived: {archived}")
if not pmb.helpers.cli.confirm(args): if not pmb.helpers.cli.confirm():
continue continue
break break
kernel = ask_for_device_kernel(args, device) kernel = ask_for_device_kernel(context.config, device)
return (device, device_path is not None, kernel) return (device, device_path is not None, kernel)
@ -675,7 +676,7 @@ def frontend(args: PmbArgs):
pmb.config.pmaports.install_githooks() pmb.config.pmaports.install_githooks()
# Device # Device
device, device_exists, kernel = ask_for_device(config) device, device_exists, kernel = ask_for_device(get_context())
config.device = device config.device = device
config.kernel = kernel config.kernel = kernel
@ -689,19 +690,19 @@ def frontend(args: PmbArgs):
if device_exists: if device_exists:
config.keymap = ask_for_keymaps(args, info) config.keymap = ask_for_keymaps(args, info)
config.user = ask_for_username(args) config.user = ask_for_username(args, config.user)
ask_for_provider_select_pkg(args, "postmarketos-base", config.providers) ask_for_provider_select_pkg("postmarketos-base", config.providers)
ask_for_provider_select_pkg(args, "postmarketos-base-ui", config.providers) ask_for_provider_select_pkg("postmarketos-base-ui", config.providers)
# UI and various build options # UI and various build options
ui = ask_for_ui(args, info) ui = ask_for_ui(info)
config.ui = ui config.ui = ui
config.ui_extras = ask_for_ui_extras(args, ui) config.ui_extras = ask_for_ui_extras(config, ui)
# systemd # systemd
config.systemd = ask_for_systemd(args, ui) config.systemd = ask_for_systemd(config, ui)
ask_for_provider_select_pkg(args, f"postmarketos-ui-{ui}", ask_for_provider_select_pkg(f"postmarketos-ui-{ui}",
config.providers) config.providers)
ask_for_additional_options(args, config) ask_for_additional_options(args, config)

View file

@ -13,14 +13,22 @@ class Context():
quiet: bool = False quiet: bool = False
command_timeout: float = 900 command_timeout: float = 900
sudo_timer: bool = False sudo_timer: bool = False
force: bool = False
log: Path log: Path
# The architecture of the selected device # The architecture of the selected device
device_arch: Optional[str] = None device_arch: Optional[str] = None
offline: bool = False
# assume yes to prompts # assume yes to prompts
assume_yes: bool = False assume_yes: bool = False
# Operate offline
offline: bool = False
# Device we are operating on
# FIXME: should not be in global context, only
# specific actions actually depend on this
device: str = ""
# The pmbootstrap subcommand # The pmbootstrap subcommand
command: str = "" command: str = ""
@ -41,6 +49,7 @@ class Context():
__context: Context __context: Context
# mypy: disable-error-code="return-value"
def get_context(allow_failure: bool=False) -> Context: def get_context(allow_failure: bool=False) -> Context:
"""Get immutable global runtime context.""" """Get immutable global runtime context."""
global __context global __context

View file

@ -1,3 +1,4 @@
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
@ -9,7 +10,8 @@ import pmb.export
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def frontend(args: PmbArgs): def frontend(args: PmbArgs): # FIXME: ARGS_REFACTOR
context = get_context()
# Create the export folder # Create the export folder
target = args.export_folder target = args.export_folder
if not os.path.exists(target): if not os.path.exists(target):
@ -17,18 +19,18 @@ def frontend(args: PmbArgs):
# Rootfs image note # Rootfs image note
chroot = Chroot.native() chroot = Chroot.native()
rootfs_dir = chroot / "home/pmos/rootfs" / args.devicesdhbfvhubsud rootfs_dir = chroot / "home/pmos/rootfs" / context.device
if not rootfs_dir.glob("*.img"): if not rootfs_dir.glob("*.img"):
logging.info("NOTE: To export the rootfs image, run 'pmbootstrap" logging.info("NOTE: To export the rootfs image, run 'pmbootstrap"
" install' first (without the 'disk' parameter).") " install' first (without the 'disk' parameter).")
# Rebuild the initramfs, just to make sure (see #69) # Rebuild the initramfs, just to make sure (see #69)
flavor = pmb.helpers.frontend._parse_flavor(args, args.autoinstall) flavor = pmb.helpers.frontend._parse_flavor(context.device, args.autoinstall)
if args.autoinstall: if args.autoinstall:
pmb.chroot.initfs.build(args, flavor, Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)) pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, context.device))
# Do the export, print all files # Do the export, print all files
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(args, flavor, target) pmb.export.odin(context, flavor, target)
pmb.export.symlinks(args, flavor, target) pmb.export.symlinks(args, flavor, target)

View file

@ -1,25 +1,26 @@
# 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.context import Context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import pmb.build import pmb.build
import pmb.chroot.apk import pmb.chroot.apk
import pmb.config import pmb.config
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 odin(args: PmbArgs, flavor, folder: Path): def odin(context: Context, flavor, folder: Path):
""" """
Create Odin flashable tar file with kernel and initramfs Create Odin flashable tar file with kernel and initramfs
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(args) pmb.flasher.init(context.device)
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) suffix = Chroot(ChrootType.ROOTFS, context.device)
deviceinfo = pmb.parse.deviceinfo(context.device)
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
suffix_flavor = f"-{flavor}" suffix_flavor = f"-{flavor}"
@ -28,7 +29,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
suffix_flavor = "" suffix_flavor = ""
# Validate method # Validate method
method = args.deviceinfo["flash_method"] method = deviceinfo["flash_method"]
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"
@ -36,10 +37,8 @@ def odin(args: PmbArgs, flavor, folder: Path):
" Only 'heimdall' methods are supported.") " Only 'heimdall' methods are supported.")
# Partitions # Partitions
partition_kernel = \ partition_kernel = deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL"
args.deviceinfo["flash_heimdall_partition_kernel"] or "KERNEL" partition_initfs = deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY"
partition_initfs = \
args.deviceinfo["flash_heimdall_partition_initfs"] or "RECOVERY"
# Temporary folder # Temporary folder
temp_folder = "/tmp/odin-flashable-tar" temp_folder = "/tmp/odin-flashable-tar"
@ -49,12 +48,12 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Odin flashable tar generation script # Odin flashable tar generation script
# (because redirecting stdin/stdout is not allowed # (because redirecting stdin/stdout is not allowed
# in pmbootstrap's chroot/shell functions for security reasons) # in pmbootstrap's chroot/shell functions for security reasons)
odin_script = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) / "tmp/_odin.sh" odin_script = Chroot(ChrootType.ROOTFS, context.device) / "tmp/_odin.sh"
with odin_script.open("w") as handle: with odin_script.open("w") as handle:
odin_kernel_md5 = f"{partition_kernel}.bin.md5" odin_kernel_md5 = f"{partition_kernel}.bin.md5"
odin_initfs_md5 = f"{partition_initfs}.bin.md5" odin_initfs_md5 = f"{partition_initfs}.bin.md5"
odin_device_tar = f"{args.devicesdhbfvhubsud}.tar" odin_device_tar = f"{context.device}.tar"
odin_device_tar_md5 = f"{args.devicesdhbfvhubsud}.tar.md5" odin_device_tar_md5 = f"{context.device}.tar.md5"
handle.write( handle.write(
"#!/bin/sh\n" "#!/bin/sh\n"
@ -90,7 +89,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Move Odin flashable tar to native chroot and cleanup temp folder # Move Odin flashable tar to native chroot and cleanup temp folder
pmb.chroot.user(["mkdir", "-p", "/home/pmos/rootfs"]) pmb.chroot.user(["mkdir", "-p", "/home/pmos/rootfs"])
pmb.chroot.root(["mv", f"/mnt/rootfs_{args.devicesdhbfvhubsud}{temp_folder}" pmb.chroot.root(["mv", f"/mnt/rootfs_{context.device}{temp_folder}"
f"/{odin_device_tar_md5}", "/home/pmos/rootfs/"]), f"/{odin_device_tar_md5}", "/home/pmos/rootfs/"]),
pmb.chroot.root(["chown", "pmos:pmos", pmb.chroot.root(["chown", "pmos:pmos",
f"/home/pmos/rootfs/{odin_device_tar_md5}"]) f"/home/pmos/rootfs/{odin_device_tar_md5}"])
@ -99,7 +98,7 @@ def odin(args: PmbArgs, flavor, folder: Path):
# Create the symlink # Create the symlink
file = Chroot.native() / "home/pmos/rootfs" / odin_device_tar_md5 file = Chroot.native() / "home/pmos/rootfs" / odin_device_tar_md5
link = folder / odin_device_tar_md5 link = folder / odin_device_tar_md5
pmb.helpers.file.symlink(args, file, link) pmb.helpers.file.symlink(file, link)
# Display a readable message # Display a readable message
msg = f" * {odin_device_tar_md5}" msg = f" * {odin_device_tar_md5}"

View file

@ -1,5 +1,6 @@
# 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.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import List from typing import List
@ -18,6 +19,9 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
""" """
Create convenience symlinks to the rootfs and boot files. Create convenience symlinks to the rootfs and boot files.
""" """
context = get_context()
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}"
@ -35,28 +39,28 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
f"uInitrd{suffix}": "Initramfs, legacy u-boot image format", f"uInitrd{suffix}": "Initramfs, legacy u-boot image format",
f"uImage{suffix}": "Kernel, legacy u-boot image format", f"uImage{suffix}": "Kernel, legacy u-boot image format",
f"vmlinuz{suffix}": "Linux kernel", f"vmlinuz{suffix}": "Linux kernel",
f"{args.devicesdhbfvhubsud}.img": "Rootfs with partitions for /boot and /", f"{context.device}.img": "Rootfs with partitions for /boot and /",
f"{args.devicesdhbfvhubsud}-boot.img": "Boot partition image", f"{context.device}-boot.img": "Boot partition image",
f"{args.devicesdhbfvhubsud}-root.img": "Root partition image", f"{context.device}-root.img": "Root partition image",
f"pmos-{args.devicesdhbfvhubsud}.zip": "Android recovery flashable zip", f"pmos-{context.device}.zip": "Android recovery flashable zip",
"lk2nd.img": "Secondary Android bootloader", "lk2nd.img": "Secondary Android bootloader",
} }
# Generate a list of patterns # Generate a list of patterns
chroot_native = Chroot.native() chroot_native = Chroot.native()
path_boot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) / "boot" path_boot = Chroot(ChrootType.ROOTFS, context.device) / "boot"
chroot_buildroot = Chroot.buildroot(args.deviceinfo['arch']) chroot_buildroot = Chroot.buildroot(arch)
files: List[Path] = [ files: List[Path] = [
path_boot / f"boot.img{suffix}", path_boot / f"boot.img{suffix}",
path_boot / f"uInitrd{suffix}", path_boot / f"uInitrd{suffix}",
path_boot / f"uImage{suffix}", path_boot / f"uImage{suffix}",
path_boot / f"vmlinuz{suffix}", path_boot / f"vmlinuz{suffix}",
path_boot / "dtbo.img", path_boot / "dtbo.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}.img", chroot_native / "home/pmos/rootfs" / f"{context.device}.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}-boot.img", chroot_native / "home/pmos/rootfs" / f"{context.device}-boot.img",
chroot_native / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}-root.img", chroot_native / "home/pmos/rootfs" / f"{context.device}-root.img",
chroot_buildroot / "var/libpostmarketos-android-recovery-installer" / chroot_buildroot / "var/libpostmarketos-android-recovery-installer" /
f"pmos-{args.devicesdhbfvhubsud}.zip", f"pmos-{context.device}.zip",
path_boot / "lk2nd.img" path_boot / "lk2nd.img"
] ]
@ -73,4 +77,4 @@ def symlinks(args: PmbArgs, flavor, folder: Path):
msg += " (" + info[basename] + ")" msg += " (" + info[basename] + ")"
logging.info(msg) logging.info(msg)
pmb.helpers.file.symlink(args, file, link) pmb.helpers.file.symlink(file, link)

View file

@ -1,5 +1,7 @@
# 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.helpers import logging from pmb.helpers import logging
import pmb.config import pmb.config
@ -15,106 +17,93 @@ import pmb.parse.kconfig
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def kernel(args: PmbArgs): def kernel(device: str, deviceinfo: Dict[str, 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(args, args.autoinstall) flavor = pmb.helpers.frontend._parse_flavor(device, autoinstall)
if args.autoinstall: if autoinstall:
pmb.chroot.initfs.build(args, flavor, Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)) pmb.chroot.initfs.build(flavor, Chroot(ChrootType.ROOTFS, device))
# Check kernel config # Check kernel config
pmb.parse.kconfig.check(args, flavor, must_exist=False) pmb.parse.kconfig.check(flavor, must_exist=False)
# Generate the paths and run the flasher # Generate the paths and run the flasher
if args.action_flasher == "boot": if boot:
logging.info("(native) boot " + flavor + " kernel") logging.info("(native) boot " + flavor + " kernel")
pmb.flasher.run(args, "boot", flavor) pmb.flasher.run(device, deviceinfo, "boot", flavor)
else: else:
logging.info("(native) flash kernel " + flavor) logging.info("(native) flash kernel " + flavor)
pmb.flasher.run(args, "flash_kernel", flavor) pmb.flasher.run(device, deviceinfo, "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"
" booted:") " booted:")
logging.info("ssh {}@{}".format(args.user, pmb.config.default_ip)) logging.info(f"ssh {get_context().config.user}@{pmb.config.default_ip}")
logging.info("NOTE: If you enabled full disk encryption, you should make" logging.info("NOTE: If you enabled full disk encryption, you should make"
" sure that Unl0kr has been properly configured for your" " sure that Unl0kr has been properly configured for your"
" device") " device")
def list_flavors(args: PmbArgs): def list_flavors(device: str):
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) chroot = Chroot(ChrootType.ROOTFS, device)
logging.info(f"({suffix}) installed kernel flavors:") logging.info(f"({chroot}) installed kernel flavors:")
logging.info("* " + pmb.chroot.other.kernel_flavor_installed(suffix)) logging.info("* " + pmb.chroot.other.kernel_flavor_installed(chroot))
def rootfs(args: PmbArgs): def rootfs(device: str, deviceinfo: Dict[str, str], method: str):
method = args.flash_method or args.deviceinfo["flash_method"]
# 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"{args.devicesdhbfvhubsud}{suffix}" img_path = Chroot.native() / "home/pmos/rootfs" / f"{device}{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 args.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(args.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(args, "flash_rootfs") pmb.flasher.run(device, deviceinfo, "flash_rootfs")
def flash_vbmeta(args: PmbArgs): def list_devices(device, deviceinfo):
logging.info("(native) flash vbmeta.img with verity disabled flag") pmb.flasher.run(device, deviceinfo, "list_devices")
pmb.flasher.run(args, "flash_vbmeta")
def flash_dtbo(args: PmbArgs): def sideload(device: str, deviceinfo: Dict[str, str]):
logging.info("(native) flash dtbo image")
pmb.flasher.run(args, "flash_dtbo")
def list_devices(args: PmbArgs):
pmb.flasher.run(args, "list_devices")
def sideload(args: PmbArgs):
# Install depends # Install depends
pmb.flasher.install_depends(args) pmb.flasher.install_depends()
# Mount the buildroot # Mount the buildroot
chroot = Chroot.buildroot(args.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-{args.devicesdhbfvhubsud}.zip").exists(): / f"pmos-{device}.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(args, "sideload") pmb.flasher.run(device, deviceinfo, "sideload")
def flash_lk2nd(args: PmbArgs): def flash_lk2nd(device: str, deviceinfo: Dict[str, str], method: str):
method = args.flash_method or args.deviceinfo["flash_method"]
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(args) pmb.flasher.init(device)
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)
@ -125,7 +114,7 @@ def flash_lk2nd(args: PmbArgs):
" 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-{args.devicesdhbfvhubsud}" device_pkg = f"device-{device}"
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"]:
@ -136,16 +125,19 @@ def flash_lk2nd(args: PmbArgs):
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, args.devicesdhbfvhubsud) suffix = Chroot(ChrootType.ROOTFS, device)
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(args, "flash_lk2nd") pmb.flasher.run(device, deviceinfo, "flash_lk2nd")
def frontend(args: PmbArgs): def frontend(args: PmbArgs):
context = get_context()
action = args.action_flasher action = args.action_flasher
method = args.flash_method or args.deviceinfo["flash_method"] device = context.device
deviceinfo = pmb.parse.deviceinfo()
method = args.flash_method or deviceinfo["flash_method"]
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"]:
@ -153,18 +145,20 @@ def frontend(args: PmbArgs):
return return
if action in ["boot", "flash_kernel"]: if action in ["boot", "flash_kernel"]:
kernel(args) kernel(device, deviceinfo)
elif action == "flash_rootfs": elif action == "flash_rootfs":
rootfs(args) rootfs(device, deviceinfo, method)
elif action == "flash_vbmeta": elif action == "flash_vbmeta":
flash_vbmeta(args) logging.info("(native) flash vbmeta.img with verity disabled flag")
pmb.flasher.run(device, deviceinfo, "flash_vbmeta")
elif action == "flash_dtbo": elif action == "flash_dtbo":
flash_dtbo(args) logging.info("(native) flash dtbo image")
pmb.flasher.run(device, deviceinfo, "flash_dtbo")
elif action == "flash_lk2nd": elif action == "flash_lk2nd":
flash_lk2nd(args) flash_lk2nd(device, deviceinfo, method)
elif action == "list_flavors": elif action == "list_flavors":
list_flavors(args) list_flavors(device)
elif action == "list_devices": elif action == "list_devices":
list_devices(args) list_devices(device, deviceinfo)
elif action == "sideload": elif action == "sideload":
sideload(args) sideload(device, deviceinfo)

View file

@ -5,15 +5,18 @@ import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.mount import pmb.helpers.mount
import pmb.helpers.args
from pmb.helpers.mount import mount_device_rootfs from pmb.helpers.mount import mount_device_rootfs
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def install_depends(args: PmbArgs): def install_depends() -> None:
args: PmbArgs = pmb.helpers.args.please_i_really_need_args()
if hasattr(args, 'flash_method'): if hasattr(args, 'flash_method'):
method = args.flash_method or args.deviceinfo["flash_method"] method = args.flash_method
else:
method = args.deviceinfo["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"
@ -43,12 +46,12 @@ def install_depends(args: PmbArgs):
pmb.chroot.apk.install(depends, Chroot.native()) pmb.chroot.apk.install(depends, Chroot.native())
def init(args: PmbArgs): def init(device: str):
install_depends(args) install_depends()
# 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:
pmb.helpers.mount.bind(folder, Chroot.native() / folder) pmb.helpers.mount.bind(folder, Chroot.native() / folder)
# Mount device chroot inside native chroot (required for kernel/ramdisk) # Mount device chroot inside native chroot (required for kernel/ramdisk)
mount_device_rootfs(args, Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)) mount_device_rootfs(Chroot(ChrootType.ROOTFS, device))

View file

@ -1,26 +1,31 @@
# 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.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
def check_partition_blacklist(args: PmbArgs, key, value): def check_partition_blacklist(args: PmbArgs, deviceinfo: Dict[str, str], key, value):
if not key.startswith("$PARTITION_"): if not key.startswith("$PARTITION_"):
return return
name = args.deviceinfo["name"] name = deviceinfo["name"]
if value in args.deviceinfo["partition_blacklist"].split(","): if value in deviceinfo["partition_blacklist"].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(args: PmbArgs, action, flavor=None): def run(device: str, deviceinfo: Dict[str, str], action, flavor=None):
pmb.flasher.init(args) pmb.flasher.init(device)
# 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 args.deviceinfo["flash_method"] 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")
@ -74,7 +79,7 @@ def run(args: PmbArgs, 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, key, value) check_partition_blacklist(args, 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

@ -2,15 +2,18 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import Optional from typing import Optional
import pmb.config.pmaports import pmb.config.pmaports
from pmb.core.context import get_context
from pmb.types import PmbArgs from pmb.types import PmbArgs
def variables(args: PmbArgs, flavor, method): def variables(args: PmbArgs, flavor, method):
_cmdline = args.deviceinfo["kernel_cmdline"] or "" device = get_context().config.device
deviceinfo = pmb.parse.deviceinfo()
_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 = args.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.
@ -20,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 = args.deviceinfo["flash_fastboot_partition_kernel"]\ _partition_kernel = deviceinfo["flash_fastboot_partition_kernel"]\
or "boot" or "boot"
_partition_rootfs = args.deviceinfo["flash_fastboot_partition_rootfs"]\ _partition_rootfs = deviceinfo["flash_fastboot_partition_rootfs"]\
or args.deviceinfo["flash_fastboot_partition_system"] or "userdata" or deviceinfo["flash_fastboot_partition_system"] or "userdata"
_partition_vbmeta = args.deviceinfo["flash_fastboot_partition_vbmeta"]\ _partition_vbmeta = deviceinfo["flash_fastboot_partition_vbmeta"]\
or None or None
_partition_dtbo = args.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 = args.deviceinfo["flash_rk_partition_kernel"]\ _partition_kernel = deviceinfo["flash_rk_partition_kernel"]\
or None or None
_partition_rootfs = args.deviceinfo["flash_rk_partition_rootfs"]\ _partition_rootfs = deviceinfo["flash_rk_partition_rootfs"]\
or args.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 = args.deviceinfo["flash_mtkclient_partition_kernel"]\ _partition_kernel = deviceinfo["flash_mtkclient_partition_kernel"]\
or "boot" or "boot"
_partition_rootfs = args.deviceinfo["flash_mtkclient_partition_rootfs"]\ _partition_rootfs = deviceinfo["flash_mtkclient_partition_rootfs"]\
or "userdata" or "userdata"
_partition_vbmeta = args.deviceinfo["flash_mtkclient_partition_vbmeta"]\ _partition_vbmeta = deviceinfo["flash_mtkclient_partition_vbmeta"]\
or None or None
_partition_dtbo = args.deviceinfo["flash_mtkclient_partition_dtbo"]\ _partition_dtbo = deviceinfo["flash_mtkclient_partition_dtbo"]\
or None or None
else: else:
_partition_kernel = args.deviceinfo["flash_heimdall_partition_kernel"]\ _partition_kernel = deviceinfo["flash_heimdall_partition_kernel"]\
or "KERNEL" or "KERNEL"
_partition_rootfs = args.deviceinfo["flash_heimdall_partition_rootfs"]\ _partition_rootfs = deviceinfo["flash_heimdall_partition_rootfs"]\
or args.deviceinfo["flash_heimdall_partition_system"] or "SYSTEM" or deviceinfo["flash_heimdall_partition_system"] or "SYSTEM"
_partition_vbmeta = args.deviceinfo["flash_heimdall_partition_vbmeta"]\ _partition_vbmeta = deviceinfo["flash_heimdall_partition_vbmeta"]\
or None or None
_partition_dtbo = args.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:
@ -64,7 +67,7 @@ def variables(args: PmbArgs, flavor, method):
_partition_dtbo = args.partition _partition_dtbo = args.partition
_dtb = "" _dtb = ""
if args.deviceinfo["append_dtb"] == "true": if deviceinfo["append_dtb"] == "true":
_dtb = "-dtb" _dtb = "-dtb"
_no_reboot = "" _no_reboot = ""
@ -76,23 +79,23 @@ def variables(args: PmbArgs, flavor, method):
_resume = "--resume" _resume = "--resume"
vars = { vars = {
"$BOOT": "/mnt/rootfs_" + args.devicesdhbfvhubsud + "/boot", "$BOOT": "/mnt/rootfs_" + device + "/boot",
"$DTB": _dtb, "$DTB": _dtb,
"$IMAGE_SPLIT_BOOT": "/home/pmos/rootfs/" + args.devicesdhbfvhubsud + "-boot.img", "$IMAGE_SPLIT_BOOT": "/home/pmos/rootfs/" + device + "-boot.img",
"$IMAGE_SPLIT_ROOT": "/home/pmos/rootfs/" + args.devicesdhbfvhubsud + "-root.img", "$IMAGE_SPLIT_ROOT": "/home/pmos/rootfs/" + device + "-root.img",
"$IMAGE": "/home/pmos/rootfs/" + args.devicesdhbfvhubsud + ".img", "$IMAGE": "/home/pmos/rootfs/" + device + ".img",
"$KERNEL_CMDLINE": _cmdline, "$KERNEL_CMDLINE": _cmdline,
"$PARTITION_KERNEL": _partition_kernel, "$PARTITION_KERNEL": _partition_kernel,
"$PARTITION_INITFS": args.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_" + args.deviceinfo["arch"] + "$RECOVERY_ZIP": "/mnt/buildroot_" + deviceinfo["arch"] +
"/var/lib/postmarketos-android-recovery-installer" "/var/lib/postmarketos-android-recovery-installer"
"/pmos-" + args.devicesdhbfvhubsud + ".zip", "/pmos-" + device + ".zip",
"$UUU_SCRIPT": "/mnt/rootfs_" + args.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

@ -4,7 +4,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import List, Sequence from typing import List, Sequence
import pmb.chroot.run import pmb.chroot
import pmb.config.pmaports import pmb.config.pmaports
from pmb.types import PathString, PmbArgs from pmb.types import PathString, PmbArgs
import pmb.helpers.cli import pmb.helpers.cli

View file

@ -275,7 +275,7 @@ def upgrade(args: PmbArgs, pkgname, git=True, stable=True) -> None:
def upgrade_all(args: PmbArgs) -> None: def upgrade_all(args: PmbArgs) -> None:
"""Upgrade all packages, based on args.all, args.all_git and args.all_stable.""" """Upgrade all packages, based on args.all, args.all_git and args.all_stable."""
for pkgname in pmb.helpers.pmaports.get_list(args): for pkgname in pmb.helpers.pmaports.get_list():
# Always ignore postmarketOS-specific packages that have no upstream # Always ignore postmarketOS-specific packages that have no upstream
# source # source
skip = False skip = False

View file

@ -48,7 +48,7 @@ __args: PmbArgs = PmbArgs()
code as well. code as well.
Examples: Examples:
args.deviceinfo (e.g. {"name": "Mydevice", "arch": "armhf", ...}) deviceinfo (e.g. {"name": "Mydevice", "arch": "armhf", ...})
""" """
@ -82,17 +82,17 @@ def check_pmaports_path(args: PmbArgs):
# setattr(args, key, Path(getattr(args, key)).expanduser()) # setattr(args, key, Path(getattr(args, key)).expanduser())
def add_deviceinfo(args: PmbArgs):
"""Add and verify the deviceinfo (only after initialization)"""
setattr(args, "deviceinfo", pmb.parse.deviceinfo())
def init(args: PmbArgs) -> PmbArgs: def init(args: PmbArgs) -> PmbArgs:
global __args global __args
# Basic initialization # Basic initialization
config = pmb.config.load(args) config = pmb.config.load(args)
# pmb.config.merge_with_args(args)
# replace_placeholders(args) for key, value in vars(args).items():
if key.startswith("_"):
continue
if getattr(args, key, None) and hasattr(config, key):
print(f"Overriding config.{key} with {value}")
setattr(config, key, value)
# Configure runtime context # Configure runtime context
context = Context(config) context = Context(config)
@ -102,6 +102,8 @@ def init(args: PmbArgs) -> PmbArgs:
context.offline = args.offline context.offline = args.offline
context.command = args.action context.command = args.action
context.cross = args.cross context.cross = args.cross
context.assume_yes = getattr(args, "assume_yes", False)
context.force = getattr(args, "force", False)
if args.mirrors_postmarketos: if args.mirrors_postmarketos:
context.config.mirrors_postmarketos = args.mirrors_postmarketos context.config.mirrors_postmarketos = args.mirrors_postmarketos
if args.mirror_alpine: if args.mirror_alpine:
@ -121,9 +123,9 @@ def init(args: PmbArgs) -> PmbArgs:
if args.action not in ["init", "checksum", "config", "bootimg_analyze", "log", if args.action not in ["init", "checksum", "config", "bootimg_analyze", "log",
"pull", "shutdown", "zap"]: "pull", "shutdown", "zap"]:
pmb.config.pmaports.read_config() pmb.config.pmaports.read_config()
add_deviceinfo(args)
pmb.helpers.git.parse_channels_cfg(config.aports) pmb.helpers.git.parse_channels_cfg(config.aports)
context.device_arch = args.deviceinfo["arch"] deviceinfo = pmb.parse.deviceinfo()
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")
@ -134,6 +136,10 @@ def init(args: PmbArgs) -> PmbArgs:
delattr(args, "aports") delattr(args, "aports")
delattr(args, "mirrors_postmarketos") delattr(args, "mirrors_postmarketos")
delattr(args, "mirror_alpine") delattr(args, "mirror_alpine")
if hasattr(args, "force"):
delattr(args, "force")
if hasattr(args, "device"):
delattr(args, "device")
# args.work is deprecated! # args.work is deprecated!
delattr(args, "work") delattr(args, "work")
@ -142,7 +148,7 @@ def init(args: PmbArgs) -> PmbArgs:
if not key.startswith("_") and not key == "from_argparse": if not key.startswith("_") and not key == "from_argparse":
setattr(__args, key, value) setattr(__args, key, value)
print(json.dumps(__args.__dict__)) #print(json.dumps(__args.__dict__))
#sys.exit(0) #sys.exit(0)

View file

@ -90,7 +90,7 @@ def is_older_than(path, seconds):
return lastmod + seconds < time.time() return lastmod + seconds < time.time()
def symlink(args: PmbArgs, file: Path, link: Path): def symlink(file: Path, link: Path):
"""Check if the symlink is already present, otherwise create it.""" """Check if the symlink is already present, otherwise create it."""
if os.path.exists(link): if os.path.exists(link):
if (os.path.islink(link) and if (os.path.islink(link) and

View file

@ -1,7 +1,7 @@
# 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 json import json
from typing import List, Sequence from typing import List, Sequence, Tuple
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
@ -40,7 +40,7 @@ import pmb.sideload
from pmb.core import ChrootType, Chroot, get_context from pmb.core import ChrootType, Chroot, get_context
def _parse_flavor(args: PmbArgs, autoinstall=True): def _parse_flavor(device: str, autoinstall=True):
"""Verify the flavor argument if specified, or return a default value. """Verify the flavor argument if specified, or return a default value.
:param autoinstall: make sure that at least one kernel flavor is installed :param autoinstall: make sure that at least one kernel flavor is installed
@ -49,7 +49,7 @@ def _parse_flavor(args: PmbArgs, autoinstall=True):
# identifier that is typically in the form # identifier that is typically in the form
# "postmarketos-<manufacturer>-<device/chip>", e.g. # "postmarketos-<manufacturer>-<device/chip>", e.g.
# "postmarketos-qcom-sdm845" # "postmarketos-qcom-sdm845"
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) chroot = Chroot(ChrootType.ROOTFS, device)
flavor = pmb.chroot.other.kernel_flavor_installed( flavor = pmb.chroot.other.kernel_flavor_installed(
chroot, autoinstall) chroot, autoinstall)
@ -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(args.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:
@ -76,14 +76,14 @@ def _parse_suffix(args: PmbArgs) -> Chroot:
return Chroot(ChrootType.NATIVE) return Chroot(ChrootType.NATIVE)
def _install_ondev_verify_no_rootfs(args: PmbArgs): def _install_ondev_verify_no_rootfs(device: str, ondev_cp: List[Tuple[str, str]]):
chroot_dest = "/var/lib/rootfs.img" chroot_dest = "/var/lib/rootfs.img"
dest = Chroot(ChrootType.INSTALLER, args.devicesdhbfvhubsud) / chroot_dest dest = Chroot(ChrootType.INSTALLER, device) / chroot_dest
if dest.exists(): if dest.exists():
return return
if args.ondev_cp: if ondev_cp:
for _, chroot_dest_cp in args.ondev_cp: for _, chroot_dest_cp in ondev_cp:
if chroot_dest_cp == chroot_dest: if chroot_dest_cp == chroot_dest:
return return
@ -112,14 +112,14 @@ def build(args: PmbArgs):
# Set src and force # Set src and force
src = os.path.realpath(os.path.expanduser(args.src[0])) \ src = os.path.realpath(os.path.expanduser(args.src[0])) \
if args.src else None if args.src else None
force = True if src else args.force force = True if src else get_context().force
if src and not os.path.exists(src): if src and not os.path.exists(src):
raise RuntimeError("Invalid path specified for --src: " + src) raise RuntimeError("Invalid path specified for --src: " + src)
# 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(args, package)
pmb.helpers.repo_bootstrap.require_bootstrap(args, 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()
@ -134,8 +134,8 @@ def build(args: PmbArgs):
def build_init(args: PmbArgs): def build_init(args: PmbArgs):
suffix = _parse_suffix(args) chroot = _parse_suffix(args)
pmb.build.init(args, suffix) pmb.build.init(chroot)
def checksum(args: PmbArgs): def checksum(args: PmbArgs):
@ -148,7 +148,7 @@ def checksum(args: PmbArgs):
def sideload(args: PmbArgs): def sideload(args: PmbArgs):
arch = args.arch arch = args.arch
user = args.user user = get_context().config.user
host = args.host host = args.host
pmb.sideload.sideload(args, user, host, args.port, arch, args.install_key, pmb.sideload.sideload(args, user, host, args.port, arch, args.install_key,
args.packages) args.packages)
@ -156,25 +156,27 @@ def sideload(args: PmbArgs):
def netboot(args: PmbArgs): def netboot(args: PmbArgs):
if args.action_netboot == "serve": if args.action_netboot == "serve":
pmb.netboot.start_nbd_server(args) device = get_context().config.device
pmb.netboot.start_nbd_server(device, args.replace)
def chroot(args: PmbArgs): def chroot(args: PmbArgs):
# Suffix # Suffix
suffix = _parse_suffix(args) chroot = _parse_suffix(args)
if (args.user and suffix != Chroot.native() and user = get_context().config.user
not suffix.type == ChrootType.BUILDROOT): if (user and chroot != Chroot.native() and
not chroot.type == ChrootType.BUILDROOT):
raise RuntimeError("--user is only supported for native or" raise RuntimeError("--user is only supported for native or"
" buildroot_* chroots.") " buildroot_* chroots.")
if args.xauth and suffix != Chroot.native(): if args.xauth and chroot != Chroot.native():
raise RuntimeError("--xauth is only supported for native chroot.") raise RuntimeError("--xauth is only supported for native chroot.")
# apk: check minimum version, install packages # apk: check minimum version, install packages
pmb.chroot.apk.check_min_version(suffix) pmb.chroot.apk.check_min_version(chroot)
if args.add: if args.add:
pmb.chroot.apk.install(args.add.split(","), suffix) pmb.chroot.apk.install(args.add.split(","), chroot)
pmb.chroot.init(suffix) pmb.chroot.init(chroot)
# Xauthority # Xauthority
env = {} env = {}
@ -196,14 +198,14 @@ def chroot(args: PmbArgs):
size_root, size_reserve) size_root, size_reserve)
# Run the command as user/root # Run the command as user/root
if args.user: if user:
logging.info(f"({suffix}) % su pmos -c '" + logging.info(f"({chroot}) % su pmos -c '" +
" ".join(args.command) + "'") " ".join(args.command) + "'")
pmb.chroot.user(args.command, suffix, output=args.output, pmb.chroot.user(args.command, chroot, output=args.output,
env=env) env=env)
else: else:
logging.info(f"({suffix}) % " + " ".join(args.command)) logging.info(f"({chroot}) % " + " ".join(args.command))
pmb.chroot.root(args.command, suffix, output=args.output, pmb.chroot.root(args.command, chroot, output=args.output,
env=env) env=env)
@ -219,7 +221,7 @@ def config(args: PmbArgs):
raise RuntimeError("config --reset requires a name to be given.") raise RuntimeError("config --reset requires a name to be given.")
def_value = getattr(Config(), args.name) def_value = getattr(Config(), args.name)
setattr(config, args.name, def_value) setattr(config, args.name, def_value)
logging.info(f"Config changed to default: {args.name}='{value}'") logging.info(f"Config changed to default: {args.name}='{def_value}'")
pmb.config.save(args.config, config) pmb.config.save(args.config, config)
elif args.value is not None: elif args.value is not None:
setattr(config, args.name, args.value) setattr(config, args.name, args.value)
@ -258,6 +260,9 @@ def initfs(args: PmbArgs):
def install(args: PmbArgs): def install(args: PmbArgs):
config = get_context().config
device = config.device
deviceinfo = pmb.parse.deviceinfo(device)
if args.no_fde: if args.no_fde:
logging.warning("WARNING: --no-fde is deprecated," logging.warning("WARNING: --no-fde is deprecated,"
" as it is now the default.") " as it is now the default.")
@ -271,8 +276,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(args, args.deviceinfo["arch"], pmb.helpers.repo_bootstrap.require_bootstrap(deviceinfo["arch"],
f"do 'pmbootstrap install' for {args.deviceinfo['arch']}" f"do 'pmbootstrap install' for {deviceinfo['arch']}"
" (deviceinfo_arch)") " (deviceinfo_arch)")
# On-device installer checks # On-device installer checks
@ -296,7 +301,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 args.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:
@ -306,7 +311,7 @@ def install(args: PmbArgs):
raise ValueError("--no-rootfs can only be combined with --ondev." raise ValueError("--no-rootfs can only be combined with --ondev."
" Do you mean --no-image?") " Do you mean --no-image?")
if args.ondev_no_rootfs: if args.ondev_no_rootfs:
_install_ondev_verify_no_rootfs(args) _install_ondev_verify_no_rootfs(device, args.ondev_cp)
# On-device installer overrides # On-device installer overrides
if args.on_device_installer: if args.on_device_installer:
@ -315,15 +320,15 @@ def install(args: PmbArgs):
# a password for the user, disable SSH password authentication, # a password for the user, disable SSH password authentication,
# optionally add a new user for SSH that must not have the same # optionally add a new user for SSH that must not have the same
# username etc.) # username etc.)
if args.user != "user": if config.user != "user":
logging.warning(f"WARNING: custom username '{args.user}' will be" logging.warning(f"WARNING: custom username '{config.user}' will be"
" replaced with 'user' for the on-device" " replaced with 'user' for the on-device"
" installer.") " installer.")
args.user = "user" config.user = "user"
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(args.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
@ -345,10 +350,10 @@ def install(args: PmbArgs):
if not args.install_local_pkgs: if not args.install_local_pkgs:
# Implies that we don't build outdated packages (overriding the answer # Implies that we don't build outdated packages (overriding the answer
# in 'pmbootstrap init') # in 'pmbootstrap init')
args.build_pkgs_on_install = False config.build_pkgs_on_install = False
# Safest way to avoid installing local packages is having none # Safest way to avoid installing local packages is having none
if (get_context().config.work / "packages").glob("*"): if (config.work / "packages").glob("*"):
raise ValueError("--no-local-pkgs specified, but locally built" raise ValueError("--no-local-pkgs specified, but locally built"
" packages found. Consider 'pmbootstrap zap -p'" " packages found. Consider 'pmbootstrap zap -p'"
" to delete them.") " to delete them.")
@ -443,7 +448,7 @@ def kconfig(args: PmbArgs):
skipped = 0 skipped = 0
packages.sort() packages.sort()
for package in packages: for package in packages:
if not args.force: if not get_context().force:
pkgname = package if package.startswith("linux-") \ pkgname = package if package.startswith("linux-") \
else "linux-" + package else "linux-" + package
aport = pmb.helpers.pmaports.find(pkgname) aport = pmb.helpers.pmaports.find(pkgname)
@ -451,7 +456,7 @@ def kconfig(args: PmbArgs):
if "!pmb:kconfigcheck" in apkbuild["options"]: if "!pmb:kconfigcheck" in apkbuild["options"]:
skipped += 1 skipped += 1
continue continue
if not pmb.parse.kconfig.check(args, package, components_list, if not pmb.parse.kconfig.check(package, components_list,
details=details): details=details):
error = True error = True
@ -467,7 +472,7 @@ def kconfig(args: PmbArgs):
if args.package: if args.package:
pkgname = args.package if isinstance(args.package, str) else args.package[0] pkgname = args.package if isinstance(args.package, str) else args.package[0]
else: else:
pkgname = args.deviceinfo["codename"] pkgname = get_context().config.device
use_oldconfig = args.action_kconfig == "migrate" use_oldconfig = args.action_kconfig == "migrate"
pmb.build.menuconfig(args, pkgname, use_oldconfig) pmb.build.menuconfig(args, pkgname, use_oldconfig)
@ -577,7 +582,7 @@ def zap(args: PmbArgs):
def bootimg_analyze(args: PmbArgs): def bootimg_analyze(args: PmbArgs):
bootimg = pmb.parse.bootimg(args, args.path) bootimg = pmb.parse.bootimg(args.path)
tmp_output = "Put these variables in the deviceinfo file of your device:\n" tmp_output = "Put these variables in the deviceinfo file of your device:\n"
for line in pmb.aportgen.device.\ for line in pmb.aportgen.device.\
generate_deviceinfo_fastboot_content(bootimg).split("\n"): generate_deviceinfo_fastboot_content(bootimg).split("\n"):
@ -667,4 +672,4 @@ def ci(args: PmbArgs):
if not scripts_selected: if not scripts_selected:
scripts_selected = pmb.ci.ask_which_scripts_to_run(scripts_available) scripts_selected = pmb.ci.ask_which_scripts_to_run(scripts_available)
pmb.ci.run_scripts(args, topdir, scripts_selected) pmb.ci.run_scripts(topdir, scripts_selected)

View file

@ -248,8 +248,11 @@ def get_topdir(repo: Path):
:returns: a string with the top dir of the git repository, :returns: a string with the top dir of the git repository,
or an empty string if it's not a git repository. or an empty string if it's not a git repository.
""" """
return pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"], res = pmb.helpers.run.user(["git", "rev-parse", "--show-toplevel"],
repo, output_return=True, check=False).rstrip() repo, output_return=True, check=False)
if not isinstance(res, str):
raise RuntimeError("Not a git repository: " + str(repo))
return res.strip()
def get_files(repo: Path): def get_files(repo: Path):

View file

@ -1,6 +1,7 @@
# Copyright 2023 Danct12 <danct12@disroot.org> # Copyright 2023 Danct12 <danct12@disroot.org>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path from pathlib import Path
from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
import os import os
@ -17,12 +18,13 @@ def check(args: PmbArgs, pkgnames):
:param pkgnames: Names of the packages to lint :param pkgnames: Names of the packages to lint
""" """
pmb.chroot.apk.install(["atools"]) chroot = Chroot.native()
pmb.chroot.apk.install(["atools"], chroot)
# Mount pmaports.git inside the chroot so that we don't have to copy the # Mount pmaports.git inside the chroot so that we don't have to copy the
# package folders # package folders
pmaports = Path("/mnt/pmaports") pmaports = Path("/mnt/pmaports")
pmb.build.mount_pmaports(args, pmaports) pmb.build.mount_pmaports(pmaports, chroot)
# Locate all APKBUILDs and make the paths be relative to the pmaports # Locate all APKBUILDs and make the paths be relative to the pmaports
# root # root

View file

@ -10,7 +10,9 @@ See also:
""" """
import copy import copy
from typing import Any, Dict from typing import Any, Dict
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
import pmb.build._package
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -62,7 +64,7 @@ def get(pkgname, arch, replace_subpkgnames=False, must_exist=True):
pmaport = pmb.helpers.pmaports.get(pkgname, False) pmaport = pmb.helpers.pmaports.get(pkgname, False)
if pmaport: if pmaport:
ret = {"arch": pmaport["arch"], ret = {"arch": pmaport["arch"],
"depends": pmb.build._package.get_depends(args, pmaport), "depends": pmb.build._package.get_depends(get_context(), pmaport),
"pkgname": pmaport["pkgname"], "pkgname": pmaport["pkgname"],
"provides": pmaport["provides"], "provides": pmaport["provides"],
"version": pmaport["pkgver"] + "-r" + pmaport["pkgrel"]} "version": pmaport["pkgver"] + "-r" + pmaport["pkgrel"]}

View file

@ -91,7 +91,7 @@ def auto_apkindex_package(args: PmbArgs, arch, aport, apk, dry=False):
# (which means dynamic libraries that the package was linked # (which means dynamic libraries that the package was linked
# against) and packages for which no aport exists. # against) and packages for which no aport exists.
if (depend.startswith("so:") or if (depend.startswith("so:") or
not pmb.helpers.pmaports.find_optional(args, depend)): not pmb.helpers.pmaports.find_optional(depend)):
missing.append(depend) missing.append(depend)
# Increase pkgrel # Increase pkgrel
@ -116,7 +116,7 @@ def auto(args: PmbArgs, dry=False):
logging.verbose( logging.verbose(
f"{pkgname}: origin '{origin}' found again") f"{pkgname}: origin '{origin}' found again")
continue continue
aport_path = pmb.helpers.pmaports.find_optional(args, origin) aport_path = pmb.helpers.pmaports.find_optional(origin)
if not aport_path: if not aport_path:
logging.warning("{}: origin '{}' aport not found".format( logging.warning("{}: origin '{}' aport not found".format(
pkgname, origin)) pkgname, origin))

View file

@ -58,6 +58,7 @@ def urls(user_repository=True, postmarketos_mirror=True, alpine=True):
"http://...", ...] "http://...", ...]
""" """
ret: List[str] = [] ret: List[str] = []
context = get_context()
# Get mirrordirs from channels.cfg (postmarketOS mirrordir is the same as # Get mirrordirs from channels.cfg (postmarketOS mirrordir is the same as
# the pmaports branch of the channel, no need to make it more complicated) # the pmaports branch of the channel, no need to make it more complicated)
@ -68,9 +69,9 @@ def urls(user_repository=True, postmarketos_mirror=True, alpine=True):
# Local user repository (for packages compiled with pmbootstrap) # Local user repository (for packages compiled with pmbootstrap)
if user_repository: if user_repository:
channel = pmb.config.pmaports.read_config()["channel"] channel = pmb.config.pmaports.read_config()["channel"]
ret.append(str(get_context().config.work / "packages" / channel)) # FIXME: We shouldn't hardcod this here
ret.append("/mnt/pmbootstrap/packages")
context = get_context()
# Upstream postmarketOS binary repository # Upstream postmarketOS binary repository
if postmarketos_mirror: if postmarketos_mirror:
for mirror in context.config.mirrors_postmarketos: for mirror in context.config.mirrors_postmarketos:

View file

@ -20,7 +20,7 @@ def get_arch(args: PmbArgs):
return args.arch return args.arch
if args.build_default_device_arch: if args.build_default_device_arch:
return args.deviceinfo["arch"] return pmb.parse.deviceinfo()["arch"]
return pmb.config.arch_native return pmb.config.arch_native
@ -142,7 +142,7 @@ def run_steps(args: PmbArgs, steps, arch, chroot: Chroot):
for package in get_packages(bootstrap_line): for package in get_packages(bootstrap_line):
log_progress(f"building {package}") log_progress(f"building {package}")
bootstrap_stage = int(step.split("bootstrap_", 1)[1]) bootstrap_stage = int(step.split("bootstrap_", 1)[1])
pmb.build.package(args, package, arch, force=True, pmb.build.package(package, arch, force=True,
strict=True, bootstrap_stage=bootstrap_stage) strict=True, bootstrap_stage=bootstrap_stage)
log_progress("bootstrap complete!") log_progress("bootstrap complete!")
@ -175,7 +175,7 @@ def require_bootstrap_error(repo, arch, trigger_str):
" and then try again.") " and then try again.")
def require_bootstrap(args: PmbArgs, arch, trigger_str): def require_bootstrap(arch, trigger_str):
""" """
Check if repo_bootstrap was done, if any is needed. Check if repo_bootstrap was done, if any is needed.

View file

@ -33,12 +33,12 @@ def print_channel(config: Config) -> None:
print_status_line("Channel", value) print_status_line("Channel", value)
def print_device(args: PmbArgs, config: Config) -> None: def print_device(config: Config) -> None:
kernel = "" kernel = ""
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} ({args.deviceinfo['arch']}{kernel})" value = f"{config.device} ({pmb.parse.deviceinfo()['arch']}{kernel})"
print_status_line("Device", value) print_status_line("Device", value)
@ -56,6 +56,6 @@ def print_status(args: PmbArgs) -> None:
:returns: True if all checks passed, False otherwise """ :returns: True if all checks passed, False otherwise """
config = get_context().config config = get_context().config
print_channel(config) print_channel(config)
print_device(args, config) print_device(config)
print_ui(config) print_ui(config)
print_systemd(config) print_systemd(config)

View file

@ -9,7 +9,7 @@ import pmb.helpers.package
import pmb.parse import pmb.parse
def list_ui(args: PmbArgs, arch): def list_ui(arch):
"""Get all UIs, for which aports are available with their description. """Get all UIs, for which aports are available with their description.
:param arch: device architecture, for which the UIs must be available :param arch: device architecture, for which the UIs must be available
@ -22,7 +22,7 @@ def list_ui(args: PmbArgs, arch):
for path in sorted(context.config.aports.glob("main/postmarketos-ui-*")): for path in sorted(context.config.aports.glob("main/postmarketos-ui-*")):
apkbuild = pmb.parse.apkbuild(path) apkbuild = pmb.parse.apkbuild(path)
ui = os.path.basename(path).split("-", 2)[2] ui = os.path.basename(path).split("-", 2)[2]
if pmb.helpers.package.check_arch(args, apkbuild["pkgname"], arch): if pmb.helpers.package.check_arch(apkbuild["pkgname"], arch):
ret.append((ui, apkbuild["pkgdesc"])) ret.append((ui, apkbuild["pkgdesc"]))
return ret return ret

View file

@ -21,6 +21,7 @@ import pmb.helpers.devices
from pmb.helpers.mount import mount_device_rootfs from pmb.helpers.mount import mount_device_rootfs
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.other import pmb.helpers.other
import pmb.helpers.package
import pmb.install.blockdevice import pmb.install.blockdevice
import pmb.install.recovery import pmb.install.recovery
import pmb.install.ui import pmb.install.ui
@ -113,11 +114,11 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot):
""" """
# Mount the device rootfs # Mount the device rootfs
logging.info(f"(native) copy {chroot} to /mnt/install/") logging.info(f"(native) copy {chroot} to /mnt/install/")
mountpoint = mount_device_rootfs(args, chroot) mountpoint = mount_device_rootfs(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(args.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])
@ -148,16 +149,16 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot):
working_dir=mountpoint) working_dir=mountpoint)
def create_home_from_skel(args: PmbArgs): def create_home_from_skel(filesystem: str, user: str):
""" """
Create /home/{user} from /etc/skel Create /home/{user} from /etc/skel
""" """
rootfs = (Chroot.native() / "mnt/install") rootfs = (Chroot.native() / "mnt/install")
# In btrfs, home subvol & home dir is created in format.py # In btrfs, home subvol & home dir is created in format.py
if args.filesystem != "btrfs": if filesystem != "btrfs":
pmb.helpers.run.root(["mkdir", rootfs / "home"]) pmb.helpers.run.root(["mkdir", rootfs / "home"])
home = (rootfs / "home" / args.user) home = (rootfs / "home" / user)
if (rootfs / "etc/skel").exists(): if (rootfs / "etc/skel").exists():
pmb.helpers.run.root(["cp", "-a", (rootfs / "etc/skel"), home]) pmb.helpers.run.root(["cp", "-a", (rootfs / "etc/skel"), home])
else: else:
@ -184,8 +185,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(args, index_files = pmb.helpers.repo.apkindex_files(arch=pmb.parse.deviceinfo()["arch"],
arch=args.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/"])
@ -221,7 +221,7 @@ def set_user(config: Config):
pmb.chroot.root(["addgroup", config.user, group], chroot) pmb.chroot.root(["addgroup", config.user, group], chroot)
def setup_login_chpasswd_user_from_arg(args: PmbArgs, chroot: Chroot): def setup_login_chpasswd_user_from_arg(args: PmbArgs, user: str, chroot: Chroot):
""" """
Set the user's password from what the user passed as --password. Make an Set the user's password from what the user passed as --password. Make an
effort to not have the password end up in the log file by writing it to effort to not have the password end up in the log file by writing it to
@ -237,7 +237,7 @@ def setup_login_chpasswd_user_from_arg(args: PmbArgs, chroot: Chroot):
path_outside = chroot / path path_outside = chroot / path
with open(path_outside, "w", encoding="utf-8") as handle: with open(path_outside, "w", encoding="utf-8") as handle:
handle.write(f"{args.user}:{args.password}") handle.write(f"{user}:{args.password}")
pmb.chroot.root(["sh", "-c", f"cat {shlex.quote(path)} | chpasswd"], pmb.chroot.root(["sh", "-c", f"cat {shlex.quote(path)} | chpasswd"],
chroot) chroot)
@ -258,7 +258,7 @@ def is_root_locked(chroot: Chroot):
return shadow_root.startswith("root:!:") return shadow_root.startswith("root:!:")
def setup_login(args: PmbArgs, chroot: Chroot): def setup_login(args: PmbArgs, config: Config, chroot: Chroot):
""" """
Loop until the password for user has been set successfully, and disable Loop until the password for user has been set successfully, and disable
root login. root login.
@ -268,13 +268,13 @@ def setup_login(args: PmbArgs, chroot: Chroot):
""" """
if not args.on_device_installer: if not args.on_device_installer:
# User password # User password
logging.info(f" *** SET LOGIN PASSWORD FOR: '{args.user}' ***") logging.info(f" *** SET LOGIN PASSWORD FOR: '{config.user}' ***")
if args.password: if args.password:
setup_login_chpasswd_user_from_arg(args, chroot) setup_login_chpasswd_user_from_arg(args, config.user, chroot)
else: else:
while True: while True:
try: try:
pmb.chroot.root(["passwd", args.user], chroot, pmb.chroot.root(["passwd", config.user], chroot,
output="interactive") output="interactive")
break break
except RuntimeError: except RuntimeError:
@ -363,11 +363,9 @@ def setup_keymap(config: Config):
logging.info("NOTE: No valid keymap specified for device") logging.info("NOTE: No valid keymap specified for device")
def setup_timezone(config: Config): def setup_timezone(chroot: Chroot, timezone: str):
suffix = Chroot(ChrootType.ROOTFS, config.device) # We don't care about the arch since it's built for all!
alpine_conf = pmb.helpers.package.get("alpine-conf", pmb.config.arch_native)
arch = args.deviceinfo["arch"]
alpine_conf = pmb.helpers.package.get(args, "alpine-conf", arch)
version = alpine_conf["version"].split("-r")[0] version = alpine_conf["version"].split("-r")[0]
setup_tz_cmd = ["setup-timezone"] setup_tz_cmd = ["setup-timezone"]
@ -375,23 +373,22 @@ def setup_timezone(config: Config):
# and disregard tzdata, to save space. If we actually have tzdata # and disregard tzdata, to save space. If we actually have tzdata
# installed, make sure that setup-timezone makes use of it, since # installed, make sure that setup-timezone makes use of it, since
# there's no space to be saved. # there's no space to be saved.
if "tzdata" in pmb.chroot.apk.installed(args, suffix): if "tzdata" in pmb.chroot.apk.installed(chroot):
setup_tz_cmd += ["-i"] setup_tz_cmd += ["-i"]
if not pmb.parse.version.check_string(version, ">=3.14.0"): if not pmb.parse.version.check_string(version, ">=3.14.0"):
setup_tz_cmd += ["-z"] setup_tz_cmd += ["-z"]
setup_tz_cmd += [args.timezone] setup_tz_cmd += [timezone]
pmb.chroot.root(setup_tz_cmd, suffix) pmb.chroot.root(setup_tz_cmd, chroot)
def setup_hostname(args: PmbArgs): def setup_hostname(device: str, hostname: Optional[str]):
""" """
Set the hostname and update localhost address in /etc/hosts Set the hostname and update localhost address in /etc/hosts
""" """
# Default to device name. If device name is not a valid hostname then # Default to device name. If device name is not a valid hostname then
# default to a static default. # default to a static default.
hostname = args.hostname
if not hostname: if not hostname:
hostname = args.devicesdhbfvhubsud hostname = device
if not pmb.helpers.other.validate_hostname(hostname): if not pmb.helpers.other.validate_hostname(hostname):
# A valid host name, see: # A valid host name, see:
# https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.1 # https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.1
@ -402,7 +399,7 @@ def setup_hostname(args: PmbArgs):
raise RuntimeError("Hostname '" + hostname + "' is not valid, please" raise RuntimeError("Hostname '" + hostname + "' is not valid, please"
" run 'pmbootstrap init' to configure it.") " run 'pmbootstrap init' to configure it.")
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) suffix = Chroot(ChrootType.ROOTFS, device)
# Generate /etc/hostname # Generate /etc/hostname
pmb.chroot.root(["sh", "-c", "echo " + shlex.quote(hostname) + pmb.chroot.root(["sh", "-c", "echo " + shlex.quote(hostname) +
" > /etc/hostname"], suffix) " > /etc/hostname"], suffix)
@ -412,37 +409,32 @@ def setup_hostname(args: PmbArgs):
pmb.chroot.root(["sed", "-i", "-e", regex, "/etc/hosts"], suffix) pmb.chroot.root(["sed", "-i", "-e", regex, "/etc/hosts"], suffix)
def setup_appstream(args: PmbArgs): def setup_appstream(offline: bool, chroot: Chroot):
""" """
If alpine-appstream-downloader has been downloaded, execute it to have If alpine-appstream-downloader has been downloaded, execute it to have
update AppStream data on new installs update AppStream data on new installs
""" """
suffix = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) installed_pkgs = pmb.chroot.apk.installed(chroot)
installed_pkgs = pmb.chroot.apk.installed(args, suffix)
if "alpine-appstream-downloader" not in installed_pkgs or args.offline: if "alpine-appstream-downloader" not in installed_pkgs or offline:
return return
if not pmb.chroot.root(["alpine-appstream-downloader", if not pmb.chroot.root(["alpine-appstream-downloader",
"/mnt/appstream-data"], suffix, check=False): "/mnt/appstream-data"], chroot, check=False):
pmb.chroot.root(["mkdir", "-p", "/var/lib/swcatalog"], suffix) pmb.chroot.root(["mkdir", "-p", "/var/lib/swcatalog"], chroot)
pmb.chroot.root(["cp", "-r", "/mnt/appstream-data/icons", pmb.chroot.root(["cp", "-r", "/mnt/appstream-data/icons",
"/mnt/appstream-data/xml", "/mnt/appstream-data/xml",
"-t", "/var/lib/swcatalog"], suffix) "-t", "/var/lib/swcatalog"], chroot)
def disable_sshd(args: PmbArgs): def disable_sshd(chroot: Chroot):
if not args.no_sshd:
return
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
pmb.chroot.root(["rc-update", "del", "sshd", "default"], chroot, pmb.chroot.root(["rc-update", "del", "sshd", "default"], chroot,
check=False) check=False)
# Verify that it's gone # Verify that it's gone
sshd_files = pmb.helpers.run.root( sshd_files = pmb.helpers.run.root(
args, ["find", "-name", "sshd"], output_return=True, ["find", "-name", "sshd"], output_return=True,
working_dir=chroot / "etc/runlevels") working_dir=chroot / "etc/runlevels")
if sshd_files: if sshd_files:
raise RuntimeError(f"Failed to disable sshd service: {sshd_files}") raise RuntimeError(f"Failed to disable sshd service: {sshd_files}")
@ -457,7 +449,7 @@ def print_sshd_info(args: PmbArgs):
logging.info("SSH daemon is disabled (--no-sshd).") logging.info("SSH daemon is disabled (--no-sshd).")
else: else:
logging.info("SSH daemon is enabled (disable with --no-sshd).") logging.info("SSH daemon is enabled (disable with --no-sshd).")
logging.info(f"Login as '{args.user}' with the password given" logging.info(f"Login as '{get_context().config.user}' with the password given"
" during installation.") " during installation.")
if args.on_device_installer: if args.on_device_installer:
@ -469,24 +461,20 @@ def print_sshd_info(args: PmbArgs):
logging.info("More info: https://postmarketos.org/ondev-debug") logging.info("More info: https://postmarketos.org/ondev-debug")
def disable_firewall(args: PmbArgs): def disable_firewall(chroot: Chroot):
if not args.no_firewall:
return
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)
pmb.chroot.root(["rc-update", "del", "nftables", "default"], chroot, pmb.chroot.root(["rc-update", "del", "nftables", "default"], chroot,
check=False) check=False)
# Verify that it's gone # Verify that it's gone
nftables_files = pmb.helpers.run.root( nftables_files = pmb.helpers.run.root(
args, ["find", "-name", "nftables"], output_return=True, ["find", "-name", "nftables"], output_return=True,
working_dir=chroot / "etc/runlevels") working_dir=chroot / "etc/runlevels")
if nftables_files: if nftables_files:
raise RuntimeError(f"Failed to disable firewall: {nftables_files}") raise RuntimeError(f"Failed to disable firewall: {nftables_files}")
def print_firewall_info(args: PmbArgs): def print_firewall_info(disabled: bool, arch: str):
pmaports_cfg = pmb.config.pmaports.read_config() pmaports_cfg = pmb.config.pmaports.read_config()
pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables" pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables"
@ -494,7 +482,6 @@ def print_firewall_info(args: PmbArgs):
apkbuild_found = False apkbuild_found = False
apkbuild_has_opt = False apkbuild_has_opt = False
arch = args.deviceinfo["arch"]
kernel = get_kernel_package(get_context().config) kernel = get_kernel_package(get_context().config)
if kernel: if kernel:
kernel_apkbuild = pmb.build._package.get_apkbuild(kernel[0], arch) kernel_apkbuild = pmb.build._package.get_apkbuild(kernel[0], arch)
@ -510,7 +497,7 @@ def print_firewall_info(args: PmbArgs):
if not pmaports_ok: if not pmaports_ok:
logging.info("Firewall is not supported in checked out pmaports" logging.info("Firewall is not supported in checked out pmaports"
" branch.") " branch.")
elif args.no_firewall: elif disabled:
logging.info("Firewall is disabled (--no-firewall).") logging.info("Firewall is disabled (--no-firewall).")
elif not apkbuild_found: elif not apkbuild_found:
logging.info("Firewall is enabled, but may not work (couldn't" logging.info("Firewall is enabled, but may not work (couldn't"
@ -538,7 +525,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 = args.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(':')
@ -554,7 +541,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 = args.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:
@ -587,19 +574,19 @@ 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 args.deviceinfo["sd_embed_firmware"]: if not pmb.parse.deviceinfo()["sd_embed_firmware"]:
return return
step = 1024 step = 1024
if args.deviceinfo["sd_embed_firmware_step_size"]: if pmb.parse.deviceinfo()["sd_embed_firmware_step_size"]:
try: try:
step = int(args.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 "
"is not valid: {}".format(step)) "is not valid: {}".format(step))
device_rootfs = mount_device_rootfs(args, suffix) device_rootfs = mount_device_rootfs(suffix)
binary_list = generate_binary_list(args, suffix, step) binary_list = generate_binary_list(args, suffix, step)
# Write binaries to disk # Write binaries to disk
@ -619,13 +606,13 @@ 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 args.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(args, suffix) device_rootfs = mount_device_rootfs(suffix)
filename = f"{device_rootfs}{args.deviceinfo['cgpt_kpart']}" filename = f"{device_rootfs}{pmb.parse.deviceinfo()['cgpt_kpart']}"
pmb.chroot.root( pmb.chroot.root(
args, ["dd", f"if={filename}", f"of=/dev/installp{layout['kernel']}"]) ["dd", f"if={filename}", f"of=/dev/installp{layout['kernel']}"])
def sanity_check_boot_size(): def sanity_check_boot_size():
@ -680,7 +667,7 @@ def sanity_check_disk_size(args: PmbArgs):
def get_ondev_pkgver(args: PmbArgs): def get_ondev_pkgver(args: PmbArgs):
arch = args.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]
@ -775,7 +762,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 = args.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"
@ -821,23 +808,25 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
:param split: create separate images for boot and root partitions :param split: create separate images for boot and root partitions
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None :param disk: path to disk block device (e.g. /dev/mmcblk0) or None
""" """
config = get_context().config
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(args, True)
(size_boot, size_root) = get_subpartitions_size(chroot) (size_boot, size_root) = get_subpartitions_size(chroot)
layout = get_partition_layout(size_reserve, args.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 args.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:
pmb.install.partition(args, layout, size_boot, size_reserve) pmb.install.partition(args, layout, size_boot, size_reserve)
if not split: if not split:
pmb.install.partitions_mount(args, layout, disk) pmb.install.partitions_mount(device, layout, disk)
pmb.install.format(args, layout, boot_label, root_label, disk) pmb.install.format(args, layout, boot_label, root_label, disk)
@ -860,9 +849,9 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
# Just copy all the files # Just copy all the files
logging.info(f"*** ({step + 1}/{steps}) FILL INSTALL BLOCKDEVICE ***") logging.info(f"*** ({step + 1}/{steps}) FILL INSTALL BLOCKDEVICE ***")
copy_files_from_chroot(args, chroot) copy_files_from_chroot(args, chroot)
create_home_from_skel(args) create_home_from_skel(args.filesystem, config.user)
configure_apk(args) configure_apk(args)
copy_ssh_keys(args) copy_ssh_keys(config)
# Don't try to embed firmware and cgpt on split images since there's no # Don't try to embed firmware and cgpt on split images since there's no
# place to put it and it will end up in /dev of the chroot instead # place to put it and it will end up in /dev of the chroot instead
@ -878,26 +867,26 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
# 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 = args.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")
logging.info("(native) make sparse rootfs") logging.info("(native) make sparse rootfs")
pmb.chroot.apk.install(["android-tools"], Chroot.native()) pmb.chroot.apk.install(["android-tools"], Chroot.native())
sys_image = args.devicesdhbfvhubsud + ".img" sys_image = device + ".img"
sys_image_sparse = args.devicesdhbfvhubsud + "-sparse.img" sys_image_sparse = device + "-sparse.img"
pmb.chroot.user(["img2simg", sys_image, sys_image_sparse], pmb.chroot.user(["img2simg", sys_image, sys_image_sparse],
working_dir=workdir) working_dir=workdir)
pmb.chroot.user(["mv", "-f", sys_image_sparse, sys_image], pmb.chroot.user(["mv", "-f", sys_image_sparse, sys_image],
working_dir=workdir) working_dir=workdir)
# patch sparse image for Samsung devices if specified # patch sparse image for Samsung devices if specified
samsungify_strategy = args.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())
sys_image = f"{args.devicesdhbfvhubsud}.img" sys_image = f"{device}.img"
sys_image_patched = f"{args.devicesdhbfvhubsud}-patched.img" sys_image_patched = f"{device}-patched.img"
pmb.chroot.user(["sm_sparse_image_tool", "samsungify", "--strategy", pmb.chroot.user(["sm_sparse_image_tool", "samsungify", "--strategy",
samsungify_strategy, sys_image, sys_image_patched], samsungify_strategy, sys_image, sys_image_patched],
working_dir=workdir) working_dir=workdir)
@ -905,14 +894,14 @@ def install_system_image(args: PmbArgs, size_reserve, chroot: Chroot, step, step
working_dir=workdir) working_dir=workdir)
def print_flash_info(args: PmbArgs): def print_flash_info(device: str, deviceinfo: Dict[str, str], 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 = args.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):
@ -929,14 +918,14 @@ def print_flash_info(args: PmbArgs):
logging.info("Run the following to flash your installation to the" logging.info("Run the following to flash your installation to the"
" target device:") " target device:")
if "flash_rootfs" in flasher_actions and not args.disk and \ if "flash_rootfs" in flasher_actions and not have_disk and \
bool(args.split) == requires_split: bool(split) == requires_split:
logging.info("* pmbootstrap flasher flash_rootfs") logging.info("* pmbootstrap flasher flash_rootfs")
logging.info(" Flashes the generated rootfs image to your device:") logging.info(" Flashes the generated rootfs image to your device:")
if args.split: if split:
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / args.devicesdhbfvhubsud}-rootfs.img") logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / device}-rootfs.img")
else: else:
logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / args.devicesdhbfvhubsud}.img") logging.info(f" {Chroot.native() / 'home/pmos/rootfs' / device}.img")
logging.info(" (NOTE: This file has a partition table, which" logging.info(" (NOTE: This file has a partition table, which"
" contains /boot and / subpartitions. That way we" " contains /boot and / subpartitions. That way we"
" don't need to change the partition layout on your" " don't need to change the partition layout on your"
@ -945,16 +934,16 @@ def print_flash_info(args: PmbArgs):
# 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 \
(args.deviceinfo["flash_fastboot_partition_vbmeta"] or (deviceinfo["flash_fastboot_partition_vbmeta"] or
args.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 \
(args.deviceinfo["flash_fastboot_partition_dtbo"] or (deviceinfo["flash_fastboot_partition_dtbo"] or
args.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.")
@ -963,14 +952,14 @@ def print_flash_info(args: PmbArgs):
# works even when partitions are split or installing to disk. This is not # works even when partitions are split or installing to disk. This is not
# possible if the flash method requires split partitions. # possible if the flash method requires split partitions.
if "flash_kernel" in flasher_actions and \ if "flash_kernel" in flasher_actions and \
(not requires_split or args.split): (not requires_split or split):
logging.info("* pmbootstrap flasher flash_kernel") logging.info("* pmbootstrap flasher flash_kernel")
logging.info(" Flashes the kernel + initramfs to your device:") logging.info(" Flashes the kernel + initramfs to your device:")
if requires_split: if requires_split:
logging.info(f" {Chroot.native()}/home/pmos/rootfs/" logging.info(f" {Chroot.native()}/home/pmos/rootfs/"
f"{args.devicesdhbfvhubsud}-boot.img") f"{device}-boot.img")
else: else:
logging.info(f" {Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud)}/boot") logging.info(f" {Chroot(ChrootType.ROOTFS, device)}/boot")
if "boot" in flasher_actions: if "boot" in flasher_actions:
logging.info(" (NOTE: " + method + " also supports booting" logging.info(" (NOTE: " + method + " also supports booting"
@ -978,7 +967,7 @@ def print_flash_info(args: PmbArgs):
" Use 'pmbootstrap flasher boot' to do that.)") " Use 'pmbootstrap flasher boot' to do that.)")
if "flash_lk2nd" in flasher_actions and \ if "flash_lk2nd" in flasher_actions and \
(Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) / "/boot/lk2nd.img").exists(): (Chroot(ChrootType.ROOTFS, device) / "/boot/lk2nd.img").exists():
logging.info("* Your device supports and may even require" logging.info("* Your device supports and may even require"
" flashing lk2nd. You should flash it before" " flashing lk2nd. You should flash it before"
" flashing anything else. Use 'pmbootstrap flasher" " flashing anything else. Use 'pmbootstrap flasher"
@ -990,11 +979,11 @@ def print_flash_info(args: PmbArgs):
" and flash outside of pmbootstrap.") " and flash outside of pmbootstrap.")
def install_recovery_zip(args: PmbArgs, steps): def install_recovery_zip(args: PmbArgs, device: str, arch: str, steps):
logging.info(f"*** ({steps}/{steps}) CREATING RECOVERY-FLASHABLE ZIP ***") logging.info(f"*** ({steps}/{steps}) CREATING RECOVERY-FLASHABLE ZIP ***")
suffix = "buildroot_" + args.deviceinfo["arch"] chroot = Chroot(ChrootType.BUILDROOT, arch)
mount_device_rootfs(args, Chroot.rootfs(args.devicesdhbfvhubsud)) mount_device_rootfs(Chroot.rootfs(device))
pmb.install.recovery.create_zip(args, suffix) pmb.install.recovery.create_zip(args, chroot, device)
# Flash information # Flash information
logging.info("*** FLASHING INFORMATION ***") logging.info("*** FLASHING INFORMATION ***")
@ -1060,12 +1049,12 @@ def install_on_device_installer(args: PmbArgs, step, steps):
# Remove $DEVICE-boot.img (we will generate a new one if --split was # Remove $DEVICE-boot.img (we will generate a new one if --split was
# specified, otherwise the separate boot image is not needed) # specified, otherwise the separate boot image is not needed)
if not args.ondev_no_rootfs: if not args.ondev_no_rootfs:
img_boot = f"{args.devicesdhbfvhubsud}-boot.img" img_boot = f"{config.device}-boot.img"
logging.info(f"(native) rm {img_boot}") logging.info(f"(native) rm {img_boot}")
pmb.chroot.root(["rm", f"/home/pmos/rootfs/{img_boot}"]) pmb.chroot.root(["rm", f"/home/pmos/rootfs/{img_boot}"])
# Disable root login # Disable root login
setup_login(args, chroot_installer) setup_login(args, config, chroot_installer)
# Generate installer image # Generate installer image
size_reserve = round(os.path.getsize(img_path_dest) / 1024 / 1024) + 200 size_reserve = round(os.path.getsize(img_path_dest) / 1024 / 1024) + 200
@ -1185,8 +1174,8 @@ def create_device_rootfs(args: PmbArgs, step, steps):
logging.info(f'*** ({step}/{steps}) CREATE DEVICE ROOTFS ("{device}")' logging.info(f'*** ({step}/{steps}) CREATE DEVICE ROOTFS ("{device}")'
' ***') ' ***')
suffix = Chroot(ChrootType.ROOTFS, device) chroot = Chroot(ChrootType.ROOTFS, device)
pmb.chroot.init(suffix) pmb.chroot.init(chroot)
# Create user before installing packages, so post-install scripts of # Create user before installing packages, so post-install scripts of
# pmaports can figure out the username (legacy reasons: pmaports#820) # pmaports can figure out the username (legacy reasons: pmaports#820)
set_user(context.config) set_user(context.config)
@ -1215,7 +1204,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
install_packages += context.config.extra_packages.split(",") install_packages += context.config.extra_packages.split(",")
if args.add: if args.add:
install_packages += args.add.split(",") install_packages += args.add.split(",")
locale_is_set = (config.locale != pmb.config.defaults["locale"]) locale_is_set = (config.locale != Config().locale)
if locale_is_set: if locale_is_set:
install_packages += ["lang", "musl-locales"] install_packages += ["lang", "musl-locales"]
@ -1231,58 +1220,63 @@ def create_device_rootfs(args: PmbArgs, step, steps):
# Pick the most suitable unlocker depending on the packages # Pick the most suitable unlocker depending on the packages
# selected for installation # selected for installation
unlocker = pmb.parse.depends.package_provider( unlocker = pmb.parse.depends.package_provider(
"postmarketos-fde-unlocker", install_packages, suffix) "postmarketos-fde-unlocker", install_packages, chroot)
if unlocker["pkgname"] not in install_packages: if unlocker["pkgname"] not in install_packages:
install_packages += [unlocker["pkgname"]] install_packages += [unlocker["pkgname"]]
else: else:
install_packages += ["postmarketos-base-nofde"] install_packages += ["postmarketos-base-nofde"]
pmb.helpers.repo.update(args.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)
# Explicitly call build on the install packages, to re-build them or any # Explicitly call build on the install packages, to re-build them or any
# dependency, in case the version increased # dependency, in case the version increased
if args.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, args.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
# installed a hook without pmbootstrap - see #69 for more info) # installed a hook without pmbootstrap - see #69 for more info)
pmb.chroot.apk.install(install_packages, suffix) pmb.chroot.apk.install(install_packages, chroot)
flavor = pmb.chroot.other.kernel_flavor_installed(suffix) flavor = pmb.chroot.other.kernel_flavor_installed(chroot)
pmb.chroot.initfs.build(args, flavor, suffix) pmb.chroot.initfs.build(flavor, chroot)
# Set the user password # Set the user password
setup_login(args, suffix) setup_login(args, config, chroot)
# Set the keymap if the device requires it # Set the keymap if the device requires it
setup_keymap(config) setup_keymap(config)
# Set timezone # Set timezone
setup_timezone(config) setup_timezone(chroot, config.timezone)
# Set locale # Set locale
if locale_is_set: if locale_is_set:
# 10locale-pmos.sh gets sourced before 20locale.sh from # 10locale-pmos.sh gets sourced before 20locale.sh from
# alpine-baselayout by /etc/profile. Since they don't override the # alpine-baselayout by /etc/profile. Since they don't override the
# locale if it exists, it warranties we have preference # locale if it exists, it warranties we have preference
line = f"export LANG=${{LANG:-{shlex.quote(args.locale)}}}" line = f"export LANG=${{LANG:-{shlex.quote(config.locale)}}}"
pmb.chroot.root(["sh", "-c", f"echo {shlex.quote(line)}" pmb.chroot.root(["sh", "-c", f"echo {shlex.quote(line)}"
" > /etc/profile.d/10locale-pmos.sh"], suffix) " > /etc/profile.d/10locale-pmos.sh"], chroot)
# Set the hostname as the device name # Set the hostname as the device name
setup_hostname(args) setup_hostname(device, config.hostname)
setup_appstream(args) setup_appstream(context.offline, chroot)
disable_sshd(args) if args.no_sshd:
disable_firewall(args) disable_sshd(chroot)
if args.no_firewall:
disable_firewall(chroot)
def install(args: PmbArgs): def install(args: PmbArgs):
device = get_context().config.device
chroot = Chroot(ChrootType.ROOTFS, device)
deviceinfo = pmb.parse.deviceinfo()
# Sanity checks # Sanity checks
sanity_check_boot_size() sanity_check_boot_size()
if not args.android_recovery_zip and args.disk: if not args.android_recovery_zip and args.disk:
@ -1319,18 +1313,18 @@ 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, 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
install_on_device_installer(args, step, steps) install_on_device_installer(args, step, steps)
else: else:
install_system_image(args, 0, Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud), step, steps, install_system_image(args, 0, chroot, step, steps,
split=args.split, disk=args.disk) split=args.split, disk=args.disk)
print_flash_info(args) 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) 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 = args.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 args.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 = args.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

@ -14,15 +14,15 @@ from pmb.core import Chroot
# FIXME (#2324): this function drops disk to a string because it's easier # FIXME (#2324): this function drops disk to a string because it's easier
# to manipulate, this is probably bad. # to manipulate, this is probably bad.
def partitions_mount(args: PmbArgs, layout, disk: Optional[Path]): def partitions_mount(device: str, layout, disk: Optional[Path]):
""" """
Mount blockdevices of partitions inside native chroot Mount blockdevices of partitions inside native chroot
:param layout: partition layout from get_partition_layout() :param layout: partition layout from get_partition_layout()
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None :param disk: path to disk block device (e.g. /dev/mmcblk0) or None
""" """
if not disk: if not disk:
img_path = Path("/home/pmos/rootfs") / f"{args.devicesdhbfvhubsud}.img" img_path = Path("/home/pmos/rootfs") / f"{device}.img"
disk = pmb.install.losetup.device_by_back_file(args, img_path) disk = pmb.install.losetup.device_by_back_file(img_path)
logging.info(f"Mounting partitions of {disk} inside the chroot") logging.info(f"Mounting partitions of {disk} inside the chroot")
@ -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 = args.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 = args.deviceinfo["boot_part_start"] or "2048" boot_part_start = pmb.parse.deviceinfo()["boot_part_start"] or "2048"
partition_type = args.deviceinfo["partition_type"] or "msdos" partition_type = pmb.parse.deviceinfo()["partition_type"] or "msdos"
commands = [ commands = [
["mktable", partition_type], ["mktable", partition_type],
@ -128,11 +128,11 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
:param size_reserve: empty partition between root and boot in MiB (pma#463) :param size_reserve: empty partition between root and boot in MiB (pma#463)
""" """
pmb.chroot.apk.install(["cgpt"], build=False) pmb.chroot.apk.install(["cgpt"], Chroot.native(), build=False)
cgpt = { cgpt = {
'kpart_start': args.deviceinfo["cgpt_kpart_start"], 'kpart_start': pmb.parse.deviceinfo()["cgpt_kpart_start"],
'kpart_size': args.deviceinfo["cgpt_kpart_size"], 'kpart_size': pmb.parse.deviceinfo()["cgpt_kpart_size"],
} }
# Convert to MB and print info # Convert to MB and print info
@ -178,7 +178,7 @@ def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
] ]
dev_size = pmb.chroot.root( dev_size = pmb.chroot.root(
args, ["blockdev", "--getsz", "/dev/install"], output_return=True) ["blockdev", "--getsz", "/dev/install"], output_return=True)
# 33: Sec GPT table (32) + Sec GPT header (1) # 33: Sec GPT table (32) + Sec GPT header (1)
root_size = str(int(dev_size) - int(s_root_start) - 33) root_size = str(int(dev_size) - int(s_root_start) - 33)

View file

@ -1,38 +1,40 @@
# Copyright 2023 Attila Szollosi # Copyright 2023 Attila Szollosi
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path from pathlib import Path
from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
import pmb.chroot import pmb.chroot
import pmb.chroot.apk
import pmb.config.pmaports import pmb.config.pmaports
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.flasher import pmb.flasher
import pmb.helpers.frontend import pmb.helpers.frontend
def create_zip(args: PmbArgs, suffix): def create_zip(args: PmbArgs, chroot: Chroot, device: str):
""" """
Create android recovery compatible installer zip. Create android recovery compatible installer zip.
""" """
zip_root = Path("/var/lib/postmarketos-android-recovery-installer/") zip_root = Path("/var/lib/postmarketos-android-recovery-installer/")
rootfs = "/mnt/rootfs_" + args.devicesdhbfvhubsud rootfs = "/mnt/rootfs_" + device
flavor = pmb.helpers.frontend._parse_flavor(args) flavor = pmb.helpers.frontend._parse_flavor(device)
method = args.deviceinfo["flash_method"] deviceinfo = pmb.parse.deviceinfo()
method = deviceinfo["flash_method"]
vars = pmb.flasher.variables(args, flavor, method) vars = pmb.flasher.variables(args, flavor, method)
# Install recovery installer package in buildroot # Install recovery installer package in buildroot
pmb.chroot.apk.install(args, pmb.chroot.apk.install(["postmarketos-android-recovery-installer"],
["postmarketos-android-recovery-installer"], chroot)
suffix)
logging.info(f"({suffix}) create recovery zip") logging.info(f"({chroot}) create recovery zip")
for key in vars: for key in vars:
pmb.flasher.check_partition_blacklist(args, key, vars[key]) pmb.flasher.check_partition_blacklist(args, deviceinfo, key, vars[key])
# Create config file for the recovery installer # Create config file for the recovery installer
options = { options = {
"DEVICE": args.devicesdhbfvhubsud, "DEVICE": device,
"FLASH_KERNEL": args.recovery_flash_kernel, "FLASH_KERNEL": args.recovery_flash_kernel,
"ISOREC": method == "heimdall-isorec", "ISOREC": method == "heimdall-isorec",
"KERNEL_PARTLABEL": vars["$PARTITION_KERNEL"], "KERNEL_PARTLABEL": vars["$PARTITION_KERNEL"],
@ -52,7 +54,7 @@ def create_zip(args: PmbArgs, suffix):
options["FLAVOR"] = f"-{flavor}" if flavor is not None else "-" options["FLAVOR"] = f"-{flavor}" if flavor is not None else "-"
# Write to a temporary file # Write to a temporary file
config_temp = suffix / "tmp/install_options" config_temp = chroot / "tmp/install_options"
with config_temp.open("w") as handle: with config_temp.open("w") as handle:
for key, value in options.items(): for key, value in options.items():
if isinstance(value, bool): if isinstance(value, bool):
@ -69,6 +71,6 @@ def create_zip(args: PmbArgs, suffix):
["tar", "-prf", "rootfs.tar", "-C", "/", "./etc/apk/keys"], ["tar", "-prf", "rootfs.tar", "-C", "/", "./etc/apk/keys"],
# Compress with -1 for speed improvement # Compress with -1 for speed improvement
["gzip", "-f1", "rootfs.tar"], ["gzip", "-f1", "rootfs.tar"],
["build-recovery-zip", args.devicesdhbfvhubsud]] ["build-recovery-zip", device]]
for command in commands: for command in commands:
pmb.chroot.root(command, suffix, working_dir=zip_root) pmb.chroot.root(command, chroot, working_dir=zip_root)

View file

@ -1,45 +1,46 @@
# Copyright 2023 Mark Hargreaves, Luca Weiss # Copyright 2023 Mark Hargreaves, Luca Weiss
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
import socket import socket
import time import time
import pmb.chroot.run import pmb.chroot
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
from pmb.core import Chroot from pmb.core import Chroot
def start_nbd_server(args: PmbArgs, ip="172.16.42.2", port=9999): def start_nbd_server(device: str, replace: bool, ip="172.16.42.2", port=9999):
""" """
Start nbd server in chroot_native with pmOS rootfs. Start nbd server in chroot_native with pmOS rootfs.
:param ip: IP address to serve nbd server for :param ip: IP address to serve nbd server for
:param port: port of nbd server :param port: port of nbd server
""" """
pmb.chroot.apk.install(['nbd']) pmb.chroot.apk.install(['nbd'], Chroot.native())
chroot = Chroot.native() chroot = Chroot.native()
rootfs_path = Path("/mnt/pmbootstrap/netboot") / f"{args.devicesdhbfvhubsud}.img" rootfs_path = Path("/mnt/pmbootstrap/netboot") / f"{device}.img"
if not (chroot / rootfs_path).exists() or args.replace: if not (chroot / rootfs_path).exists() or replace:
rootfs_path2 = Path("/home/pmos/rootfs") / f"{args.devicesdhbfvhubsud}.img" rootfs_path2 = Path("/home/pmos/rootfs") / f"{device}.img"
if not (chroot / rootfs_path2).exists(): if not (chroot / rootfs_path2).exists():
raise RuntimeError("The rootfs has not been generated yet, please " raise RuntimeError("The rootfs has not been generated yet, please "
"run 'pmbootstrap install' first.") "run 'pmbootstrap install' first.")
if args.replace and not \ if replace and not \
pmb.helpers.cli.confirm(f"Are you sure you want to " pmb.helpers.cli.confirm(f"Are you sure you want to "
f"replace the rootfs for " f"replace the rootfs for "
f"{args.devicesdhbfvhubsud}?"): f"{device}?"):
return return
pmb.chroot.run(args, ["cp", rootfs_path2, rootfs_path]) pmb.chroot.root(["cp", rootfs_path2, rootfs_path])
logging.info(f"NOTE: Copied device image to {get_context().config.work}" logging.info(f"NOTE: Copied device image to {get_context().config.work}"
f"/images_netboot/. The image will persist \"pmbootstrap " f"/images_netboot/. The image will persist \"pmbootstrap "
f"zap\" for your convenience. Use \"pmbootstrap netboot " f"zap\" for your convenience. Use \"pmbootstrap netboot "
f"serve --help\" for more options.") f"serve --help\" for more options.")
logging.info(f"Running nbd server for {args.devicesdhbfvhubsud} on {ip} port {port}.") logging.info(f"Running nbd server for {device} on {ip} port {port}.")
while True: while True:
logging.info("Waiting for postmarketOS device to appear...") logging.info("Waiting for postmarketOS device to appear...")
@ -61,8 +62,7 @@ def start_nbd_server(args: PmbArgs, ip="172.16.42.2", port=9999):
break break
logging.info("Found postmarketOS device, serving image...") logging.info("Found postmarketOS device, serving image...")
pmb.chroot.run( pmb.chroot.root(["nbd-server", f"{ip}@{port}", rootfs_path, "-d"],
args, ["nbd-server", f"{ip}@{port}", rootfs_path, "-d"],
check=False, disable_timeout=True) check=False, disable_timeout=True)
logging.info("nbd-server quit. Connection lost?") logging.info("nbd-server quit. Connection lost?")
# On a reboot nbd-server will quit, but the IP address sticks around # On a reboot nbd-server will quit, but the IP address sticks around

View file

@ -1,6 +1,7 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# mypy: disable-error-code="attr-defined" # mypy: disable-error-code="attr-defined"
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path

View file

@ -594,11 +594,8 @@ def arguments_ci(subparser):
def package_completer(prefix, action, parser=None, parsed_args=None): def package_completer(prefix, action, parser=None, parsed_args=None):
args = parsed_args
pmb.config.merge_with_args(args)
pmb.helpers.args.replace_placeholders(args)
packages = set( packages = set(
package for package in pmb.helpers.pmaports.get_list(args) package for package in pmb.helpers.pmaports.get_list()
if package.startswith(prefix)) if package.startswith(prefix))
return packages return packages

View file

@ -1,11 +1,12 @@
# 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 os import os
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from pmb.types import PmbArgs from pmb.types import PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.chroot.run import pmb.chroot
import pmb.chroot.other import pmb.chroot.other
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core import Chroot from pmb.core import Chroot
@ -72,7 +73,7 @@ def get_qcdt_type(path):
return None return None
def bootimg(args: PmbArgs, path: Path): def bootimg(path: Path):
if not path.exists(): if not path.exists():
raise RuntimeError(f"Could not find file '{path}'") raise RuntimeError(f"Could not find file '{path}'")
@ -93,7 +94,7 @@ def bootimg(args: PmbArgs, path: Path):
working_dir=temp_path, working_dir=temp_path,
output_return=True).rstrip() output_return=True).rstrip()
if "android bootimg" not in file_output.lower(): if "android bootimg" not in file_output.lower():
if "force" in args and args.force: if get_context().force:
logging.warning("WARNING: boot.img file seems to be invalid, but" logging.warning("WARNING: boot.img file seems to be invalid, but"
" proceeding anyway (-f specified)") " proceeding anyway (-f specified)")
else: else:
@ -166,6 +167,6 @@ def bootimg(args: PmbArgs, path: Path):
output["cmdline"] = f.read().replace('\n', '') output["cmdline"] = f.read().replace('\n', '')
# Cleanup # Cleanup
pmb.chroot.run.user(["rm", "-r", temp_path]) pmb.chroot.user(["rm", "-r", temp_path])
return output return output

View file

@ -230,7 +230,7 @@ def check_config(config_path, config_arch, pkgver, components_list=[],
return all(results) return all(results)
def check(args: PmbArgs, pkgname, components_list=[], details=False, must_exist=True): def check(pkgname, components_list=[], details=False, must_exist=True):
""" """
Check for necessary kernel config options in a package. Check for necessary kernel config options in a package.

View file

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import subprocess import subprocess
from typing import Sequence from typing import Sequence
from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
@ -24,12 +25,12 @@ import pmb.parse.cpuinfo
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def system_image(args: PmbArgs): def system_image(device: str):
""" """
Returns path to rootfs for specified device. In case that it doesn't Returns path to rootfs for specified device. In case that it doesn't
exist, raise and exception explaining how to generate it. exist, raise and exception explaining how to generate it.
""" """
path = Chroot.native() / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}.img" path = Chroot.native() / "home/pmos/rootfs" / f"{device}.img"
if not path.exists(): if not path.exists():
logging.debug(f"Could not find rootfs: {path}") logging.debug(f"Could not find rootfs: {path}")
raise RuntimeError("The rootfs has not been generated yet, please " raise RuntimeError("The rootfs has not been generated yet, please "
@ -37,14 +38,14 @@ def system_image(args: PmbArgs):
return path return path
def create_second_storage(args: PmbArgs): def create_second_storage(args: PmbArgs, device: str):
""" """
Generate a second storage image if it does not exist. Generate a second storage image if it does not exist.
:returns: path to the image or None :returns: path to the image or None
""" """
path = Chroot.native() / "home/pmos/rootfs" / f"{args.devicesdhbfvhubsud}-2nd.img" path = Chroot.native() / "home/pmos/rootfs" / f"{device}-2nd.img"
pmb.helpers.run.root(["touch", path]) pmb.helpers.run.root(["touch", path])
pmb.helpers.run.root(["chmod", "a+w", path]) pmb.helpers.run.root(["chmod", "a+w", path])
resize_image(args, args.second_storage, path) resize_image(args, args.second_storage, path)
@ -87,11 +88,11 @@ def create_gdk_loader_cache(args: PmbArgs) -> Path:
return chroot_native / custom_cache_path return chroot_native / custom_cache_path
def command_qemu(args: PmbArgs, arch, img_path, img_path_2nd=None): 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 = args.deviceinfo["kernel_cmdline"] cmdline = pmb.parse.deviceinfo()["kernel_cmdline"]
if args.cmdline: if args.cmdline:
cmdline = args.cmdline cmdline = args.cmdline
@ -102,9 +103,9 @@ def command_qemu(args: PmbArgs, arch, img_path, img_path_2nd=None):
port_ssh = str(args.port) port_ssh = str(args.port)
chroot = Chroot(ChrootType.ROOTFS, args.devicesdhbfvhubsud) chroot = Chroot(ChrootType.ROOTFS, device)
chroot_native = Chroot.native() chroot_native = Chroot.native()
flavor = pmb.chroot.other.kernel_flavor_installed(args, chroot) flavor = pmb.chroot.other.kernel_flavor_installed(chroot)
flavor_suffix = f"-{flavor}" flavor_suffix = f"-{flavor}"
# Backwards compatibility with old mkinitfs (pma#660) # Backwards compatibility with old mkinitfs (pma#660)
pmaports_cfg = pmb.config.pmaports.read_config() pmaports_cfg = pmb.config.pmaports.read_config()
@ -220,7 +221,7 @@ def command_qemu(args: PmbArgs, 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 == args.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"]
@ -315,7 +316,7 @@ def install_depends(args: PmbArgs, arch):
] ]
# QEMU packaging isn't split up as much in 3.12 # QEMU packaging isn't split up as much in 3.12
channel_cfg = pmb.config.pmaports.read_config_channel(args) channel_cfg = pmb.config.pmaports.read_config_channel()
if channel_cfg["branch_aports"] == "3.12-stable": if channel_cfg["branch_aports"] == "3.12-stable":
depends.remove("qemu-hw-display-virtio-gpu") depends.remove("qemu-hw-display-virtio-gpu")
depends.remove("qemu-hw-display-virtio-gpu-pci") depends.remove("qemu-hw-display-virtio-gpu-pci")
@ -332,22 +333,23 @@ def run(args: PmbArgs):
""" """
Run a postmarketOS image in qemu Run a postmarketOS image in qemu
""" """
if not args.devicesdhbfvhubsud.startswith("qemu-"): device = get_context().config.device
if not device.startswith("qemu-"):
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(args.deviceinfo["arch"]) arch = pmb.parse.arch.alpine_to_qemu(pmb.parse.deviceinfo()["arch"])
img_path = system_image(args) img_path = system_image(device)
img_path_2nd = None img_path_2nd = None
if args.second_storage: if args.second_storage:
img_path_2nd = create_second_storage(args) img_path_2nd = create_second_storage(args, device)
if not args.host_qemu: if not args.host_qemu:
install_depends(args, arch) install_depends(args, arch)
logging.info("Running postmarketOS in QEMU VM (" + arch + ")") logging.info("Running postmarketOS in QEMU VM (" + arch + ")")
qemu, env = command_qemu(args, arch, img_path, img_path_2nd) qemu, env = command_qemu(args, device, arch, img_path, img_path_2nd)
# Workaround: QEMU runs as local user and needs write permissions in the # Workaround: QEMU runs as local user and needs write permissions in the
# rootfs, which is owned by root # rootfs, which is owned by root

View file

@ -46,7 +46,6 @@ class PmbArgs(Namespace):
autoinstall: str autoinstall: str
boot_size: str boot_size: str
build_default_device_arch: str build_default_device_arch: str
build_pkgs_on_install: bool
buildroot: str buildroot: str
built: str built: str
ccache_size: str ccache_size: str
@ -72,14 +71,13 @@ class PmbArgs(Namespace):
filesystem: str filesystem: str
flash_method: str flash_method: str
folder: str folder: str
force: str force: bool
fork_alpine: str fork_alpine: str
# This is a filthy lie # This is a filthy lie
from_argparse: "PmbArgs" from_argparse: "PmbArgs"
full_disk_encryption: str full_disk_encryption: str
hook: str hook: str
host: str host: str
hostname: str
host_qemu: str host_qemu: str
image_size: str image_size: str
install_base: str install_base: str
@ -101,7 +99,7 @@ class PmbArgs(Namespace):
name: str name: str
no_depends: str no_depends: str
no_fde: str no_fde: str
no_firewall: str no_firewall: bool
no_image: str no_image: str
non_existing: str non_existing: str
no_reboot: str no_reboot: str
@ -132,7 +130,7 @@ class PmbArgs(Namespace):
recovery_flash_kernel: str recovery_flash_kernel: str
recovery_install_partition: str recovery_install_partition: str
ref: str ref: str
replace: str replace: bool
repository: str repository: str
reset: str reset: str
resume: str resume: str
@ -142,7 +140,7 @@ class PmbArgs(Namespace):
second_storage: str second_storage: str
selected_providers: Dict[str, str] selected_providers: Dict[str, str]
sparse: str sparse: str
split: str split: bool
src: str src: str
ssh_keys: str ssh_keys: str
strict: str strict: str
@ -150,7 +148,6 @@ class PmbArgs(Namespace):
suffix: str suffix: str
systemd: str systemd: str
timeout: float timeout: float
user: str
value: str value: str
verbose: str verbose: str
verify: str verify: str